/**
	If you import this package in any of your packages, each order
	any unit makes will be recorded and up to ORDERS_TO_HOLD will be saved.
	You can access these old orders and reissue them using .getLastOrder()
*/
package LastOrder
import RegisterEvents
import UnitIndexer

/** How many past orders to hold.
	Should be at least 2 */
@configurable constant ORDERS_TO_HOLD = 3

/** Clears all recorded orders for this unit */
public function unit.clearLastOrders()
	let index = this.getIndex()
	if lastOrder[index] != null
		destroy lastOrder[index]
		lastOrder[index] = null

/** Returns the lasz recorded order for this unit.
	To access further back orders, use .prev */
public function unit.getLastOrder() returns Order
	return lastOrder[this.getIndex()]

public enum OrderType
	TARGET
	POINT
	IMMEDIATE

Order array lastOrder

public class Order
	Order prev = null

	OrderType orderType
	unit orderedUnit = null
	var orderId = -1

	widget target
	vec2 targetPosition

	var finished = false

	construct(unit ordered, int orderId, OrderType ordtype, widget target, vec2 targetPos)
		this.orderedUnit   = ordered
		this.orderId  = orderId
		this.orderType = ordtype
		this.target = target
		this.targetPosition = targetPos

		if ordered != null
			let index = ordered.getIndex()
			if lastOrder[index] != null
				if lastOrder[index].orderedUnit == orderedUnit
					prev = lastOrder[index]
				else
					destroy lastOrder[index]
			var i = 0
			var e = this
			while e.prev != null
				i++
				if i > ORDERS_TO_HOLD
					let tmp = e.prev
					destroy tmp
					e.prev = null
					break
				e = e.prev

			lastOrder[index] = this

	ondestroy
		if prev != null
			destroy prev
			prev = null

	/** Reissues this order on the unit that originally received the order.
		If you want the reissued order to be recorded as well,
		in a response to another order, i.e. chained, you need to wrap
		it inside using a nulltimer:

			let ordr = caster.getLastOrder().prev
			nullTimer() ->
				ordr.issue()
	*/
	function issue() returns boolean
		if orderedUnit.getTypeId() != 0 and not orderedUnit.isType(UNIT_TYPE_DEAD) and not finished
			switch orderType
				case TARGET
					return orderedUnit.issueTargetOrderById(orderId, target)
				case POINT
					if orderId == 851971
						// This adjusts for a bug in the point order's boolean return when issuing a smart order
						orderedUnit.issuePointOrderById(orderId, targetPosition)
						return true
					else
						return orderedUnit.issuePointOrderById(orderId, targetPosition)
				case IMMEDIATE
					return orderedUnit.issueImmediateOrderById(orderId)
		return false

	/** Use this to copy this order to apply it to another unit.
		The copy must be destroyed manually. */
	function copy() returns Order
		return new Order(null, orderId, orderType, target, targetPosition)


function badOrderList(int id) returns boolean
	return (id==852055 or id==852056 or id==852064 or id==852065 or id==852067 or
			 id==852068 or id==852076 or id==852077 or id==852090 or id==852091 or
			 id==852100 or id==852102 or id==852103 or id==852107 or id==852108 or
			 id==852129 or id==852130 or id==852133 or id==852134 or id==852136 or
			 id==852137 or id==852150 or id==852151 or id==852174 or id==852158 or
			 id==852159 or id==852162 or id==852163 or id==852174 or id==852175 or
			 id==852177 or id==852178 or id==852191 or id==852192 or id==852198 or
			 id==852199 or id==852203 or id==852204 or id==852212 or id==852213 or
			 id==852244 or id==852245 or id==852249 or id==852250 or id==852255 or
			 id==852256 or id==852458 or id==852459 or id==852478 or id==852479 or
			 id==852484 or id==852485 or id==852515 or id==852516 or id==852522 or
			 id==852523 or id==852540 or id==852541 or id==852543 or id==852544 or
			 id==852546 or id==852547 or id==852549 or id==852550 or id==852552 or
			 id==852553 or id==852562 or id==852563 or id==852571 or id==852578 or
			 id==852579 or id==852589 or id==852590 or id==852602 or id==852603 or
			 id==852671 or id==852672)


function unit.orderFilter(int id) returns boolean
	return (id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or
			id == 851993 or (id >= 852055 and id <= 852762)) and not badOrderList(id)

public function unit.abortOrder() returns boolean
	if this.isPaused()
		return false
	else
		this..unpause()..issueImmediateOrder("stop")..pause()
	return true

function conditions() returns boolean
	return GetTriggerUnit().orderFilter(GetIssuedOrderId())

function actions()
	if conditions()
		let	u   = GetTriggerUnit()
		let t   = GetOrderTarget()
		let oid = GetIssuedOrderId()
		Log.trace("ordered: <" + OrderId2StringBJ(oid) + ">")
		if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
			new Order(u, oid, OrderType.TARGET, t, EventData.getOrderTargetPos())
		else if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
			new Order(u, oid, OrderType.POINT, null, EventData.getOrderPos())
		else if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER
			new Order(u, oid, OrderType.IMMEDIATE, null, u.getPos())

function spellActions()
	let index = GetTriggerUnit().getIndex()
	if lastOrder[index] != null
		// Only do this if the last order actually exists
		lastOrder[index].finished = true

init
	registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null, function actions)
	registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null, function actions)
	registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, null, function actions)
	registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, null, function spellActions)

	onUnitDeindex() ->
		getIndexingUnit().clearLastOrders()