package types

import (
	"sync"
	"time"

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

type Player struct {
	sync.RWMutex
	PosActual             rl.Vector3
	PosTile               Tile
	TargetPath            []Tile
	ActionQueue           []*pb.Action
	Speed                 float32
	ID                    int32
	IsMoving              bool
	AnimationFrame        int32
	LastAnimUpdate        time.Time
	LastUpdateTime        time.Time
	InterpolationProgress float32
	FloatingMessage       *FloatingMessage
	QuitDone              chan struct{}
	UserData              interface{}
	Model                 rl.Model
	Texture               rl.Texture2D
	Username              string
}

func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
	p.Lock()
	defer p.Unlock()

	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 {
		wasMoving := p.IsMoving
		p.IsMoving = true

		if !wasMoving {
			p.AnimationFrame = 0
		}

		oldFrame := p.AnimationFrame
		p.AnimationFrame += int32(deltaTime * 60)
		rl.TraceLog(rl.LogInfo, "Walk frame update: %d -> %d (delta: %f)",
			oldFrame, p.AnimationFrame, deltaTime)
	} else {
		wasMoving := p.IsMoving
		p.IsMoving = false

		if wasMoving {
			p.AnimationFrame = 0
		}

		oldFrame := p.AnimationFrame
		p.AnimationFrame += int32(deltaTime * 60)
		rl.TraceLog(rl.LogInfo, "Idle frame update: %d -> %d (delta: %f)",
			oldFrame, p.AnimationFrame, deltaTime)
	}

	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(),
	}
}

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) {
	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
}
