package systems

import (
	"log/slog"
	"openquell/assets"
	"openquell/components"
	. "openquell/components"
	"openquell/grid"
	"openquell/observers"

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

type TransientSystem struct {
	World         *ecs.World
	Selector      *generic.Filter3[Position, Renderable, Transient]
	GridContainer *grid.GridContainer
	SolidMapper   generic.Map4[ // needed for replacement
		components.Position,
		components.Renderable,
		components.Tilish,
		components.Solid]
}

type TransientToWall struct {
	Entity    ecs.Entity
	NewSprite *ebiten.Image
	Position  components.Position
}

func NewTransientSystem(world *ecs.World, gridcontainer *grid.GridContainer) System {
	solidmapper := generic.NewMap4[
		components.Position,
		components.Renderable,
		components.Tilish,
		components.Solid](world)

	system := &TransientSystem{
		Selector:      generic.NewFilter3[Position, Renderable, Transient](),
		World:         world,
		GridContainer: gridcontainer,
		SolidMapper:   solidmapper,
	}

	return system
}

func (system *TransientSystem) Update() error {
	observer := observers.GetGameObserver(system.World)
	posID := ecs.ComponentID[components.Position](system.World)
	veloID := ecs.ComponentID[components.Velocity](system.World)

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

	EntitiestoMakeSolid := []TransientToWall{}

	for query.Next() {
		transientposition, _, transient := 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, _ := transientposition.Intersects(playerposition, playervelocity)
			if ok {
				// display the transient sprite as long as the player crosses it
				transient.Activated = true
			} else {
				// the player crossed the transient wall completely
				if transient.Activated {
					EntitiestoMakeSolid = append(EntitiestoMakeSolid, TransientToWall{
						Entity:    query.Entity(),
						Position:  *transientposition,
						NewSprite: transient.Wall,
					})
					slog.Debug("transient added to make solid")
				}
			}
		}
	}

	for _, convertible := range EntitiestoMakeSolid {
		slog.Debug("transient remove")
		// remove transient entity
		system.World.RemoveEntity(convertible.Entity)

		// replace with solid entity
		slog.Debug("transient add solid", "wall", convertible.NewSprite.Bounds())
		entity := system.SolidMapper.New()
		pos, render, _, _ := system.SolidMapper.Get(entity)

		// set it up apropriately
		pos.Set(&convertible.Position)
		render.Image = convertible.NewSprite
		render.Pos = pos
		slog.Debug("new render", "render", render)

		//  also  setup the  grid  tile  with  a  new solid,  so  that
		// collision detection works
		system.GridContainer.Grid.SetSolidTile(
			assets.NewTileBlock(),
			convertible.Position.Point(),
		)
	}

	return nil
}

func (system *TransientSystem) Draw(screen *ebiten.Image) {
	// write transients (these are no tiles!)
	op := &ebiten.DrawImageOptions{}
	query := system.Selector.Query(system.World)

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

		op.GeoM.Reset()
		op.GeoM.Translate(float64(pos.X), float64(pos.Y))
		screen.DrawImage(render.Image, op)
	}
}
