#include "die_order.h"
#include <SCBW/api.h>

//helper functions def

namespace {

CThingy* createThingy(u32 spriteId, s16 x, s16 y, u32 playerId);		//0x00488210
void clearUnitSprites(CSprite* unit_sprite, CSprite* subunit_sprite);	//0x00496F00
void function_00497480(CSprite* thingySprite, u32 visibility_flags);	//0x00497480
void _UnitDestructor(CUnit* unit);										//0x004A0740
void UnitDestructor(CUnit* unit);										//0x004A0990
void hideAndDisableUnit(CUnit* unit);									//0x004E6340

} //unnamed namespace

const u32 Func_fixTargetLocation = 0x00401FA0;
	void fixTargetLocation(Point16* coords, u32 unitId) {

		__asm {
			PUSHAD
			MOV EAX, unitId
			MOV EDX, coords
			CALL Func_fixTargetLocation
			POPAD
		}

	}
	const u32 Func_Sub42D810_do = 0x0042D810;
	bool function_0042D810_do(int x, int y, u32 unitId) {

		static Bool32 bPreReturnValue;

		__asm {
			PUSHAD
			MOV EAX, x
			MOV EDI, y
			MOV EBX, unitId
			CALL Func_Sub42D810_do
			MOV bPreReturnValue, EAX
			POPAD
		}

		return (bPreReturnValue != 0);

	}
	const u32 Func_Sub4A01F0 = 0x004A01F0;
	void function_004A01F0(CUnit* unit) {

		__asm {
			PUSHAD
			MOV EAX, unit
			CALL Func_Sub4A01F0
			POPAD
		}

	}
	const u32 Func_CreateUnit = 0x004A09D0;
	CUnit* CreateUnit(u32 unitId, int x, int y, u32 playerId) {

		static CUnit* unit_created;

		__asm {
			PUSHAD
			PUSH playerId
			PUSH y
			MOV ECX, unitId
			MOV EAX, x
			CALL Func_CreateUnit
			MOV unit_created, EAX
			POPAD
		}

		return unit_created;

	}
	const u32 Func_displayLastNetErrForPlayer = 0x0049E530;
	void displayLastNetErrForPlayer(u32 playerId) {
		__asm {
			PUSHAD
			PUSH playerId
			CALL Func_displayLastNetErrForPlayer
			POPAD
		}
	}
	const u32 Func_UpdateUnitStrength = 0x0049FA40;
	void updateUnitStrength(CUnit* unit) {

		__asm {
			PUSHAD
			MOV EAX, unit
			CALL Func_UpdateUnitStrength
			POPAD
		}

	}

namespace hooks {

void spawnCrashedBC(CUnit* unit) {
	int current_x = unit->sprite->position.x - 2;
	int current_y = unit->sprite->position.y - 2;
	u32 wreckage_remaining = 1;

	do {
		bool jump_to_end_of_loop = false; //F4A82

		//check various conditions before attempting to create a broodling
		if(function_0042D810_do(current_x,current_y,UnitId::Special_CrashedNoradII)) {
			

			Point16 inPos,outPos;
			Box16 moveArea;

			inPos.y = current_y;
			moveArea.top = current_y - 32;
			moveArea.bottom = current_y + 32;
			moveArea.left = current_x - 32;
			inPos.x = current_x;
			moveArea.right = current_x + 32;

			//if impossible to avoid a collision even within moveArea,
			//skip the broodling creation code for the current position
			jump_to_end_of_loop = !scbw::checkUnitCollisionPos(unit,&inPos,&outPos,&moveArea,false,0);

			if(!jump_to_end_of_loop) {
				current_y = outPos.y;
				current_x = outPos.x;
			}

		}

		if(!jump_to_end_of_loop) { //F49E2

			Point16 pos;
			CUnit* wreckage;

			pos.x = current_x;
			pos.y = current_y;

			//adjust the position to welcome the unit...I think
			fixTargetLocation(&pos,UnitId::Special_CrashedNoradII);

			//create the broodling
			wreckage = CreateUnit(UnitId::Special_CrashedNoradII,pos.x,pos.y,unit->playerId);

			if(wreckage == NULL) {
				displayLastNetErrForPlayer(unit->playerId);
				jump_to_end_of_loop = true;
			}
			else { //F4A26

				//update various stuff (set hp, set shield...) not finished on CreateUnit
				function_004A01F0(wreckage);
				updateUnitStrength(wreckage);
				wreckage->hitPoints *= .3;

			}

		} //if(!jump_to_end_of_loop)

		//F4A82 (end of loop)
		current_y += 4;
		current_x += 4;
		wreckage_remaining--;

	}while(wreckage_remaining > 0);
}

void orders_Die(CUnit* unit) {

	bool jump_to_795AB = false;

	if(unit->userActionFlags & 4)
		hideAndDisableUnit(unit);

	if(unit->subunit != NULL) {

		clearUnitSprites(unit->sprite,unit->subunit->sprite);

		if(unit->subunit->subunit != NULL) {

			if(units_dat::BaseProperty[(unit->subunit->subunit)->id] & UnitProperty::Subunit) {
				UnitDestructor(unit->subunit->subunit);
				unit->subunit->subunit = NULL;
			}

			_UnitDestructor(unit->subunit);
			unit->subunit->sprite->free();

			unit->subunit->sprite = NULL;
			unit->subunit = NULL;

		}

	}

	unit->mainOrderState = 1;

	if(!(unit->sprite->flags & CSprite_Flags::Hidden)) {

		if(
			!(unit->status & UnitStatus::IsHallucination) ||
			unit->status & UnitStatus::IsSelfDestructing
		)
			jump_to_795AB = true;
		else {

			CThingy* hallucination_smoke;

			scbw::playSound(SoundId::Protoss_TEMPLAR_PTeHal01_WAV,unit);

			hallucination_smoke = 
				createThingy(
					SpriteId::Hallucination_Death1,
					unit->getX(),
					unit->getY(),
					0
				);

			if(hallucination_smoke != NULL) {

				//allow players to see smoke if could already see unit vanishing
				function_00497480(hallucination_smoke->sprite,unit->sprite->visibilityFlags);

			}

		}

	}

	if(!jump_to_795AB) {//79566

		hideAndDisableUnit(unit);

		if(unit->subunit != NULL) {

			if(units_dat::BaseProperty[unit->subunit->id] & UnitProperty::Subunit) {
				UnitDestructor(unit->subunit);
				unit->subunit = NULL;
			}

		}

		_UnitDestructor(unit);
		unit->sprite->free();
		unit->sprite = NULL;

	}
	else {

		if(unit->id == UnitId::battlecruiser) {

			ActiveTile currentTile = scbw::getActiveTileAt(unit->getX(), unit->getY());
			if(currentTile.isWalkable) {
				spawnCrashedBC(unit);
			}

			
		}

		unit->sprite->playIscriptAnim(IscriptAnimation::Death,true);
		_UnitDestructor(unit);
	}

}

;

} //namespace hooks

//-------- Helper function definitions. Do NOT modify! --------//

namespace {

//original referenced name was replaceSprite (but this one is probably
//more accurate since it does create something rather than replacing)
const u32 Func_CreateThingy = 0x00488210;
CThingy* createThingy(u32 spriteId, s16 x, s16 y, u32 playerId) {

	static CThingy* thingy;
	s32 x_ = x;

	__asm {
		PUSHAD
		PUSH playerId
		MOVSX EDI, y
		PUSH x_
		PUSH spriteId
		CALL Func_CreateThingy
		MOV thingy, EAX
		POPAD
	}

	return thingy;

}

;

const u32 Func_clearUnitSprites = 0x00496F00;
void clearUnitSprites(CSprite* unit_sprite, CSprite* subunit_sprite) {

	__asm {
		PUSHAD
		MOV ECX, subunit_sprite
		MOV ESI, unit_sprite
		CALL Func_clearUnitSprites
		POPAD
	}

}

;

const u32 Func_Sub497480 = 0x00497480;
void function_00497480(CSprite* thingySprite, u32 visibility_flags) {

	__asm {
		PUSHAD
		MOV EBX, thingySprite
		PUSH visibility_flags
		CALL Func_Sub497480
		POPAD
	}

}

;

const u32 Func__UnitDestructor = 0x004A0740;
void _UnitDestructor(CUnit* unit) {

	__asm {
		PUSHAD
		MOV EAX, unit
		CALL Func__UnitDestructor
		POPAD
	}

}

;

const u32 Func_UnitDestructor = 0x004A0990;
void UnitDestructor(CUnit* unit) {

	__asm {
		PUSHAD
		MOV ECX, unit
		CALL Func_UnitDestructor
		POPAD
	}

}

;

const u32 Func_unitDeathSomething_0 = 0x004E6340;
void hideAndDisableUnit(CUnit* unit) {

	__asm {
		PUSHAD
		MOV EAX, unit
		CALL Func_unitDeathSomething_0
		POPAD
	  }

}

;

} //Unnamed namespace

//End of helper functions
