package Key
import ItemObject
import ClosureTimers
import ChannelAbilityPreset
import HashMap
import Assets

public constant KEY_DUMMY_ID = compiletime(UNIT_ID_GEN.next())

public let keyIds = ['I009','I00C','I004','I008','I00J','I005','I00O','I00B','I006']

constant DYN_ID = 'A00M'
constant STATIC_ID = 'A01O'

constant INFINITE_ID = compiletime(ABIL_ID_GEN.next())

constant KEY_COLOR = "c"

public class Key extends ItemObject
	var col = EBRColor.WHITE
	var picked = false
	var followWalkable = true
	var infinite = false
	effect infiniteEff = null
	effect returnSfx = null
	let effectMap = new IterableMap<Escaper, effect>()
	var ignoreNextDrop = false

	construct(vec2 ppos, player owner)
		super(ppos, createItem(keyIds[0], ppos), createUnit(owner, KEY_DUMMY_ID, ppos, 0 .fromDeg()))
		EventListener.add(setup.actor, EVENT_PLAYER_UNIT_SPELL_CAST, () -> onCast())

	function recolor()
		setNewActor(createItem(keyIds[col castTo int], pos))
		if returnSfx != null
			returnSfx.destr()
			returnSfx = addEffect("XTex" + (this.col castTo int).toString() + ".mdx", pos)

	override function onPickup(UnitEntity holder)
		if isEscaperPlayer(holder.owner) or this.getCurrentRegion().getPermission(holder.owner) != Permission.GUEST
			var count = 0
			for itm in holder.actor.inventory()
				if itm.getEntity() == this
					count++
			if count > 1
				ignoreNextDrop = true
				holder.actor.removeItem(actor)
				actor.setPos(pos.toVec2())
			else
				let eff = addEffect("war3mapImported\\Key_ITEM.mdx", holder.actor, "overhead")
				..setColor(col.toColor())
				effectMap.put(holder castTo Escaper, eff)
				if infinite
					actor = createItem(keyIds[col castTo int], pos)
					actor.setEntity(this)
				else
					picked = true
					this.holder = holder
					setup.actor.hide()
					activate()
		else
			picked = false
			holder.actor.removeItem(actor)
			actor.setPos(pos.toVec2())
			setup.setXY(pos)
			setup.actor.show()
			this.holder = null

	override function onDrop(UnitEntity e)
		if ignoreNextDrop
			ignoreNextDrop = false
		else
			if effectMap.has(e castTo Escaper)
				effectMap.getAndRemove(e castTo Escaper).destr()

			if infinite
				GetManipulatedItem().setEntity(null)
				GetManipulatedItem().remove()
			else
				picked = false
				holder = null
				setup.setXY(pos)
				setup.actor.show()
				nullTimer() ->
					actor..setEntity(null)
					..remove()
					actor = createItem(keyIds[col castTo int], pos)
					actor.setEntity(this)

	override function update()
		if followWalkable and picked and holder instanceof Escaper
			let escaper = holder castTo Escaper
			if not holder.flying and not escaper.sliding and escaper.alive
				pos = holder.getPos()
		else
			super.update()

	function setFollow(boolean flag)
		if flag and returnSfx != null
			returnSfx.destr()
			returnSfx = null
		else if not flag and returnSfx == null
			returnSfx = addEffect("XTex" + (this.col castTo int).toString() + ".mdx", pos)
		followWalkable = flag

	function setInfinite(boolean flag)
		if not flag and infinite
			infiniteEff.destr()
			infiniteEff = null
			clearKeys()
			effectMap.flush()
		else if flag and not infinite
			infiniteEff = addEffect(Abilities.generalAuraTarget, actor.getPos())
		infinite = flag

	function onCast()
		let id = GetSpellAbilityId()
		switch id
			case DYN_ID
				setInfinite(false)
				setFollow(false)
				setup.actor..removeAbility(DYN_ID)..addAbility(STATIC_ID)
			case STATIC_ID
				setInfinite(false)
				setFollow(true)
				setup.actor..removeAbility(STATIC_ID)..addAbility(DYN_ID)
			case INFINITE_ID
				setInfinite(not infinite)
				setFollow(false)

		for i = 0 to 8
			if colorSpells[i] == id
				col = i castTo EBRColor
				break
		recolor()

	function clearKeys()
		for escaper in effectMap
			for itm in escaper.actor.inventory()
				if itm.getEntity() == this
					itm..setEntity(null)..remove()
			effectMap.get(escaper).destr()

	override function serialize() returns Json
		let json = super.serialize()
		json.addProperty(new Property(KEY_TYPE, KEY_INDEX.toString()))
		json.addProperty(new Property(KEY_COLOR, (col castTo int).toString()))
		return json

	override function deserialize(Json json)
		col = json.getInt(KEY_COLOR) castTo EBRColor
		recolor()

	ondestroy
		clearKeys()
		destroy effectMap

		holder = null
		if returnSfx != null
			returnSfx.destr()
		if infiniteEff != null
			infiniteEff.destr()

@objectgen public function genKey()
	new UnitDefinition(KEY_DUMMY_ID, 'hfoo')
	..setAttacksEnabled(0)
	..setTargetedAs("structure")
	..setAnimationBlendTimeseconds(0)
	..setAnimationCastBackswing(0)
	..setAnimationCastPoint(0)
	..setScalingValue(0.55)
	..setShadowImageUnit("")
	..setShadowImageCenterX(0)
	..setShadowImageCenterY(0)
	..setShadowImageWidth(0)
	..setShadowImageHeight(0)
	..setIconGameInterface("ReplaceableTextures\\CommandButtons\\BTNGhostKey.blp")
	..setModelFile("models\\tcBox.mdl")
	..setSelectionScale(0.55)
	..setSpeedBase(0)
	..setTurnRate(0)
	..setMovementType(MovementType.None)
	..setUnitSoundSet("")
	..setCollisionSize(0)
	..setNormalAbilities(commaList(DYN_ID, 'A05R', INFINITE_ID, REMOVE_OBJECT_ID, GHOST_INVIS_ID, INVULNERABILITY_ID))
	..setName("Key Dummy")
	..setUpgradesUsed("")
	..setBuildTime(1)
	..setUnitClassification("mechanical")
	..setHitPointsMaximumBase(100000000)
	..setHideMinimapDisplay(true)
	..setIsaBuilding(true)
	..setShadowImageUnit("")

	new ChannelAbilityPreset(INFINITE_ID, 1, true)
	..setName("Infinite Key Source")
	..presetTooltipNormal(lvl -> "Mode: One Key Per Player")
	..presetTooltipNormalExtended(lvl -> "This enables every escaper to pick up this key once.\nIf one of the keys is used all instances currently in inventory will be removed.\nForces rooted key mode.")