package systems

import (
	"openquell/assets"
	"openquell/components"
	. "openquell/components"
	"openquell/config"
	"openquell/util"

	"log"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/mlange-42/arche/ecs"
	"github.com/mlange-42/arche/generic"
)

type GridSystem struct {
	World                   *ecs.World
	Selector                *generic.Filter3[Renderable, Position, Solid]
	UseCache                bool
	Cache                   *ebiten.Image
	Count                   int // register tile count, invalidates cache
	Background              *ebiten.Image
	Animate                 components.Animation
	BackgroundAnimated      bool
	Width, Height, Tilesize int
}

func NewGridSystem(world *ecs.World, width, height,
	tilesize int, background string) System {

	cache := ebiten.NewImage(width, height)

	if !util.Exists(assets.Assets, background) {
		log.Fatalf("no background %s in assets", background)
	}

	system := &GridSystem{
		Selector:   generic.NewFilter3[Renderable, Position, Solid](),
		UseCache:   false,
		Cache:      cache,
		Width:      width,
		Height:     height,
		Tilesize:   tilesize,
		Background: assets.Assets[background],
		World:      world,
	}

	if util.Exists(assets.Animations, background+"-animated") {
		system.BackgroundAnimated = true

		system.Animate = components.Animation{
			Active:  true,
			Loop:    true,
			Sprites: assets.Animations[background+"-animated"].Sprites,
		}

		system.Animate.Timer.Start(config.ANIMATION_LOOPWAIT)
	}

	return system
}

func (system *GridSystem) Update() error {
	if system.BackgroundAnimated {
		if system.Animate.Timer.IsReady() {
			switch {
			// animation shows from earlier tick, animate
			case system.Animate.Index > -1 && system.Animate.Index < len(system.Animate.Sprites)-1:
				system.Animate.Index += 1
				system.Animate.Timer.Start(system.Animate.GetDuration())
			default:
				// last sprite reached
				if system.Animate.Loop {
					system.Animate.Index = 0
				}
			}
		} else {
			system.Animate.Timer.Update()
		}

	}

	return nil
}

func (system *GridSystem) Draw(screen *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	query := system.Selector.Query(system.World)

	if !system.UseCache || query.Count() != system.Count || system.BackgroundAnimated {
		// map not cached or cacheable, write it to the cache
		if system.BackgroundAnimated {
			system.Cache.DrawImage(system.Animate.GetSprite(), op)
		} else {
			system.Cache.DrawImage(system.Background, op)
		}

		system.Count = query.Count()
		counter := 0
		for query.Next() {
			sprite, pos, _ := query.Get()

			counter++
			op.GeoM.Reset()
			op.GeoM.Translate(float64(pos.X), float64(pos.Y))
			system.Cache.DrawImage(sprite.Image, op)

		}

		op.GeoM.Reset()
		screen.DrawImage(system.Cache, op)

		system.UseCache = true

	} else {
		// use the cached map
		op.GeoM.Reset()
		screen.DrawImage(system.Cache, op)
		query.Close()
	}
}
