package types

import (
	"sync"
	"time"

	pb "gitea.boner.be/bdnugget/goonserver/actions"
	rl "github.com/gen2brain/raylib-go/raylib"
)

// AnimationController manages animation state and updates
type AnimationController struct {
	animations       AnimationSet
	currentAnimation string // "idle" or "walk"
	frame            int32
	lastUpdate       time.Time
	frameCount       int32
}

// NewAnimationController creates a new animation controller
func NewAnimationController(animations AnimationSet) *AnimationController {
	return &AnimationController{
		animations:       animations,
		currentAnimation: "idle",
		frame:            0,
		lastUpdate:       time.Now(),
	}
}

// Update updates the animation state based on movement
func (ac *AnimationController) Update(deltaTime float32, isMoving bool) {
	// Set the current animation based on movement
	newAnimation := "idle"
	if isMoving {
		newAnimation = "walk"
	}

	// Reset frame counter when animation changes
	if ac.currentAnimation != newAnimation {
		ac.frame = 0
		ac.currentAnimation = newAnimation
	}

	// Update the frame
	ac.frame += int32(deltaTime * 60)

	// Determine which animation set to use
	var frames []rl.ModelAnimation
	if ac.currentAnimation == "walk" && len(ac.animations.Walk) > 0 {
		frames = ac.animations.Walk
	} else if len(ac.animations.Idle) > 0 {
		frames = ac.animations.Idle
	}

	// If we have frames, ensure we loop properly
	if len(frames) > 0 && frames[0].FrameCount > 0 {
		ac.frame = ac.frame % frames[0].FrameCount
	}
}

// GetAnimFrame returns the current animation frame
func (ac *AnimationController) GetAnimFrame() int32 {
	return ac.frame
}

// GetCurrentAnimation returns the current animation type
func (ac *AnimationController) GetCurrentAnimation() string {
	return ac.currentAnimation
}

type Player struct {
	sync.RWMutex          // Keep this for network operations
	Model                 rl.Model
	Texture               rl.Texture2D
	PosActual             rl.Vector3
	PosTile               Tile
	TargetPath            []Tile
	Speed                 float32
	ActionQueue           []*pb.Action
	ID                    int32
	QuitDone              chan struct{}
	CurrentTick           int64
	UserData              interface{}
	FloatingMessage       *FloatingMessage
	IsMoving              bool
	AnimationFrame        int32
	LastAnimUpdate        time.Time
	LastUpdateTime        time.Time
	InterpolationProgress float32
	PlaceholderColor      rl.Color
	AnimController        *AnimationController
}

func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
	// No need for lock here as this is called from a single thread (game loop)
	targetPos := rl.Vector3{
		X: float32(target.X * TileSize),
		Y: mapGrid[target.X][target.Y].Height * TileHeight,
		Z: float32(target.Y * TileSize),
	}

	direction := rl.Vector3Subtract(targetPos, p.PosActual)
	distance := rl.Vector3Length(direction)

	if distance > 1.0 {
		p.IsMoving = true
	} else {
		p.IsMoving = false
	}

	// Update animation if controller exists
	if p.AnimController != nil {
		p.AnimController.Update(deltaTime, p.IsMoving)
		p.AnimationFrame = p.AnimController.GetAnimFrame()
	} else {
		// Legacy animation update for backward compatibility
		if p.IsMoving {
			if !p.IsMoving {
				p.AnimationFrame = 0
			}
			p.AnimationFrame += int32(deltaTime * 60)
		} else {
			wasMoving := p.IsMoving
			if wasMoving {
				p.AnimationFrame = 0
			}
			p.AnimationFrame += int32(deltaTime * 60)
		}
	}

	if distance > 0 {
		direction = rl.Vector3Scale(direction, p.Speed*deltaTime/distance)
	}

	if distance > 1.0 {
		p.PosActual = rl.Vector3Add(p.PosActual, direction)
	} else {
		p.PosActual = targetPos
		p.PosTile = target
		if len(p.TargetPath) > 1 {
			p.TargetPath = p.TargetPath[1:]
		}
	}
}

func NewPlayer(state *pb.PlayerState) *Player {
	return &Player{
		PosActual: rl.Vector3{
			X: float32(state.X * TileSize),
			Y: float32(state.Y * TileHeight),
			Z: float32(state.Y * TileSize),
		},
		PosTile:               Tile{X: int(state.X), Y: int(state.Y)},
		Speed:                 50.0,
		ID:                    state.PlayerId,
		IsMoving:              false,
		AnimationFrame:        0,
		LastAnimUpdate:        time.Now(),
		LastUpdateTime:        time.Now(),
		InterpolationProgress: 1.0,
	}
}

// InitializeAnimations sets up the animation controller for the player
func (p *Player) InitializeAnimations(animations AnimationSet) {
	p.AnimController = NewAnimationController(animations)
}

func (p *Player) UpdatePosition(state *pb.PlayerState, tickRate time.Duration) {
	p.Lock()
	defer p.Unlock()

	targetTile := Tile{X: int(state.X), Y: int(state.Y)}
	if p.PosTile != targetTile {
		p.PosTile = targetTile
		p.LastUpdateTime = time.Now()
		p.InterpolationProgress = 0
		p.TargetPath = []Tile{targetTile}
	}
}

func (p *Player) ForceResync(state *pb.PlayerState) {
	// Keep this lock since it's called from the network goroutine
	p.Lock()
	defer p.Unlock()

	p.PosTile = Tile{X: int(state.X), Y: int(state.Y)}
	p.PosActual = rl.Vector3{
		X: float32(state.X * TileSize),
		Y: float32(state.Y * TileHeight),
		Z: float32(state.Y * TileSize),
	}
	p.TargetPath = nil
	p.ActionQueue = nil
	p.InterpolationProgress = 1.0
}
