package parser

import "eq2emu/internal/common"

// PacketDef defines a complete packet structure with versioned field ordering
type PacketDef struct {
	Fields map[string]FieldDesc // Field definitions by name
	Orders map[uint32][]string  // Field order by version number
}

// Creates packet definition with estimated capacity
func NewPacketDef(estimatedFields int) *PacketDef {
	return &PacketDef{
		Fields: make(map[string]FieldDesc, estimatedFields),
		Orders: make(map[uint32][]string, 4),
	}
}

func (def *PacketDef) Parse(data []byte, version uint32, flags uint64) (map[string]any, error) {
	ctx := NewContext(data, version, flags)
	return def.parseStruct(ctx)
}

func (def *PacketDef) parseStruct(ctx *ParseContext) (map[string]any, error) {
	result := make(map[string]any)
	order := def.getVersionOrder(ctx.version)

	for _, fieldName := range order {
		field := def.Fields[fieldName]

		if !ctx.checkCondition(field.Condition) {
			continue
		}

		fieldType := field.Type
		if field.Type2 != 0 && ctx.checkCondition(field.Type2Cond) {
			fieldType = field.Type2
		}

		value := def.parseField(ctx, field, fieldType, fieldName)
		result[fieldName] = value
		ctx.setVarWithArrayIndex(fieldName, value)
	}

	return result, nil
}

func (def *PacketDef) parseField(ctx *ParseContext, field FieldDesc, fieldType common.EQ2DataType, fieldName string) any {
	switch fieldType {
	case common.TypeInt8:
		if field.Oversized > 0 {
			return ctx.readOversizedUint8(field.Oversized)
		}
		return ctx.readUint8()
	case common.TypeInt16:
		if field.Oversized > 0 {
			return ctx.readOversizedUint16(field.Oversized)
		}
		return ctx.readUint16()
	case common.TypeInt32:
		if field.Oversized > 0 {
			return ctx.readOversizedUint32(field.Oversized)
		}
		return ctx.readUint32()
	case common.TypeInt64:
		return ctx.readUint64()
	case common.TypeSInt8:
		return ctx.readSint8()
	case common.TypeSInt16:
		if field.Oversized > 0 {
			return ctx.readOversizedSint16(field.Oversized)
		}
		return ctx.readSint16()
	case common.TypeSInt32:
		return ctx.readSint32()
	case common.TypeSInt64:
		return ctx.readSint64()
	case common.TypeString8:
		return ctx.readEQ2String8()
	case common.TypeString16:
		return ctx.readEQ2String16()
	case common.TypeString32:
		return ctx.readEQ2String32()
	case common.TypeChar:
		return ctx.readBytes(field.Length)
	case common.TypeFloat:
		return ctx.readFloat32()
	case common.TypeDouble:
		return ctx.readFloat64()
	case common.TypeColor:
		return ctx.readEQ2Color()
	case common.TypeEquipment:
		return ctx.readEQ2Equipment()
	case common.TypeArray:
		size := ctx.getArraySize(field.Condition)
		if field.MaxArraySize > 0 && size > field.MaxArraySize {
			size = field.MaxArraySize
		}
		result := make([]map[string]any, size)
		for i := 0; i < size; i++ {
			ctx.pushArrayIndex(i)
			item, _ := field.SubDef.parseStruct(ctx)
			result[i] = item
			ctx.popArrayIndex()
		}
		return result
	}
	return nil
}

func (def *PacketDef) getVersionOrder(version uint32) []string {
	var bestVersion uint32
	for v := range def.Orders {
		if v <= version && v > bestVersion {
			bestVersion = v
		}
	}
	return def.Orders[bestVersion]
}

// FieldDesc describes a single packet field
type FieldDesc struct {
	Type         common.EQ2DataType // Primary data type
	Condition    string             // Conditional parsing expression
	Length       int                // Array length or size for fixed-size fields
	SubDef       *PacketDef         // Nested packet definition for arrays
	Type2        common.EQ2DataType // Alternative data type for conditional parsing
	Type2Cond    string             // Condition for using Type2
	Oversized    int                // Threshold for oversized field handling
	DefaultValue int8               // Default value for initialization
	MaxArraySize int                // Maximum allowed array size
	Optional     bool               // Whether this field is optional
	AddToStruct  bool               // Whether to include in packet structure
	AddType      common.EQ2DataType // Type to use when adding to packet
}
