package CongaLineHead
import SetupObject
import Spinner
import CongaObjects
import Interpolation


public enum CongaMode
	STRICT
	LOOSE
	PENDULUM

class CongaSegment
	CongaSegment next = null
	CongaSegment prev = null
	SetupObject target
	CongaLineHead head

	real mass = 1

	angle phi = angle(0)
	real dPhi = 0
	real angularMomentum = 0

	construct(CongaLineHead head, SetupObject target)
		this.target = target
		this.head = head
		target.setup.removeAbility(REMOVE_OBJECT_ID)
		phi = head.getPos().angleTo2d(target.getPos())

	function calcPos()
		if head.cmode == CongaMode.STRICT
			if prev == null
				target.setNewPosA(calculateSegmentPos(head.pos.toVec2(), target.pos.toVec2(), 75.), target.pos.angleTo2d(head.pos))
			else
				target.setNewPosA(calculateSegmentPos(prev.target.pos.toVec2(), target.pos.toVec2(), head.segLength), target.pos.angleTo2d(prev.target.pos))
		else if head.cmode == CongaMode.LOOSE
			if prev == null
				if target.pos.distanceTo2dSq(head.getPos().toVec2()) > 32*32
					target.setNewPosA(target.pos.toVec2().lerp(head.pos.toVec2(), head.segLength * 0.001), target.pos.angleTo2d(head.pos))
			else
				if target.pos.distanceTo2dSq(prev.target.pos.toVec2()) > 24*24
					target.setNewPosA(target.pos.toVec2().lerp(prev.target.pos.toVec2(), head.segLength * 0.001), target.pos.angleTo2d(prev.target.pos))

	ondestroy
		if next != null
			next.prev = prev
		if prev != null
			prev.next = next
		if head.firstSegment == this
			head.firstSegment = next
		if not target.done and target.actor != null
			target.setup.addAbility(REMOVE_OBJECT_ID)

public class CongaLineHead extends SetupObject
	CongaSegment firstSegment = null
	CongaMode cmode = CongaMode.LOOSE
	real segLength = 75.

	construct(vec2 pos, player owner)
		super(createUnit(owner, CONGA_LINE_HEAD_ID, pos, angle(0)), createUnit(owner, CONGA_LINE_HEAD_ID, pos, angle(0)), 0.)
		EventListener.add(setup, EVENT_PLAYER_UNIT_SPELL_CAST, () -> onCast())
		sleeps = false
		setup.addAbility(CONGA_CONFIG_ID)

	override function update()
		super.update()
		var segment = firstSegment
		while segment != null
			segment.calcPos()
			segment = segment.next

	function addSegment(SetupObject e)
		boolean isNew = true
		var segment = firstSegment
		while segment != null
			if segment.target == e
				isNew = false
				break
			segment = segment.next
		if isNew
			if firstSegment == null
				firstSegment = new CongaSegment(this, e)
			else
				segment = firstSegment
				while segment.next != null
					segment = segment.next
				segment.next = new CongaSegment(this, e)
				segment.next.prev = segment
				segment.next.phi = e.getPos().angleTo2d(pos.toVec2())
		else
			destroy segment

	function onCast()
		let id = GetSpellAbilityId()
		let t = GetSpellTargetUnit()
		switch id
			case ADD_ID
				let ed = t.getEntity()
				if ed.owner == owner and ed instanceof SetupObject
					addSegment(ed castTo SetupObject)
			case INC_ID
				if segLength < 500.
					segLength += 25
					createFText(pos, vec2(0,0.05), "Segment Length: "+ R2SW(segLength,1,2), 10, 2., colorA(255,255,255,0), owner)
				else
					createFText(pos, vec2(0,0.05), "Maximum Speed", 10, 2., colorA(255,255,255,0), owner)
			case DEC_ID
				if segLength > 25
					segLength -= 25
					createFText(pos, vec2(0,0.05), "Segment Length: "+ R2SW(segLength,1,2), 10, 2., colorA(255,255,255,0), owner)
				else
					createFText(pos, vec2(0,0.05), "Minimum Speed", 10, 2., colorA(255,255,255,0), owner)
			case CONGA_STRICT_ID
				cmode = CongaMode.STRICT
			case CONGA_LOOSE_ID
				cmode = CongaMode.LOOSE

	override function serialize() returns Json
		let json = super.serialize()
		json
		..addProperty(new Property(KEY_TYPE, CONGA_INDEX.toString()))
		return json

	override function setEnabled(boolean b)

	ondestroy
		var l = firstSegment
		while l != null
			let tmp = l
			l = l.next
			destroy tmp


function UnitEntity.setNewPosA(vec2 tpos, angle angl)
	if this instanceof SetupObject
		(this castTo SetupObject).setXY(tpos.toVec3())
		(this castTo SetupObject).setFacing(angl)
	else if this instanceof Spinner
		(this castTo Spinner).setXY(tpos.toVec3())
		(this castTo Spinner).actor.setFacing(angl)

function calculateSegmentPos(vec2 pos1, vec2 pos2, angle a, real segLength) returns vec2
	let angl = pos2.angleTo(pos1) + a
	return vec2(pos1.x - Cos(angl.radians()) * segLength, pos1.y - Sin(angl.radians()) * segLength)

function calculateSegmentPos(vec2 pos1, vec2 pos2, real segLength) returns vec2
	let angl = pos2.angleTo(pos1)
	return vec2(pos1.x - Cos(angl.radians()) * segLength, pos1.y - Sin(angl.radians()) * segLength)