package systems

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

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

type CollectibleSystem struct {
	World    *ecs.World
	Selector *generic.Filter3[Position, Collectible, Renderable]
}

func NewCollectibleSystem(world *ecs.World) System {
	system := &CollectibleSystem{
		Selector: generic.NewFilter3[Position, Collectible, Renderable](),
		World:    world,
	}

	return system
}

func (system *CollectibleSystem) Update() error {
	observer := observers.GetGameObserver(system.World)

	posID := ecs.ComponentID[components.Position](system.World)
	veloID := ecs.ComponentID[components.Velocity](system.World)

	particlepositions := []*components.Position{}
	EntitiesToRemove := []ecs.Entity{}

	query := system.Selector.Query(system.World)
	numcollectibles := query.Count()

	if numcollectibles == 0 || observer.Lost {
		query.Close()
		return nil
	}

	for query.Next() {
		colposition, _, _ := query.Get()

		for _, player := range observer.GetPlayers() {
			if !system.World.Alive(player) {
				continue
			}

			playerposition := (*Position)(system.World.Get(player, posID))
			playervelocity := (*Velocity)(system.World.Get(player, veloID))

			ok, _ := colposition.Intersects(playerposition, playervelocity)
			if ok {
				//slog.Debug("bumped into collectible", "collectible", collectible)
				particlepositions = append(particlepositions, colposition)
				EntitiesToRemove = append(EntitiesToRemove, query.Entity())
			}
		}
	}

	for _, pos := range particlepositions {
		system.AddParticle(pos)
	}

	for _, entity := range EntitiesToRemove {
		system.World.RemoveEntity(entity)
		numcollectibles--
	}

	if numcollectibles == 0 {
		// winner, winner,  chicken dinner!
		timer := observers.GetGameObserver(system.World).StopTimer
		if !timer.Running {
			timer.Start(LEVEL_END_WAIT)
		}
	}

	return nil
}

func (system *CollectibleSystem) Draw(screen *ebiten.Image) {
	// write the movable tiles
	op := &ebiten.DrawImageOptions{}
	query := system.Selector.Query(system.World)

	for query.Next() {
		pos, _, sprite := query.Get()

		op.GeoM.Reset()
		op.GeoM.Translate(float64(pos.X), float64(pos.Y))

		screen.DrawImage(sprite.Image, op)
	}
}

func (system *CollectibleSystem) AddParticle(position *components.Position) {
	observer := observers.GetGameObserver(system.World)

	ptmapper := generic.NewMap3[
		components.Position,
		components.Particle,
		components.Timer,
	](system.World)

	particleID := ecs.ComponentID[components.Particle](system.World)

	entity := ptmapper.New()
	pos, particle, timer := ptmapper.Get(entity)
	observer.AddEntity(entity, particleID)

	particle.Index = assets.Tiles["Particle"].Particle
	particle.Tiles = assets.Tiles["Particle"].Tiles

	pos.Update(
		position.X-(16), // FIXME: use global tilesize!
		position.Y-(16),
		64,
	)

	timer.Start(config.PARTICLE_LOOPWAIT)
}
