package main

import (
	"flag"
	"image"
	"log"
	"math"
	"os"

	"github.com/hajimehoshi/ebiten"
	"golang.org/x/image/colornames"

	"code.ur.gs/lupine/ordoor/internal/assetstore"
	"code.ur.gs/lupine/ordoor/internal/ui"
)

var (
	gamePath = flag.String("game-path", "./orig", "Path to a WH40K: Chaos Gate installation")
	groupIdx = flag.Int("group", 1, "Group index to start at")
	recIdx   = flag.Int("record", 0, "Record index to start at")

	winX = flag.Int("win-x", 1280, "Pre-scaled window X dimension")
	winY = flag.Int("win-y", 1024, "Pre-scaled window Y dimension")
)

type env struct {
	assets *assetstore.AssetStore
	ani    *assetstore.Animation
	step   int

	state     state
	lastState state
}

type state struct {
	groupIdx int
	recIdx   int

	zoom   float64
	origin image.Point
}

func main() {
	flag.Parse()

	if *gamePath == "" {
		flag.Usage()
		os.Exit(1)
	}

	assets, err := assetstore.New(*gamePath)
	if err != nil {
		log.Fatal("Failed to set up asset store: %v", err)
	}

	state := state{
		zoom:     6.0,
		origin:   image.Point{-300, -200}, // Show them somewhat centered
		groupIdx: *groupIdx,
		recIdx:   *recIdx,
	}

	env := &env{
		assets:    assets,
		state:     state,
		lastState: state,
	}

	win, err := ui.NewWindow(env, "View Animations", *winX, *winY)
	if err != nil {
		log.Fatal(err)
	}

	win.OnKeyUp(ebiten.KeyMinus, env.changeGroup(-1))
	win.OnKeyUp(ebiten.KeyEqual, env.changeGroup(+1))

	win.OnKeyUp(ebiten.KeyComma, env.changeRec(-1))
	win.OnKeyUp(ebiten.KeyPeriod, env.changeRec(+1))

	win.OnKeyUp(ebiten.KeyLeft, env.changeOrigin(-16, +0))
	win.OnKeyUp(ebiten.KeyRight, env.changeOrigin(+16, +0))
	win.OnKeyUp(ebiten.KeyUp, env.changeOrigin(+0, -16))
	win.OnKeyUp(ebiten.KeyDown, env.changeOrigin(+0, +16))
	win.OnMouseWheel(env.changeZoom)

	// The main thread now belongs to ebiten
	if err := win.Run(); err != nil {
		log.Fatal(err)
	}
}

func (e *env) Update(screenX, screenY int) error {
	if e.step == 0 || e.lastState != e.state {

		ani, err := e.assets.Animation(e.state.groupIdx, e.state.recIdx)
		if err != nil {
			return err
		}
		e.ani = ani

		log.Printf(
			"new state: group=%d rec=%d zoom=%.2f, origin=%+v",
			e.state.groupIdx,
			e.state.recIdx,
			e.state.zoom,
			e.state.origin,
		)
	}

	// This should be the final action
	e.step += 1
	e.lastState = e.state

	return nil
}

func (e *env) Draw(screen *ebiten.Image) error {
	cam := ebiten.GeoM{}
	cam.Scale(e.state.zoom, e.state.zoom)                                 // apply current zoom factor
	cam.Translate(float64(-e.state.origin.X), float64(-e.state.origin.Y)) // Move to origin
	screen.Fill(colornames.White)

	if len(e.ani.Frames) > 0 {
		sprite := e.ani.Frames[e.step/4%len(e.ani.Frames)]

		return screen.DrawImage(sprite.Image, &ebiten.DrawImageOptions{GeoM: cam})
	}

	return nil
}

func (e *env) changeGroup(by int) func() {
	return func() {
		e.state.groupIdx += by

		if e.state.groupIdx < 0 {
			e.state.groupIdx = 0
		}

		// Reset the record if the group changes
		e.state.recIdx = 0

		// TODO: upper bounds checking
	}
}

func (e *env) changeRec(by int) func() {
	return func() {
		e.state.recIdx += by

		if e.state.recIdx < 0 {
			e.state.recIdx = 0
		}

		// TODO: upper bounds checking
	}
}

func (e *env) changeOrigin(byX, byY int) func() {
	return func() {
		e.state.origin.X += byX
		e.state.origin.Y += byY
	}
}

func (e *env) changeZoom(_, y float64) {
	// Zoom in and out with the mouse wheel
	e.state.zoom *= math.Pow(1.2, y)
}
