#include "stdafx.h"
#include "SurvivalAgentPlugin.h"
#include "BehaviourTree.h"
#include "IExamInterface.h"
#include "Blackboard.h"
#include "Thinker.h"
#include "BigThink.h"
#include "WorldExplorationGrid.h"

//Called only once, during initialization
void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& info)
{
	//Retrieving the interface
	//This interface gives you access to certain actions the AI_Framework can perform for you
	m_pInterface = static_cast<IExamInterface*>(pInterface);

	//Information for the leaderboards!
	info.BotName = "Cat";
	info.Student_Name = "Bram Verhulst";//No special characters allowed. Highscores won't work with special characters.
	info.Student_Class = "2DAE11";
	info.LB_Password = "ILikeCuteCats!";//Don't use a real password! This is only to prevent other students from overwriting your highscore!
	
	m_Thinker = new Thinker();

	constexpr float closeRadius{ 50.f };
	const float farRadius{ m_pInterface->World_GetInfo().Dimensions.x };

	const std::string searchTimer{ "Search" };

	Blackboard* blackboardPtr{ CreateBlackboard() };
	m_BehaviourTree = new BT::BehaviorTree(blackboardPtr,
		new BT::Selector({
			BigThink::ItemHandling(),
			BigThink::ZombieHandling(),
			BigThink::PurgeZoneHandling(),
			BigThink::PickUpHandling(),
			BigThink::HouseHandling()
	}));
	
	Thinker* thinkerPtr{};
	blackboardPtr->GetData("Brain", thinkerPtr);

	const WorldInfo& worldInfo = m_pInterface->World_GetInfo();
	thinkerPtr->m_ExplorationGrid.SetWorldInfo(worldInfo);

	m_pExplorationGrid = &m_Thinker->m_ExplorationGrid;
	m_pExplorationGrid->SetInterfacePtr(m_pInterface);



}

Blackboard* SurvivalAgentPlugin::CreateBlackboard() {

	Blackboard* blackboardPtr = new Blackboard();

	blackboardPtr->AddData("Brain", m_Thinker);
	blackboardPtr->AddData("Interface", m_pInterface);
	blackboardPtr->AddData("Steering", SteeringPlugin_Output{});
	blackboardPtr->AddData("Target", m_Target);
	blackboardPtr->AddData("Spin", false);

	blackboardPtr->AddData("FailSafe", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("MaxFailSafe", 5.f);
	blackboardPtr->AddData("FailSafeDoOnce", false);

	blackboardPtr->AddData("TargetZombie", EnemyInfo{});
	blackboardPtr->AddData("angleDiff", float{});
	blackboardPtr->AddData("TimerShotgun", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("TimerShotgunDoOnce", false);
	blackboardPtr->AddData("MaxTimeShotgun", 1.f);
	blackboardPtr->AddData("TimerPistol", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("TimerPistolDoOnce", false);
	blackboardPtr->AddData("MaxTimePistol", 1.f);

	blackboardPtr->AddData("TargetItem", ItemInfo{});
	blackboardPtr->AddData("NextFreeSlot", 0);

	blackboardPtr->AddData("TargetHouse", HouseInfo{});
	blackboardPtr->AddData("TimerBeforeLeaving", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("TimerBeforeLeavingDoOnce", false);
	blackboardPtr->AddData("MaxTimeBeforeLeaving", 3.f);

	blackboardPtr->AddData("MaxTimeNoHouse", 30.f);
	blackboardPtr->AddData("TimerNoHouse", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("TimerNoHouseDoOnce", false);

	blackboardPtr->AddData("TimerStuckTimer", std::chrono::steady_clock::time_point{});
	blackboardPtr->AddData("MaxStuckTimer", 5.f);
	blackboardPtr->AddData("TimerStuckTimerDoOnce", false);

	blackboardPtr->AddData("LastPosition", m_pInterface->Agent_GetInfo().Position);
	blackboardPtr->AddData("LastMovementTime", std::chrono::steady_clock::now());

	return blackboardPtr;
}

void SurvivalAgentPlugin::UpdateBlackboard(const SteeringPlugin_Output& steering) {
	Blackboard* blackboard{ m_BehaviourTree->GetBlackboard() };

	blackboard->ChangeData("Steering", steering);
	blackboard->GetData("Target", m_Target);
}

SurvivalAgentPlugin::~SurvivalAgentPlugin() {
	SAFE_DELETE(m_BehaviourTree);
	SAFE_DELETE(m_Thinker);
}

//Called only once
void SurvivalAgentPlugin::DllInit()
{
	//Called when the plugin is loaded
}

//Called only once
void SurvivalAgentPlugin::DllShutdown()
{
	//Called when the plugin gets unloaded
}

//Called only once, during initialization. Only works in DEBUG Mode
void SurvivalAgentPlugin::InitGameDebugParams(GameDebugParams& params)
{
	params.AutoFollowCam = true; //Automatically follow the AI? (Default = true)
	params.RenderUI = true; //Render the IMGUI Panel? (Default = true)
	params.SpawnEnemies = true; //Do you want to spawn enemies? (Default = true)
	params.EnemyCount = 20; //How many enemies? (Default = 20)
	params.GodMode = false; //GodMode > You can't die, can be useful to inspect certain behaviors (Default = false)
	params.LevelFile = "GameLevel.gppl";
	params.AutoGrabClosestItem = true; //A call to Item_Grab(...) returns the closest item that can be grabbed. (EntityInfo argument is ignored)
	params.StartingDifficultyStage = 1;
	params.InfiniteStamina = false;
	params.SpawnDebugPistol = true;
	params.SpawnDebugShotgun = true;
	params.SpawnPurgeZonesOnMiddleClick = true;
	params.PrintDebugMessages = true;
	params.ShowDebugItemNames = true;
	params.Seed = int(time(nullptr)); //-1 = don't set seed. Any other number = fixed seed //TIP: use Seed = int(time(nullptr)) for pure randomness
}

void SurvivalAgentPlugin::Update_Debug(float dt)
{
	//Demo Event Code
	//In the end your Agent should be able to walk around without external input
	if (m_pInterface->Input_IsMouseButtonUp(Elite::InputMouseButton::eLeft))
	{
		//Update_Debug target based on input
		Elite::MouseData mouseData = m_pInterface->Input_GetMouseData(Elite::InputType::eMouseButton, Elite::InputMouseButton::eLeft);
		const Elite::Vector2 pos = Elite::Vector2(static_cast<float>(mouseData.X), static_cast<float>(mouseData.Y));
		m_Target = m_pInterface->Debug_ConvertScreenToWorld(pos);
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_Space))
	{
		m_CanRun = true;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_Left))
	{
		m_AngSpeed -= Elite::ToRadians(10);
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_Right))
	{
		m_AngSpeed += Elite::ToRadians(10);
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_G))
	{
		m_GrabItem = true;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_U))
	{
		m_UseItem = true;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_R))
	{
		m_RemoveItem = true;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_X))
	{
		m_DestroyItemsInFOV = true;
	}
	else if (m_pInterface->Input_IsKeyboardKeyUp(Elite::eScancode_Space))
	{
		m_CanRun = false;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_Delete))
	{
		m_pInterface->RequestShutdown();
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_KP_Minus))
	{
		if (m_InventorySlot > 0)
			--m_InventorySlot;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_KP_Plus))
	{
		if (m_InventorySlot < 4)
			++m_InventorySlot;
	}
	else if (m_pInterface->Input_IsKeyboardKeyDown(Elite::eScancode_Q))
	{
		ItemInfo info = {};
		m_pInterface->Inventory_GetItem(m_InventorySlot, info);
		std::cout << (int)info.Type << std::endl;
	}
}

//This function calculates the new SteeringOutput, called once per frame
SteeringPlugin_Output SurvivalAgentPlugin::UpdateSteering(float dt)
{
	auto steering = SteeringPlugin_Output(Elite::Vector2{}, m_pInterface->Agent_GetInfo().MaxAngularSpeed, true, false);
	bool spin{ false };

	UpdateBlackboard(steering);

	m_Thinker->m_ExplorationGrid.Update(m_pInterface->Agent_GetInfo(), m_pInterface->GetHousesInFOV());

	m_BehaviourTree->Update();

	m_BehaviourTree->GetBlackboard()->GetData("Steering", steering);
	m_BehaviourTree->GetBlackboard()->GetData("Spin", spin);

	if (m_pInterface->Agent_GetInfo().Stamina >= 10 || m_Running) {
		m_Running = true;
		steering.RunMode = false;
	}

	if (m_pInterface->Agent_GetInfo().Stamina <= 0.1f) {
		m_Running = false;
		steering.RunMode = false;
	}

	if (spin) {
		steering.AutoOrient = false;
	}

	if (m_GrabItem) {
		ItemInfo item;

		m_BehaviourTree->GetBlackboard()->GetData("TargetItem", item);

		if (m_pInterface->GrabItem(item)) {
			m_pInterface->Inventory_AddItem(m_InventorySlot, item);
		}
	}

	std::cout << m_pInterface->Agent_GetInfo().Position.x << " " << m_pInterface->Agent_GetInfo().Position.y << std::endl;

	return steering;


}

//This function should only be used for rendering debug elements
void SurvivalAgentPlugin::Render(float dt) const
{
	//This Render function should only contain calls to Interface->Draw_... functions
	m_pInterface->Draw_SolidCircle(m_Target, .7f, { 0,0 }, { 1, 0, 0 });
	
	m_pInterface->Draw_Circle(m_pInterface->Agent_GetInfo().Position, 500.f, { 1.f, 0.f, 0 });
	m_pInterface->Draw_Circle(m_pInterface->Agent_GetInfo().Position, 400.f, { 1.f, 1.f, 0 });
	m_pInterface->Draw_Circle(m_pInterface->Agent_GetInfo().Position, 50.f, { 1.f, 1.f, 1.f });
	m_pInterface->Draw_Circle(m_pInterface->Agent_GetInfo().Position, 15.f, { 0.f, 1.f, 1.f });
	m_pInterface->Draw_Circle(m_pInterface->Agent_GetInfo().Position, 8.f, { 0.f, 1.f, 1.f });

	
	m_pExplorationGrid->RenderDebug();
}


