// 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 (
	"bytes"
	"fmt"
	"strconv"
)

// TagValue is a low-level FIX field abstraction.
type TagValue struct {
	tag   Tag
	value []byte
	bytes []byte
}

func (tv *TagValue) init(tag Tag, value []byte) {
	tv.bytes = strconv.AppendInt(nil, int64(tag), 10)
	tv.bytes = append(tv.bytes, []byte("=")...)
	tv.bytes = append(tv.bytes, value...)
	tv.bytes = append(tv.bytes, []byte("")...)

	tv.tag = tag
	tv.value = value
}

func (tv *TagValue) parse(rawFieldBytes []byte) error {
	var sepIndex int

	// Most of the Fix tags are 4 or less characters long, so we can optimize
	// for that by checking the 5 first characters without looping over the
	// whole byte slice.
	if len(rawFieldBytes) >= 5 {
		if rawFieldBytes[1] == '=' {
			sepIndex = 1
			goto PARSE
		} else if rawFieldBytes[2] == '=' {
			sepIndex = 2
			goto PARSE
		} else if rawFieldBytes[3] == '=' {
			sepIndex = 3
			goto PARSE
		} else if rawFieldBytes[4] == '=' {
			sepIndex = 4
			goto PARSE
		}
	}

	sepIndex = bytes.IndexByte(rawFieldBytes, '=')
	switch sepIndex {
	case -1:
		return fmt.Errorf("tagValue.Parse: No '=' in '%s'", rawFieldBytes)
	case 0:
		return fmt.Errorf("tagValue.Parse: No tag in '%s'", rawFieldBytes)
	}

PARSE:
	parsedTag, err := atoi(rawFieldBytes[:sepIndex])
	if err != nil {
		return fmt.Errorf("tagValue.Parse: %s", err.Error())
	}

	tv.tag = Tag(parsedTag)
	n := len(rawFieldBytes)
	tv.value = rawFieldBytes[(sepIndex + 1):(n - 1):(n - 1)]
	tv.bytes = rawFieldBytes[:n:n]

	return nil
}

func (tv TagValue) String() string {
	return string(tv.bytes)
}

// Tag returns the FIX tag for this TagValue.
func (tv TagValue) Tag() Tag {
	return tv.tag
}

// Value returns the raw FIX value bytes for this TagValue.
func (tv TagValue) Value() []byte {
	return tv.value
}

func bytesTotal(bytes []byte) (total int) {
	for _, b := range bytes {
		total += int(b)
	}
	return
}

func (tv TagValue) total() int {
	return bytesTotal(tv.bytes)
}

func (tv TagValue) length() int {
	return len(tv.bytes)
}
