#include "scripts/Input.txt"

namespace DebugLevelCreate
{
	
	const array<AData@> actorData = {
	AData("BPRaptor_Common", false),
	AData("BPPurlin_MeleeCommon", false),
	AData("BPPurlin_Boss", false),
	AData("BPHead", false),
	AData("BPGruntMelee_PoacherKnife", false),
	AData("BPGruntRange_Poacher", false),
	AData("BPGruntGuardRange-Poacher", false),
	AData("BPGruntGuardGrenade", false),
	AData("BPStalker", false),
	AData("BPBeetle", false),
	AData("BPFish", false),
	AData("Animal_Deer", true),
	AData("Animal_Boar", true),
	AData("BPDetail_Spike03", true), AData("BPDetail_Ruins03", false), AData("BPDetail_Ruins04", true),
	AData("BPDetail_Ruins05", true), AData("BPDetail_Bones01", false), AData("BPDetail_Bones02", false), AData("BPDetail_Bones03", false),
	AData("BPDetail_Bones05", false), AData("BPDetail_CatacombCoffin", true), AData("BPDetail_CityStatue01", true), AData("BPDetail_CaveRock01", true),
	AData("BPDetail_CaveRock02", true), AData("BPDetail_CaveRock06", true), AData("BPDetail_CaveRock07", true), AData("BPDetail_CaveRock08", true),
	AData("BPDetail_Weeds01", false), AData("BPDetail_Weeds02", false), AData("BP_Statue1", true), AData("BP_Statue2", true),
	AData("BPDetail_VillageFence01", false), AData("BPDetail_TorchPot01", true),
	AData("BPDestruct-PalmTree", true),
	AData("Plant1", false), AData("Plant2", false), AData("Plant3", false), AData("Plant4", false), AData("Plant5", false),
	AData("Plant6", false), AData("Plant7", false), AData("Plant8", false), AData("Plant9", false), AData("Plant10", false),
	AData("Plant11", false), AData("Plant12", false), AData("Plant13", false), AData("Plant14", false), AData("Plant15", false),
	AData("Plant16", false), AData("Plant17", false), AData("Plant18", false),
	AData("BP_FallingRocks", false), 
	AData("BP_SpikeFenceLarge", false), 
	AData("BP_SpikeFence", false),
	AData("BPDetail-Tree1", false), AData("BPDetail-Tree2", false), AData("BPDetail-Tree3", true), AData("BPDetail-Tree4", false),
	AData("BPDetail-Tree5", true), AData("BPDetail-Tree6", false),
	AData("BPDetail-TechPlate", false), AData("BPDetail-FirePlace", true), AData("BPDetail-Ladder", true)
	
	};
	
	//========================================================================================================================
	// Actor Data
	//========================================================================================================================
	class AData {
		kStr name;
		bool isSolid;
		AData(const kStr &in newName, bool newIsSolid) {
			name = newName;
			isSolid = newIsSolid;
		}
	}
	//========================================================================================================================
	
	float yawRotate = Math::Deg2Rad(22.5f);
	float inputDelayValue = 0.12f;
	float inputDelay = 0.0f;
	int actorSelectIndex = 0;
	int actorInc = 1;
	array<kActor@> actors;
	int selectedActorIndex = 0;
	kActor @selectedActor;
	int alignType = 0; //0 = none, 1 = step radius, 2 = floor height, 3 = Player Position
	bool didInit = false;
	Input input;
	//------------------------------------------------------------------------------------------------------------------------
	void Update() {
		if (!didInit) {
			Init();
		}
		input.Update();
		
		if (CheckDeleteRemovedActors()) {
			return;
		}
		
		if (selectedActor !is null) {
			if (selectedActor.IsStale()) {
				DeleteActor(selectedActorIndex);
				DeselectActor();
				return;
			}
			
			if (CanMoveActor(@selectedActor)) {
				kVec3 newOrigin = PlayerForwardPos();
				//0 = none, 1 = step radius, 2 = floor height, 3 = Player Position
				if (alignType == 1) {
					newOrigin.z = selectedActor.FloorHeight() + selectedActor.StepHeight();
				} else if (alignType == 2) {
					newOrigin.z = selectedActor.FloorHeight();
				} else if (alignType == 3) {
					newOrigin = Player.Actor().Origin();
				}
				selectedActor.Origin() = newOrigin;
			}
			
			kStr alignText = "None";
			if (alignType == 1) {
				alignText = "Step";
			} else if (alignType == 2) {
				alignText = "Floor";
			} else if (alignType == 3) {
				alignText = "Player";
			}
			
			// DisplayInfo("" + selectedActorIndex + " " + GetActorGVName(selectedActorIndex) + " - Yaw " + Math::Rad2Deg(selectedActor.Yaw()),
			// "QE-Rotate Z-Delete M0-Deselect Align-" + isFloorAligning, "");
			DisplayInfo("" + selectedActorIndex + " " + GetActorGVName(selectedActorIndex) + " - Yaw " + Math::RoundToInt(Math::Rad2Deg(selectedActor.Yaw())),
			"QE-Rotate Z-Delete M0-Deselect Align-" + alignText, "");
			//Yaw -
			if (input.WeaponLeftDown()) {
				selectedActor.Yaw() -= yawRotate;
			}
			//Yaw +
			if (input.WeaponRightDown()) {
				selectedActor.Yaw() += yawRotate;
			}
			//Align Selected actor to floor
			if (input.MapZoomInDown()) {
				inputDelay = inputDelayValue;
				alignType++;
				if (alignType > 3) {
					alignType = 0;
				}
			}
			//Delete Selected actor
			if (input.MapZoomOutDown()) {
				inputDelay = inputDelayValue;
				DeleteActor(selectedActorIndex);
				DeselectActor();
			}
			//UnSelect Actor
			if (input.AttackDown()) {
				inputDelay = inputDelayValue;
				DeselectActor();
			}
		} else {
			//find the closest Actor
			kActor @closestActor = null;
			int closestActorIndex = 0;
			float closestDist = 45000.0f; //9999999.0f;
			for (int i = 0; i < int(actors.length()); i++) {
				float d = Player.Actor().DistanceToPoint(actors[i].Origin());
				if (d < closestDist) {
					closestDist = d;
					closestActorIndex = i;
					@closestActor = actors[i];
				}
			}
			//Select Previous Actor
			if (input.WeaponLeftDown()) {
				inputDelay = inputDelayValue;
				actorSelectIndex = Math::Clamp(actorSelectIndex - actorInc, 0, actorData.length() - 1);
			}
			//Select Next Actor
			if (input.WeaponRightDown()) {
				inputDelay = inputDelayValue;
				actorSelectIndex = Math::Clamp(actorSelectIndex + actorInc, 0, actorData.length() - 1);
			}
			//Output to console and stdout.txt file
			if (input.MapZoomOutDown()) {
				inputDelay = inputDelayValue;
				Output();
			}
			//Select Next/Prev Actor Increment Value
			if (input.JumpDown()) {
				inputDelay = inputDelayValue;
				if (actorInc != 10) {
					actorInc = 10;
				} else {
					actorInc = 1;
				}
			}
			//Select Closest Actor
			if (input.AttackDown()) {
				inputDelay = inputDelayValue;
				if (closestActor !is null) {
					@selectedActor = @closestActor;
					selectedActorIndex = closestActorIndex;
				}
			}
			//Place Selected Actor
			if (input.MapZoomInDown()) {
				inputDelay = inputDelayValue;
				@selectedActor = CreateActorBrush();
				selectedActorIndex = int(actors.length - 1);
			}
			
			kStr selectedActorName = actorData[actorSelectIndex].name;
			bool selectedActorIsSolid = actorData[actorSelectIndex].isSolid;
			Game.PrintLine("Brush - " + selectedActorName, 0);
			
			if (closestActor !is null) {
				kStr closestName = GetActorGVName(closestActorIndex);
				Game.PrintLine("Closest Actor " + closestActorIndex + " - " + closestName, 1);
			} else {
				Game.PrintLine("Closest Actor - None", 1);
			}
			Game.PrintLine("Z-Output X-Add M0-Select Inc-" + actorInc, 2);
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	bool CanMoveActor(kActor @actor) {
		kStr s;
		actor.Definition().GetString("classname", s);
		//can't move if is an emitter
		if (s.IndexOf("kexEmitter") >= 0) {
			return false;
		}
		return true;
	}
	//------------------------------------------------------------------------------------------------------------------------
	void DeselectActor() {
		@selectedActor = null;
		alignType = 0;
	}
	//------------------------------------------------------------------------------------------------------------------------
	// Checks if any actors are null or IsStale(). Deletes them from the actors array and returns true. else returns false.
	//------------------------------------------------------------------------------------------------------------------------
	bool CheckDeleteRemovedActors() {
		bool deletedActor = false;
		int actorsLength = int(actors.length());
		for (int i = 0; i < actorsLength; i++) {
			kActor @actor = actors[i];
			if (actor is null or actor.IsStale()) {
				deletedActor = true;
				DeleteActor(i);
				DeselectActor();
			}
		}
		return deletedActor;
	}
	//------------------------------------------------------------------------------------------------------------------------
	kVec3 PlayerForwardPos() {
		float r = Player.Actor().Yaw();
		kVec3 dir = kVec3(Math::Sin(r), Math::Cos(r), 0.0f);
		kVec3 forwardPos = Player.Actor().Origin() + (dir * 100.0f);
		return forwardPos;
	}
	//------------------------------------------------------------------------------------------------------------------------
	kStr GetActorGVName(int actorIndex) {
		kStr name;
		GameVariables.GetString("BP.LC.Actor" + actorIndex + ".name", name);
		return name;
	}
	//------------------------------------------------------------------------------------------------------------------------
	bool GetActorGVSolid(int actorIndex) {
		bool isSolid;
		GameVariables.GetBool("BP.LC.Actor" + actorIndex + ".solid", isSolid);
		return isSolid;
	}
	//------------------------------------------------------------------------------------------------------------------------
	void SetActorGameVariables(int actorIndex, const kStr &in name, bool isSolid) {
		SetGameVariable("BP.LC.Actor" + actorIndex + ".name", name);
		// SetGameVariable("BP.LC.Actor" + actorIndex + ".dname", name);
		SetGameVariable("BP.LC.Actor" + actorIndex + ".solid", isSolid ? "1" : "0");
	}
	//------------------------------------------------------------------------------------------------------------------------
	void SetGameVariable(const kStr &in key, const kStr &in text) {
		if (!GameVariables.HasKey(key)) {
			GameVariables.Add(key, text);
		} else {
			GameVariables.SetValue(key, text);
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	kActor@ CreateActorBrush() {
		kStr selectedActorName = actorData[actorSelectIndex].name;
		bool selectedActorIsSolid = actorData[actorSelectIndex].isSolid;
		float yaw = Player.Actor().Yaw() - (Player.Actor().Yaw() % yawRotate);
		int sector = Player.Actor().SectorIndex();
		return AddActor(selectedActorName, selectedActorIsSolid, Player.Actor().Origin().x, Player.Actor().Origin().y, Player.Actor().Origin().z, yaw, sector);
	}
	//------------------------------------------------------------------------------------------------------------------------
	kActor@ AddActor(const kStr &in name, bool isSolid, float x, float y, float z, float yaw, int sector) {
		kActor @actor;
		if (!isSolid) {
			@actor = SpawnActor(name, x, y, z, sector, yaw);
		} else {
			@actor = SpawnSolidActor(name, x, y, z, sector, yaw);
		}
		
		kStr s;
		actor.Definition().GetString("classname", s);
		if (s.IndexOf("kexAI") >= 0) {
			actor.AnimState().Stop();
			actor.CastToAI().AIFlags() |= AIF_NOTHINK;
		}
		actor.Flags() |= AF_INVINCIBLE;
		actor.Flags() |= AF_NOMOVEMENT;
		
		SetActorGameVariables(int(actors.length()), name, isSolid);
		actors.insertLast(@actor);
		return @actor;
	}
	//------------------------------------------------------------------------------------------------------------------------
	void DeleteActor(int actorIndex) {
		int actorsLength = int(actors.length());
		if (actorsLength > 1 && actorIndex < actorsLength - 1) {
			for (int i = actorIndex; i < actorsLength; i++) {
				kStr name = GetActorGVName(i + 1);
				bool isSolid = GetActorGVSolid(i + 1);
				SetActorGameVariables(i, name, isSolid);
			}
		}
		
		if (!actors[actorIndex].IsStale()) {
			actors[actorIndex].Remove();
		}
		actors.removeAt(actorIndex);
	}
	//------------------------------------------------------------------------------------------------------------------------
	void DisplayInfo(const kStr &in topText, const kStr &in middleText, const kStr &in bottomText) {
		int iTime = 60.0f;
		Game.PrintLine(bottomText, 0, iTime); //bottom
		Game.PrintLine(middleText, 1, iTime); //middle
		Game.PrintLine(topText, 2, iTime); //top
	}
	//------------------------------------------------------------------------------------------------------------------------
	void Output() {
		Sys.Print("---"); //incase previous line didn't do line break
		Sys.Print("<BP.LevelActors>");
		int actorsLength = int(actors.length());
		for (int i = 0; i < actorsLength; i++) {
			kStr name = GetActorGVName(i);
			bool isSolid = GetActorGVSolid(i);
			kActor @actor = actors[i];
			float x = actor.Origin().x;
			float y = actor.Origin().y;
			float z = actor.Origin().z;
			float yaw = actor.Yaw();
			int sector = actor.SectorIndex();
			kStr s = isSolid ? "SpawnSolidActor(" : "SpawnActor(";
			s += "\"" + name + "\", " + x + "f, " + y + "f, " + z + "f, " + sector + ", " + yaw + "f);";
			Sys.Print(s);
		}
		Sys.Print("</BP.LevelActors>");
		Sys.Print("<BP.LevelActorsScript>");
		for (int i = 0; i < actorsLength; i++) {
			kActor @actor = actors[i];
			kStr s = "AddActor(\"" + GetActorGVName(i) + "\", " + GetActorGVSolid(i) + ", " + actor.Origin().x + "f, " + actor.Origin().y + "f, " + actor.Origin().z + "f, " + actor.Yaw() + "f, " + actor.SectorIndex() + ");";
			Sys.Print(s);
		}			
		Sys.Print("</BP.LevelActorsScript>");
	}
	//------------------------------------------------------------------------------------------------------------------------
	void Init() {
		didInit = true;
		input = Input();
		Player.Actor().PlayerFlags() |= PF_GOD;
		SetupInititalMapActors();
	}
	//------------------------------------------------------------------------------------------------------------------------
	// Copy Setup Script Actors Text Here!!!
	//------------------------------------------------------------------------------------------------------------------------
	void SetupInititalMapActors() {

	}
	//------------------------------------------------------------------------------------------------------------------------
}
