package mainwindow

import (
	"errors"
	"fmt"
	"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
	"lab.zaar.be/thefish/alchemyst-go/engine/fov"
	"lab.zaar.be/thefish/alchemyst-go/engine/fov/precomputed_shade"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
	"time"
)

var NotInViewError = errors.New("not in ViewPort")

type ViewPort struct {
	types.Rect
	cameraCoords types.Coords
	layer        *Layer
	Fov          fov.Fov
	TorchRadius  int
	animateTiles *time.Ticker
}

func NewViewPort(x, y, w, h int, layer *Layer) *ViewPort {

	computedFov := precomputed_shade.NewPrecomputedShade(15)
	computedFov.Init()
	vp := ViewPort{
		Rect:  types.Rect{x, y, w, h},
		layer: layer,
		Fov:   computedFov,
	}

	vp.TorchRadius = 12
	vp.animateTiles = time.NewTicker(time.Second / 12)

	return &vp
}

func (vp *ViewPort) Close() {
	vp.animateTiles.Stop()
	vp.animateTiles = nil //free pointer to ticker
}

func (vp *ViewPort) Move(state *gamestate.GameState, newCoords types.Coords) {

	x := newCoords.X - vp.Rect.W/2
	y := newCoords.Y - vp.Rect.H/2

	if x < 0 {
		x = 0
	}
	if y < 0 {
		y = 0
	}
	if x > state.Level.W - vp.W - 1 {
		x = state.Level.W - vp.W
	}
	if y > state.Level.H-vp.H - 1 {
		y = state.Level.H - vp.H
	}
	if x != vp.cameraCoords.X || y != vp.cameraCoords.Y {
		state.FovRecompute <- struct{}{}
	}
	vp.cameraCoords.X = x
	vp.cameraCoords.Y = y

}

func (vp *ViewPort) ToVPCoords(c types.Coords) (newCoords types.Coords, err error) {
	//coords on map to coords on vp
	x, y := c.X-vp.cameraCoords.X, c.Y-vp.cameraCoords.Y
	if x < 0 || y < 0 || x > vp.W || y > vp.H {
		return types.Coords{-1, -1}, NotInViewError
	}
	return types.Coords{x, y}, nil
}

var redraw = true
var fovRecompute = true

func (vp *ViewPort) Listen(state gamestate.GameState) {
	for {
		select {
		case <-state.FovRecompute:
			fovRecompute = true
		case <-state.Redraw:
			redraw = true
		case <-vp.animateTiles.C:
			redraw = true
		}
	}
}

func (vp *ViewPort) Render(state *gamestate.GameState) {

	playerCoords := state.Controller.GetComponent(state.Player, ecs.CoordsComponent).(types.Coords)

	vp.Move(state, playerCoords)

	if fovRecompute {
		vp.layer.ClearRect(vp.Rect)
		fovRecompute = false
		redraw = true
		vp.Fov.ComputeFov(state.Level, playerCoords, vp.TorchRadius)
	}

	//vp.layer.ClearArea(0, 7, 20, 1)
	//vp.layer.Print(0,7, fmt.Sprintf("pcds: %v", playerCoords))


	if redraw {
		//terrain
		for y := 0; y < vp.H; y++ {
			for x := 0; x < vp.W; x++ {
				mapCoords := types.Coords{vp.cameraCoords.X + x, vp.cameraCoords.Y + y}

				if state.Level.InBounds(mapCoords) {
					tile := state.Level.GetTile(mapCoords)
					if tile.Explored || tile.Visible {
						vp.layer.PutToBase(x+vp.X, y+vp.Y, tile.GetChar(), tile.GetRawColor(), tile.GetRawBgColor())
					}
				}
			}
		}
		//mobs
		pc, err := vp.ToVPCoords(playerCoords)
		_ = pc
		if err != nil {
			fmt.Println("error on getting player position")
		} else {
			vp.layer.WithColor("white").Put(pc.X+vp.X, pc.Y+vp.Y, "@")
			//mw.GetLayer("base").WithColor("white").Put(42, 10, "B")
			//mw.GetLayer("overlay").WithColor("white").Put(59, 10, "O")
		}

		//redraw = true
		redraw = false
	}

}