package items

import (
	"fmt"
	"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamelog"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
)

type CarriedFace interface {
	Drop()
	Pickup()
}

type Carried struct {
	Mass int //масса в граммах
	Bulk int //внешний размер, см3
}

func (c Carried) Type() string {
	return ecs.CarriedComponent
}

func (c Carried) Pickup(who, what ecs.Entity) error {
	// check if im lying on ground
	if !Controller.HasComponent(what, ecs.CoordsComponent) {
		return fmt.Errorf("bug! item with no coords?!")
	}
	// something inexistent on map trying to pickup an item?!
	if !Controller.HasComponent(who, ecs.CoordsComponent) {
		//todo log error - investigate this situation
		return fmt.Errorf("bug! actor with no coords?!")
	}
	//check if who and what are in adjacent tiles
	whoCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords)
	whatCoords := Controller.GetComponent(what, ecs.CoordsComponent).(types.Coords)
	if !whoCoords.IsAdjacentTo(&whatCoords) {
		//todo log error - something strange happened
		return fmt.Errorf("bug! actor and item in inadjacent coords?!")
	}
	//does not have inventory?
	if !Controller.HasComponent(who, ecs.BackpackComponent) {
		//todo send message - you cant carry items
		return fmt.Errorf("bug! actor cannot carry items")
	}
	bp := Controller.GetComponent(who, Backpack{}.Type()).(Backpack)
	err := bp.HasFreeSpace(c.Bulk, c.Mass)
	if err != nil {
		//todo send message - does not fit to your inventory
		return err
	}
	//do not remove appearance
	//remove coords instead (does not exist on map anymore)
	Controller.RemoveComponent(what, ecs.CoordsComponent)
	bp.items = append(bp.items, what)
	gamelog.Log.Msg(fmt.Sprintf("Picked up %s.", Controller.GetComponent(what, ecs.NamedComponent).(Named).GetName()))
	//fuck that, we need to update constantly
	Controller.UpdateComponent(who, ecs.BackpackComponent, bp)
	return nil
}

func (c Carried) Drop(who, what ecs.Entity) {
	var coords types.Coords
	// something inexistent on map trying to drop an item?!
	if !Controller.HasComponent(who, ecs.CoordsComponent) {
		//todo log error - investigate this situation
		return
	} else {
		coords = Controller.GetComponent(who, types.Coords{}.Type()).(types.Coords)
	}
	//does not have inventory?
	if !Controller.HasComponent(who, ecs.BackpackComponent) {
		//todo send message - you cant carry items
		return
	}

	bp := Controller.GetComponent(who, Backpack{}.Type()).(Backpack)
	var dropped ecs.Entity
	bp.items, dropped = ecs.DeleteFromEntitySlice(bp.items, what)
	if dropped != what {
		//todo log error - something strange happened
		return
	}
	//Give dropped back coordinated
	Controller.AddComponent(dropped, coords)
	//Controller.UpdateComponent(what, coords.Type(), coords) //even we need that?
	Controller.UpdateComponent(who, bp.Type(), bp)
}

func (c Carried) GetMass(what ecs.Entity) int {
	return c.Mass
}
func (c *Carried) GetBulk(what ecs.Entity) int {
	return c.Bulk
}

func FindCarriedUnder(who ecs.Entity) []ecs.Entity {
	pickerCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords)
	// _И_ носимые _И_ имеющие координаты, т.е. где-то лежащие
	carrieds := Controller.GetEntitiesWithComponent(ecs.CarriedComponent, ecs.CoordsComponent)
	result := make([]ecs.Entity, 0)
	for _, carried := range carrieds {
		carriedCoords := Controller.GetComponent(carried, ecs.CoordsComponent).(types.Coords)
		if pickerCoords.IsAdjacentTo(&carriedCoords) {
			result = append(result, carried)
		}
	}
	return result
}
