// Copyright (c) quickfixengine.org  All rights reserved.
//
// This file may be distributed under the terms of the quickfixengine.org
// license as defined by quickfixengine.org and appearing in the file
// LICENSE included in the packaging of this file.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.
//
// See http://www.quickfixengine.org/LICENSE for licensing information.
//
// Contact ask@quickfixengine.org if any conditions of this licensing
// are not clear to you.

package quickfix

import (
	"strings"
	"testing"

	"github.com/stretchr/testify/suite"
)

func BenchmarkParser_ReadMessage(b *testing.B) {
	stream := "8=FIXT.1.19=11135=D34=449=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=2348=FIXT.1.19=9535=D34=549=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=198"

	for i := 0; i < b.N; i++ {
		reader := strings.NewReader(stream)
		parser := newParser(reader)
		_, _ = parser.ReadMessage()
	}
}

type ParserSuite struct {
	suite.Suite
	*parser
}

func TestParserSuite(t *testing.T) {
	suite.Run(t, new(ParserSuite))
}

func (s *ParserSuite) SetupTest() {
	s.parser = new(parser)
}

func (s *ParserSuite) TestInvalidNilLength() {
	stream := "8=\x019=\x01"
	s.reader = strings.NewReader(stream)
	_, err := s.ReadMessage()
	s.NotNil(err)
}

func (s *ParserSuite) TestOverflowLength() {
	stream := "8=\x019=9300000000000000000\x01"
	s.reader = strings.NewReader(stream)
	_, err := s.ReadMessage()
	s.NotNil(err)
}

func (s *ParserSuite) TestJumpLength() {
	stream := "8=FIXT.1.19=11135=D34=449=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=2348=FIXT.1.19=9535=D34=549=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=198"

	s.reader = strings.NewReader(stream)
	index, err := s.parser.jumpLength()
	s.Nil(err)

	expectedIndex := 111 + 17 - 1
	s.Equal(expectedIndex, index)
}

func (s *ParserSuite) TestBadLength() {
	stream := "8=FIXT.1.19=11135=D34=449=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=2348=FIXT.1.19=9535=D34=549=TW52=20140511-23:10:3456=ISLD11=ID21=340=154=155=INTC60=20140511-23:10:3410=198"

	s.reader = strings.NewReader(stream)
	bytes, _ := s.parser.ReadMessage()

	s.Equal(stream, bytes.String())
}

func (s *ParserSuite) TestFindStart() {
	var testCases = []struct {
		stream        string
		expectError   bool
		expectedStart int
	}{
		{stream: "", expectError: true},
		{stream: "nostarthere", expectError: true},
		{stream: "hello8=FIX.4.0", expectedStart: 5},
	}
	for _, tc := range testCases {
		s.SetupTest()

		s.reader = strings.NewReader(tc.stream)

		start, err := s.parser.findStart()
		if tc.expectError {
			s.NotNil(err)
			continue
		}

		s.Nil(err)
		s.Equal(tc.expectedStart, start)
	}
}

func (s *ParserSuite) TestReadEOF() {
	var testCases = []struct {
		stream string
	}{
		{""},
		{"hello8=FIX.4.0"},
	}

	for _, tc := range testCases {
		s.SetupTest()

		s.reader = strings.NewReader(tc.stream)
		bytes, err := s.parser.ReadMessage()
		s.Nil(bytes)

		s.NotNil(err)
		s.Equal("EOF", err.Error())
	}
}

func (s *ParserSuite) TestReadMessage() {
	stream := "hello8=FIX.4.09=5blah10=1038=FIX.4.09=4foo10=103"
	s.reader = strings.NewReader(stream)

	var testCases = []struct {
		expectedBytes     string
		expectedBufferLen int
		expectedBufferCap int
	}{
		{expectedBytes: "8=FIX.4.09=5blah10=103", expectedBufferCap: defaultBufSize - 31, expectedBufferLen: len(stream) - 31},
		{expectedBytes: "8=FIX.4.09=4foo10=103", expectedBufferCap: defaultBufSize - 31 - 25, expectedBufferLen: 0},
	}

	for _, tc := range testCases {
		msg, err := s.parser.ReadMessage()
		s.Nil(err)

		s.Equal(tc.expectedBytes, msg.String())
		s.Equal(tc.expectedBufferCap, cap(s.parser.buffer))
		s.Equal(tc.expectedBufferLen, len(s.parser.buffer))
	}
}

func (s *ParserSuite) TestReadMessageGrowBuffer() {
	stream := "hello8=FIX.4.09=5blah10=1038=FIX.4.09=4foo10=103"

	var testCases = []struct {
		initialBufCap        int
		expectedBytes        string
		expectedBufferLen    int
		expectedBufferCap    int
		expectedBigBufferLen int
	}{
		{initialBufCap: 0, expectedBufferCap: defaultBufSize - 31, expectedBufferLen: len(stream) - 31, expectedBigBufferLen: defaultBufSize},
		{initialBufCap: 4, expectedBufferCap: 6, expectedBufferLen: 6, expectedBigBufferLen: 32},
		{initialBufCap: 8, expectedBufferCap: 6, expectedBufferLen: 6, expectedBigBufferLen: 32},
		{initialBufCap: 14, expectedBufferCap: 10, expectedBufferLen: 10, expectedBigBufferLen: 36},
		{initialBufCap: 16, expectedBufferCap: 18, expectedBufferLen: 18, expectedBigBufferLen: 44},
		{initialBufCap: 23, expectedBufferCap: 10, expectedBufferLen: 10, expectedBigBufferLen: 36},
		{initialBufCap: 30, expectedBufferCap: 24, expectedBufferLen: 24, expectedBigBufferLen: 50},
		{initialBufCap: 31, expectedBufferCap: 0, expectedBufferLen: 0, expectedBigBufferLen: 31},
		{initialBufCap: 40, expectedBufferCap: 9, expectedBufferLen: 9, expectedBigBufferLen: 40},
		{initialBufCap: 60, expectedBufferCap: 29, expectedBufferLen: 25, expectedBigBufferLen: 60},
		{initialBufCap: 80, expectedBufferCap: 49, expectedBufferLen: 25, expectedBigBufferLen: 80},
	}

	for _, tc := range testCases {
		s.SetupTest()

		s.reader = strings.NewReader(stream)
		s.parser.bigBuffer = make([]byte, tc.initialBufCap)
		s.parser.buffer = s.parser.bigBuffer[0:0]
		msg, err := s.parser.ReadMessage()

		s.Nil(err)
		s.Equal("8=FIX.4.09=5blah10=103", msg.String())
		s.Equal(tc.expectedBigBufferLen, len(s.parser.bigBuffer))
		s.Equal(tc.expectedBufferCap, cap(s.parser.buffer))
		s.Equal(tc.expectedBufferLen, len(s.parser.buffer))
	}
}
