package widget

import (
	"math"
	"strings"
	"sync"

	"eq2emu/internal/spawn"
)

// Widget represents an interactive spawn object like doors and lifts
// Extends the base Spawn with widget-specific functionality
type Widget struct {
	*spawn.Spawn // Embedded spawn for basic functionality

	// Widget identification
	widgetID   int32 // Unique widget identifier
	widgetType int8  // Type of widget (door, lift, etc.)

	// Widget positioning
	widgetX         float32 // Widget-specific X coordinate
	widgetY         float32 // Widget-specific Y coordinate
	widgetZ         float32 // Widget-specific Z coordinate
	includeLocation bool    // Whether to include location in updates
	includeHeading  bool    // Whether to include heading in updates

	// Door/movement states
	isOpen        bool    // Current open/closed state
	openHeading   float32 // Heading when open
	closedHeading float32 // Heading when closed
	openX         float32 // X position when open
	openY         float32 // Y position when open
	openZ         float32 // Z position when open
	closeX        float32 // X position when closed
	closeY        float32 // Y position when closed (from close_y in C++)
	closeZ        float32 // Z position when closed

	// Linked widgets
	actionSpawn   *Widget // Spawn triggered by this widget
	actionSpawnID int32   // ID of action spawn
	linkedSpawn   *Widget // Linked widget (opens/closes together)
	linkedSpawnID int32   // ID of linked spawn

	// Sounds and timing
	openSound    string // Sound played when opening
	closeSound   string // Sound played when closing
	openDuration int16  // How long widget stays open (seconds)

	// House integration
	houseID int32 // Associated house ID

	// Multi-floor lift support
	multiFloorLift bool // Whether this is a multi-floor lift

	// Thread safety
	mutex sync.RWMutex
}

// NewWidget creates a new widget instance
func NewWidget() *Widget {
	w := &Widget{
		Spawn:           spawn.NewSpawn(),
		widgetID:        0,
		widgetType:      WidgetTypeGeneric,
		widgetX:         0,
		widgetY:         0,
		widgetZ:         0,
		includeLocation: true,
		includeHeading:  true,
		isOpen:          false,
		openHeading:     DefaultOpenHeading,
		closedHeading:   DefaultClosedHeading,
		openX:           0,
		openY:           0,
		openZ:           0,
		closeX:          0,
		closeY:          0,
		closeZ:          0,
		openDuration:    DefaultOpenDuration,
		multiFloorLift:  false,
	}

	// Set spawn-specific defaults for widgets
	w.SetSpawnType(2) // Widget spawn type
	w.SetActivityStatus(DefaultActivityClosed)
	w.SetPosState(1)

	return w
}

// IsWidget returns true to identify this as a widget
func (w *Widget) IsWidget() bool {
	return true
}

// Widget ID methods

// GetWidgetID returns the widget ID
func (w *Widget) GetWidgetID() int32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.widgetID
}

// SetWidgetID sets the widget ID
func (w *Widget) SetWidgetID(id int32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.widgetID = id
}

// Widget position methods

// GetWidgetX returns the widget X coordinate
func (w *Widget) GetWidgetX() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.widgetX
}

// SetWidgetX sets the widget X coordinate
func (w *Widget) SetWidgetX(x float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.widgetX = x
}

// GetWidgetY returns the widget Y coordinate
func (w *Widget) GetWidgetY() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.widgetY
}

// SetWidgetY sets the widget Y coordinate
func (w *Widget) SetWidgetY(y float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.widgetY = y
}

// GetWidgetZ returns the widget Z coordinate
func (w *Widget) GetWidgetZ() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.widgetZ
}

// SetWidgetZ sets the widget Z coordinate
func (w *Widget) SetWidgetZ(z float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.widgetZ = z
}

// Location/heading inclusion methods

// GetIncludeLocation returns whether to include location in updates
func (w *Widget) GetIncludeLocation() bool {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.includeLocation
}

// SetIncludeLocation sets whether to include location in updates
func (w *Widget) SetIncludeLocation(include bool) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.includeLocation = include
}

// GetIncludeHeading returns whether to include heading in updates
func (w *Widget) GetIncludeHeading() bool {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.includeHeading
}

// SetIncludeHeading sets whether to include heading in updates
func (w *Widget) SetIncludeHeading(include bool) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.includeHeading = include
}

// Widget type methods

// GetWidgetType returns the widget type
func (w *Widget) GetWidgetType() int8 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.widgetType
}

// SetWidgetType sets the widget type
func (w *Widget) SetWidgetType(widgetType int8) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.widgetType = widgetType
}

// SetWidgetIcon sets the widget icon (appearance)
func (w *Widget) SetWidgetIcon(icon int8) {
	w.SetIcon(icon)
}

// Open/close state methods

// IsOpen returns whether the widget is open
func (w *Widget) IsOpen() bool {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.isOpen
}

// setOpenState sets the open state (internal use)
func (w *Widget) setOpenState(open bool) {
	w.isOpen = open
}

// Heading methods

// GetOpenHeading returns the heading when open
func (w *Widget) GetOpenHeading() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openHeading
}

// SetOpenHeading sets the heading when open
func (w *Widget) SetOpenHeading(heading float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openHeading = heading
}

// GetClosedHeading returns the heading when closed
func (w *Widget) GetClosedHeading() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.closedHeading
}

// SetClosedHeading sets the heading when closed
func (w *Widget) SetClosedHeading(heading float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.closedHeading = heading
}

// Position methods for open/close states

// GetOpenX returns the X position when open
func (w *Widget) GetOpenX() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openX
}

// SetOpenX sets the X position when open
func (w *Widget) SetOpenX(x float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openX = x
}

// GetOpenY returns the Y position when open
func (w *Widget) GetOpenY() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openY
}

// SetOpenY sets the Y position when open
func (w *Widget) SetOpenY(y float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openY = y
}

// GetOpenZ returns the Z position when open
func (w *Widget) GetOpenZ() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openZ
}

// SetOpenZ sets the Z position when open
func (w *Widget) SetOpenZ(z float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openZ = z
}

// GetCloseX returns the X position when closed
func (w *Widget) GetCloseX() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.closeX
}

// SetCloseX sets the X position when closed
func (w *Widget) SetCloseX(x float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.closeX = x
}

// GetCloseY returns the Y position when closed
func (w *Widget) GetCloseY() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.closeY
}

// SetCloseY sets the Y position when closed
func (w *Widget) SetCloseY(y float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.closeY = y
}

// GetCloseZ returns the Z position when closed
func (w *Widget) GetCloseZ() float32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.closeZ
}

// SetCloseZ sets the Z position when closed
func (w *Widget) SetCloseZ(z float32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.closeZ = z
}

// Linked spawn methods

// GetActionSpawnID returns the action spawn ID
func (w *Widget) GetActionSpawnID() int32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.actionSpawnID
}

// SetActionSpawnID sets the action spawn ID
func (w *Widget) SetActionSpawnID(id int32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.actionSpawnID = id
}

// GetLinkedSpawnID returns the linked spawn ID
func (w *Widget) GetLinkedSpawnID() int32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.linkedSpawnID
}

// SetLinkedSpawnID sets the linked spawn ID
func (w *Widget) SetLinkedSpawnID(id int32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.linkedSpawnID = id
}

// Sound methods

// GetOpenSound returns the sound played when opening
func (w *Widget) GetOpenSound() string {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openSound
}

// SetOpenSound sets the sound played when opening
func (w *Widget) SetOpenSound(sound string) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openSound = sound
}

// GetCloseSound returns the sound played when closing
func (w *Widget) GetCloseSound() string {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.closeSound
}

// SetCloseSound sets the sound played when closing
func (w *Widget) SetCloseSound(sound string) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.closeSound = sound
}

// Duration methods

// GetOpenDuration returns how long the widget stays open
func (w *Widget) GetOpenDuration() int16 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.openDuration
}

// SetOpenDuration sets how long the widget stays open
func (w *Widget) SetOpenDuration(duration int16) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.openDuration = duration
}

// House methods

// GetHouseID returns the associated house ID
func (w *Widget) GetHouseID() int32 {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.houseID
}

// SetHouseID sets the associated house ID
func (w *Widget) SetHouseID(id int32) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.houseID = id
}

// Multi-floor lift methods

// GetMultiFloorLift returns whether this is a multi-floor lift
func (w *Widget) GetMultiFloorLift() bool {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.multiFloorLift
}

// SetMultiFloorLift sets whether this is a multi-floor lift
func (w *Widget) SetMultiFloorLift(multiFloor bool) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.multiFloorLift = multiFloor
}

// Copy creates a copy of this widget
func (w *Widget) Copy() *Widget {
	w.mutex.RLock()
	defer w.mutex.RUnlock()

	newWidget := NewWidget()

	// Copy spawn data
	w.CopySpawnData(newWidget.Spawn)

	// Handle open state for appearance
	if w.openY > 0 {
		newWidget.SetPosState(0)
	}

	// Copy widget-specific data
	newWidget.widgetID = w.widgetID
	newWidget.widgetType = w.widgetType
	newWidget.widgetX = w.widgetX
	newWidget.widgetY = w.widgetY
	newWidget.widgetZ = w.widgetZ
	newWidget.includeLocation = w.includeLocation
	newWidget.includeHeading = w.includeHeading
	newWidget.openHeading = w.openHeading
	newWidget.closedHeading = w.closedHeading
	newWidget.openX = w.openX
	newWidget.openY = w.openY
	newWidget.openZ = w.openZ
	newWidget.closeX = w.closeX
	newWidget.closeY = w.closeY
	newWidget.closeZ = w.closeZ
	newWidget.openSound = w.openSound
	newWidget.closeSound = w.closeSound
	newWidget.openDuration = w.openDuration
	newWidget.actionSpawnID = w.actionSpawnID
	newWidget.linkedSpawnID = w.linkedSpawnID
	newWidget.houseID = w.houseID
	newWidget.multiFloorLift = w.multiFloorLift

	return newWidget
}

// calculateDistance calculates 3D distance between two points
func calculateDistance(x1, y1, z1, x2, y2, z2 float32) float32 {
	dx := x2 - x1
	dy := y2 - y1
	dz := z2 - z1
	return float32(math.Sqrt(float64(dx*dx + dy*dy + dz*dz)))
}

// Helper method to check if a string command matches (case-insensitive)
func isCommand(command, expected string) bool {
	return strings.EqualFold(strings.TrimSpace(command), expected)
}
