package types

import (
	"reflect"
	"strings"
)

// CalculateSize uses reflection to calculate the serialized size of a struct
func CalculateSize(v interface{}) uint32 {
	return calculateSizeValue(reflect.ValueOf(v))
}

func calculateSizeValue(v reflect.Value) uint32 {
	// Dereference pointers
	if v.Kind() == reflect.Ptr {
		if v.IsNil() {
			return 0
		}
		v = v.Elem()
	}

	switch v.Kind() {
	case reflect.Struct:
		return calculateStructSize(v)
	case reflect.Slice:
		return calculateSliceSize(v)
	case reflect.Array:
		return calculateArraySize(v)
	case reflect.String:
		// Default to 16-bit length prefix if not specified
		return 2 + uint32(v.Len())
	case reflect.Uint8, reflect.Int8:
		return 1
	case reflect.Uint16, reflect.Int16:
		return 2
	case reflect.Uint32, reflect.Int32, reflect.Float32:
		return 4
	case reflect.Uint64, reflect.Int64, reflect.Float64:
		return 8
	default:
		return 0
	}
}

func calculateStructSize(v reflect.Value) uint32 {
	var size uint32
	t := v.Type()

	// Handle known types
	switch t.Name() {
	case "Color", "EQ2Color":
		return 3 // RGB: 3 bytes
	case "EquipmentItem":
		return 8 // 2 bytes type + 3 bytes color + 3 bytes highlight
	}

	// Calculate size for each field
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		fieldType := t.Field(i)
		
		// Skip unexported fields
		if !field.CanInterface() {
			continue
		}

		// Parse struct tags for size hints
		tag := fieldType.Tag.Get("eq2")
		tagParts := parseTag(tag)

		// Handle string types with specific encoding
		if field.Kind() == reflect.String {
			strLen := uint32(field.Len())
			if strType, ok := tagParts["type"]; ok {
				switch strType {
				case "str8", "EQ2_8Bit_String":
					size += 1 + strLen
				case "str16", "EQ2_16Bit_String":
					size += 2 + strLen
				case "str32", "EQ2_32Bit_String":
					size += 4 + strLen
				default:
					size += 2 + strLen // Default to 16-bit
				}
			} else {
				size += 2 + strLen // Default to 16-bit
			}
		} else if field.Kind() == reflect.Array {
			// Fixed-size array
			elemSize := getElementSize(field.Type().Elem())
			size += uint32(field.Len()) * elemSize
		} else if field.Kind() == reflect.Slice {
			// Dynamic array - calculate size of all elements
			for j := 0; j < field.Len(); j++ {
				size += calculateSizeValue(field.Index(j))
			}
		} else {
			// Regular field
			size += calculateSizeValue(field)
		}
	}

	return size
}

func calculateSliceSize(v reflect.Value) uint32 {
	var size uint32
	for i := 0; i < v.Len(); i++ {
		size += calculateSizeValue(v.Index(i))
	}
	return size
}

func calculateArraySize(v reflect.Value) uint32 {
	if v.Len() == 0 {
		return 0
	}
	
	elemSize := getElementSize(v.Type().Elem())
	return uint32(v.Len()) * elemSize
}

func getElementSize(t reflect.Type) uint32 {
	switch t.Kind() {
	case reflect.Uint8, reflect.Int8:
		return 1
	case reflect.Uint16, reflect.Int16:
		return 2
	case reflect.Uint32, reflect.Int32, reflect.Float32:
		return 4
	case reflect.Uint64, reflect.Int64, reflect.Float64:
		return 8
	case reflect.Struct:
		// Handle known struct types
		switch t.Name() {
		case "Color", "EQ2Color":
			return 3
		case "EquipmentItem":
			return 8
		default:
			// For unknown structs, create a zero value and calculate
			return calculateSizeValue(reflect.Zero(t))
		}
	default:
		return 0
	}
}

func parseTag(tag string) map[string]string {
	result := make(map[string]string)
	if tag == "" {
		return result
	}

	parts := strings.Split(tag, ",")
	if len(parts) > 0 {
		result["name"] = parts[0]
	}

	for i := 1; i < len(parts); i++ {
		kv := strings.Split(parts[i], ":")
		if len(kv) == 2 {
			result[kv[0]] = kv[1]
		}
	}

	return result
}

// SizeCalculator interface for types that can calculate their own size
type SizeCalculator interface {
	Size() uint32
}

// CalculateSizeOptimized uses the Size() method if available, otherwise uses reflection
func CalculateSizeOptimized(v interface{}) uint32 {
	if sc, ok := v.(SizeCalculator); ok {
		return sc.Size()
	}
	return CalculateSize(v)
}