package internal

import (
	"fmt"

	"quantex.com/qfixdpl/quickfix/datadictionary"
)

func isDecimalType(quickfixType string) bool {
	switch quickfixType {
	case "FIXDecimal", "FIXUDecimal":
		return true
	default:
		return false
	}
}

func checkIfDecimalImportRequiredForFields(fTypes []*datadictionary.FieldType) (ok bool, err error) {
	var t string
	for _, fType := range fTypes {
		t, err = quickfixType(fType)
		if err != nil {
			return
		}

		if isDecimalType(t) {
			return true, nil
		}
	}

	return
}

func checkIfTimeImportRequiredForFields(fTypes []*datadictionary.FieldType) (ok bool, err error) {
	var t string
	for _, fType := range fTypes {
		t, err = quickfixType(fType)
		if err != nil {
			return
		}

		var vt string
		if vt, err = quickfixValueType(t); err != nil {
			return
		}

		if vt == "time.Time" {
			return true, nil
		}
	}

	return
}

func checkFieldDecimalRequired(f *datadictionary.FieldDef) (required bool, err error) {
	var globalType *datadictionary.FieldType
	if globalType, err = getGlobalFieldType(f); err != nil {
		return
	}

	var t string
	if t, err = quickfixType(globalType); err != nil {
		return
	}

	if isDecimalType(t) {
		required = true
		return
	}

	for _, groupField := range f.Fields {
		if required, err = checkFieldDecimalRequired(groupField); required || err != nil {
			return
		}
	}

	return
}

func checkFieldTimeRequired(f *datadictionary.FieldDef) (required bool, err error) {
	var globalType *datadictionary.FieldType
	if globalType, err = getGlobalFieldType(f); err != nil {
		return
	}

	var t string
	if t, err = quickfixType(globalType); err != nil {
		return
	}

	var vt string
	if vt, err = quickfixValueType(t); err != nil {
		return
	}

	if vt == "time.Time" {
		required = true
		return
	}

	for _, groupField := range f.Fields {
		if required, err = checkFieldTimeRequired(groupField); required || err != nil {
			return
		}
	}

	return
}

func collectStandardImports(m *datadictionary.MessageDef) (imports []string, err error) {
	var timeRequired bool
	for _, f := range m.Fields {
		if !timeRequired {
			if timeRequired, err = checkFieldTimeRequired(f); err != nil {
				return
			}
		}

		if timeRequired {
			break
		}
	}

	if timeRequired {
		imports = append(imports, "time")
	}

	return
}

func collectExtraImports(m *datadictionary.MessageDef) (imports []string, err error) {
	var decimalRequired bool
	importPath := "github.com/shopspring/decimal"
	if *useUDecimal {
		importPath = "github.com/quagmt/udecimal"
	}
	for _, f := range m.Fields {
		if !decimalRequired {
			if decimalRequired, err = checkFieldDecimalRequired(f); err != nil {
				return
			}
		}

		if decimalRequired {
			break
		}
	}

	if decimalRequired {
		imports = append(imports, importPath)
	}

	return
}

func checkIfEnumImportRequired(m *datadictionary.MessageDef) (required bool, err error) {
	for _, f := range m.Fields {
		required, err = checkFieldEnumRequired(f)
		if err != nil || required {
			return
		}
	}

	return
}

func checkFieldEnumRequired(f *datadictionary.FieldDef) (required bool, err error) {
	var globalType *datadictionary.FieldType
	if globalType, err = getGlobalFieldType(f); err != nil {
		return
	}

	if globalType.Enums != nil && 0 < len(globalType.Enums) {
		var t string
		if t, err = quickfixType(globalType); err != nil {
			return
		}
		if t != "FIXBoolean" {
			required = true
			return
		}
	}

	for _, groupField := range f.Fields {
		if required, err = checkFieldEnumRequired(groupField); required || err != nil {
			return
		}
	}

	return
}

func quickfixValueType(quickfixType string) (goType string, err error) {
	switch quickfixType {
	case "FIXString":
		goType = "string"
	case "FIXBoolean":
		goType = "bool"
	case "FIXInt":
		goType = "int"
	case "FIXUTCTimestamp":
		goType = "time.Time"
	case "FIXFloat":
		goType = "float64"
	case "FIXDecimal":
		goType = "decimal.Decimal"
	case "FIXUDecimal":
		goType = "udecimal.Decimal"
	default:
		err = fmt.Errorf("Unknown QuickFIX Type: %v", quickfixType)
	}

	return
}

func quickfixType(field *datadictionary.FieldType) (quickfixType string, err error) {
	switch field.Type {
	case "MULTIPLESTRINGVALUE", "MULTIPLEVALUESTRING":
		fallthrough
	case "MULTIPLECHARVALUE":
		fallthrough
	case "CHAR":
		fallthrough
	case "CURRENCY":
		fallthrough
	case "DATA":
		fallthrough
	case "MONTHYEAR":
		fallthrough
	case "LOCALMKTTIME", "LOCALMKTDATE":
		fallthrough
	case "TIME":
		fallthrough
	case "DATE":
		fallthrough
	case "EXCHANGE":
		fallthrough
	case "LANGUAGE":
		fallthrough
	case "XMLDATA":
		fallthrough
	case "COUNTRY":
		fallthrough
	case "UTCTIMEONLY":
		fallthrough
	case "UTCDATE":
		fallthrough
	case "UTCDATEONLY":
		fallthrough
	case "TZTIMEONLY":
		fallthrough
	case "TZTIMESTAMP":
		fallthrough
	case "XID", "XIDREF":
		fallthrough
	case "STRING":
		quickfixType = "FIXString"

	case "BOOLEAN":
		quickfixType = "FIXBoolean"

	case "LENGTH":
		fallthrough
	case "DAYOFMONTH":
		fallthrough
	case "NUMINGROUP":
		fallthrough
	case "SEQNUM":
		fallthrough
	case "TAGNUM":
		fallthrough
	case "INT":
		quickfixType = "FIXInt"

	case "UTCTIMESTAMP":
		quickfixType = "FIXUTCTimestamp"

	case "QTY":
		fallthrough
	case "QUANTITY":
		fallthrough
	case "AMT":
		fallthrough
	case "PRICE":
		fallthrough
	case "PRICEOFFSET":
		fallthrough
	case "PERCENTAGE":
		fallthrough
	case "FLOAT":
		if *useFloat {
			quickfixType = "FIXFloat"
		} else if *useUDecimal {
			quickfixType = "FIXUDecimal"
		} else {
			quickfixType = "FIXDecimal"
		}

	default:
		err = fmt.Errorf("Unknown type '%v' for tag '%v'\n", field.Type, field.Tag())
	}

	return
}

func requiredFields(m *datadictionary.MessageDef) (required []*datadictionary.FieldDef) {
	for _, part := range m.RequiredParts() {
		if part.Required() {
			switch pType := part.(type) {
			case *datadictionary.FieldDef:
				if !pType.IsGroup() {
					required = append(required, pType)
				}
			case *datadictionary.Component:
				for _, f := range pType.RequiredFields() {
					if !f.IsGroup() {
						required = append(required, f)
					}
				}
			}
		}
	}

	return
}

func beginString(spec *datadictionary.DataDictionary) string {
	if spec.FIXType == "FIXT" || spec.Major == 5 {
		return "FIXT.1.1"
	}

	return fmt.Sprintf("FIX.%v.%v", spec.Major, spec.Minor)
}

func routerBeginString(spec *datadictionary.DataDictionary) (routerBeginString string) {
	switch {
	case spec.FIXType == "FIXT":
		routerBeginString = "FIXT.1.1"
	case spec.Major != 5 && spec.ServicePack == 0:
		routerBeginString = fmt.Sprintf("FIX.%v.%v", spec.Major, spec.Minor)
		// ApplVerID enums.
	case spec.Major == 2:
		routerBeginString = "0"
	case spec.Major == 3:
		routerBeginString = "1"
	case spec.Major == 4 && spec.Minor == 0:
		routerBeginString = "2"
	case spec.Major == 4 && spec.Minor == 1:
		routerBeginString = "3"
	case spec.Major == 4 && spec.Minor == 2:
		routerBeginString = "4"
	case spec.Major == 4 && spec.Minor == 3:
		routerBeginString = "5"
	case spec.Major == 4 && spec.Minor == 4:
		routerBeginString = "6"
	case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 0:
		routerBeginString = "7"
	case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 1:
		routerBeginString = "8"
	case spec.Major == 5 && spec.Minor == 0 && spec.ServicePack == 2:
		routerBeginString = "9"
	}

	return
}
