package handlers

import (
	"fmt"
	"image/color"

	"tetris/assets"
	"tetris/engine/audio"
	"tetris/engine/core"
	"tetris/engine/render"
	"tetris/game"
	"tetris/game/draw"
	"tetris/game/state"

	rl "github.com/gen2brain/raylib-go/raylib"
)

type GamePlay struct {
	shape              game.Shape
	shape_pos          core.Vec2i8
	score              game.Score
	dropTimer          core.IntervalTimer
	moveTimer          core.IntervalTimer
	lineClearTimer     core.IntervalTimer
	grid               game.Grid
	nextShape          game.Shape
	shapeQueue         *game.ShapeQueue
	r                  draw.Renderer
	lineClearAnimation game.LineClearAnimation
}

func NewGamePlay() *GamePlay {
	return &GamePlay{
		dropTimer:      core.NewIntervalTimer(0.3),
		moveTimer:      core.NewIntervalTimer(0.1),
		lineClearTimer: core.NewIntervalTimer(0.05),
		r: draw.Renderer{
			Theme: &draw.Theme{
				FrameBG:        color.RGBA{R: 30, G: 30, B: 46, A: 255},
				FrameBorder:    color.RGBA{R: 242, G: 205, B: 205, A: 255},
				TextHeader:     color.RGBA{R: 242, G: 205, B: 205, A: 255},
				Text:           color.RGBA{R: 205, G: 214, B: 244, A: 255},
				GridBackground: color.RGBA{R: 17, G: 17, B: 27, A: 255},
			},
		},
	}
}

func (gp *GamePlay) Enter() {
	gp.grid = game.Grid{}
	gp.score = 0
	gp.dropTimer.SetInterval(0.3)
	gp.shapeQueue = game.NewShapeQueue()
	gp.nextShape = gp.shapeQueue.Next()
	gp.SpawnShape()
	gp.lineClearAnimation.Reset()

	audio.PlayLooped(assets.SFX_MUSIC)
}

func (GamePlay) Exit() {
	audio.Stop(assets.SFX_MUSIC)
}

func (gp *GamePlay) SpawnShape() {
	gp.shape = gp.nextShape
	gp.nextShape = gp.shapeQueue.Next()
	gp.shape_pos = core.Vec2i8{X: 4, Y: 0}
}

func (gp *GamePlay) LockShape() {
	audio.Play(assets.SFX_SHAPE_LOCKED)

	for _, block := range gp.shape.Coordinates() {
		block = gp.shape_pos.Add(block)
		// Check bounds
		if block.X < 0 || block.X > int8(gp.grid.Width()) || block.Y < 0 || block.Y > int8(gp.grid.Height()) {
			continue
		}
		gp.grid.Set(byte(block.X), byte(block.Y), gp.shape.GetBlock())
	}
}

func (gp *GamePlay) Update(fsm state.Transitioner, delta float32) {
	if rl.IsKeyPressed(rl.KeyDown) {
		gp.dropTimer.SetInterval(0.05)
	} else if rl.IsKeyReleased(rl.KeyDown) {
		gp.dropTimer.SetInterval(0.3)
	}

	if !gp.lineClearAnimation.Completed() {
		if gp.lineClearTimer.UpdateReset(delta) {
			gp.lineClearAnimation.Update(&gp.grid)
		}
		return
	}

	if rl.IsKeyPressed(rl.KeyUp) {
		rotated := gp.shape.RotateCW()
		if !game.CheckShapeCollision(gp.shape_pos, &rotated, &gp.grid) {
			gp.shape = rotated
		}
	}

	if gp.moveTimer.UpdateReset(delta) && (rl.IsKeyDown(rl.KeyLeft) || rl.IsKeyDown(rl.KeyRight)) {
		new_pos := gp.shape_pos
		if rl.IsKeyDown(rl.KeyLeft) {
			new_pos.X -= 1
		} else {
			new_pos.X += 1
		}
		if !game.CheckShapeCollision(new_pos, &gp.shape, &gp.grid) {
			gp.shape_pos.X = new_pos.X
		}
	}

	if gp.dropTimer.UpdateReset(delta) {
		new_pos := gp.shape_pos
		new_pos.Y += 1

		// Update position if it does not collide
		if game.CheckShapeCollision(new_pos, &gp.shape, &gp.grid) {
			gp.LockShape()
			gp.SpawnShape()
			lines := gp.grid.FullLines()
			if len(lines) > 0 {
				gp.lineClearAnimation.SetLines(lines)
				gp.score.Lines(byte(len(lines)))
				audio.Play(assets.SFX_ROW_CLEARED)
			} else {
				if game.CheckShapeCollision(gp.shape_pos, &gp.shape, &gp.grid) {
					fsm.Switch("gameover")
				}
			}
		} else {
			gp.shape_pos = new_pos
		}
	}
}

func (gp GamePlay) Render() {
	render.Begin(gp.r.Theme.GridBackground)
	gp.r.DrawGrid(rl.NewVector2(25, 25), gp.grid)
	draw.DrawShape(rl.NewVector2(25, 25), gp.shape_pos, gp.shape)
	gp.r.DrawFrame(rl.RectangleInt32{X: 400, Y: 25, Width: 250, Height: 100})
	gp.r.DrawHeaderText(410, 30, "Score")
	gp.r.DrawText(410, 65, fmt.Sprintf("%.7d", gp.score))
	gp.r.DrawFrame(rl.RectangleInt32{X: 400, Y: 150, Width: 250, Height: 200})
	gp.r.DrawHeaderText(410, 155, "Next")
	draw.DrawShape(rl.NewVector2(450, 150), core.NewVec2[int8](1, 3), gp.nextShape)
	render.End()
}
