#include "gameplay/ai.h"
#include "gameplay/gameactor.h"

#include "utility/raycaster.h"
#include "utility/script.h"

#include <tracy/Tracy.hpp>


AI::AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>& raycaster)
	: actor(actor), raycaster(raycaster), state(AIState::Idle), 
	  lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3)
{}

void AI::attachBehaviourScript(const std::shared_ptr<AIScript>& behaviour)
{
	// passing out instance of raycaster and this AI into our scripting api
	// pay special attention each ai script has control of only their own instance of ai!
	this->behaviour = behaviour;
	this->behaviour->lua["raycaster"] = raycaster;
	this->behaviour->lua["ai"] = shared_from_this();

	if (this->behaviour->lua["idle"].valid())
		idleFunc = this->behaviour->lua["idle"];
	if (this->behaviour->lua["patrol"].valid())
		patrolFunc = this->behaviour->lua["patrol"];
	if (this->behaviour->lua["alert"].valid())
		alertFunc = this->behaviour->lua["alert"];
}

void AI::update()
{
	try {
		switch (state)
		{
		case AIState::Idle:
			if (idleFunc.valid())
			{
				auto result = idleFunc(actor, target);
				if (!result.valid())
				{
					sol::error err = result;
					std::cerr << "lua error: " << err.what() << std::endl;
				}
			}
			break;
		case AIState::Patrol:
			if (patrolFunc.valid())
			{
				auto result = patrolFunc(actor, target);
				if (!result.valid())
				{
					sol::error err = result;
					std::cerr << "lua error: " << err.what() << std::endl;
				}
			}
			break;
		case AIState::Alert:
			if (alertFunc.valid())
			{
				auto result = alertFunc(actor, target);
				if (!result.valid())
				{
					sol::error err = result;
					std::cerr << "lua error: " << err.what() << std::endl;
				}
			}
			break;
		}
		std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now();
		if (curTime - lastGCTime >= GCTimeout)
		{
			behaviour->lua.collect_gc();
			lastGCTime = curTime;
		}
	}
	catch (const std::exception& e) {
		std::cerr << "Error during AI update: " << e.what() << std::endl;
		state = AIState::Idle;
	}
}