#include <iostream>
#include <Ogre.h>
#include <OgreFileSystemLayer.h>
#include "GameData.h"
#include "Components.h"
#include "CharacterModule.h"
#include "LuaData.h"
#include "goap.h"
#include "world-build.h"
struct CopulationGoal
	: public BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal {
	CopulationGoal()
		: BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal(
			  "copulation")
	{
	}
	int distance_to(const ECS::Blackboard &state) const override
	{
		if (state.get_flag(ECS::Blackboard::LOW_LUST))
			return 0;
		return 1;
	}
	bool is_reached(const ECS::Blackboard &state) const override
	{
		return state.get_flag(ECS::Blackboard::LOW_LUST);
	}
};

struct SustainabilityGoal
	: public BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal {
	SustainabilityGoal()
		: BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal(
			  "sustainability")
	{
	}
	int distance_to(const ECS::Blackboard &state) const override
	{
		bool healthy = !state.get_flag(ECS::Blackboard::LOW_HEALTH);
		bool able = !state.get_flag(ECS::Blackboard::LOW_STAMINA);
		bool lusty = !state.get_flag(ECS::Blackboard::LOW_LUST);
		if (healthy && able && lusty)
			return 0;
		else if (healthy && able)
			return 1;
		else if (able && lusty)
			return 1;
		else if (healthy && lusty)
			return 1;
		else if (healthy || able || lusty)
			return 2;
		else
			return 3;
	}
	bool is_reached(const ECS::Blackboard &state) const override
	{
		bool healthy = !state.get_flag(ECS::Blackboard::LOW_HEALTH);
		bool able = !state.get_flag(ECS::Blackboard::LOW_STAMINA);
		bool lusty = !state.get_flag(ECS::Blackboard::LOW_LUST);
		return healthy && able && lusty;
	}
};

struct Position {
	Ogre::Vector2 position;
};

struct Velocity {
	Ogre::Vector2 velocity;
};

struct Locked {
	flecs::entity e;
};
struct rest_lust_runner : public BaseActionExec {
	rest_lust_runner()
		: BaseActionExec(ECS::Blackboard::HIGH_LUST |
					 ECS::Blackboard::FULL_LUST,
				 ECS::Blackboard::LOW_LUST)
	{
	}
	void _enter(ECS::Blackboard &state) override
	{
	}
	void _exit(ECS::Blackboard &state) override
	{
	}
	int _execute(ECS::Blackboard &state)
	{
		state.lust += 1;
		if (state.lust > 100)
			state.lust = 100;
		std::cout << ">>> " << state.me.name() << " restored some lust"
			  << std::endl;
		bool ok = state.get_flag(ECS::Blackboard::HIGH_LUST) ||
			  state.get_flag(ECS::Blackboard::FULL_LUST);
		if (ok)
			return BaseAction::OK;
		else
			return BaseAction::BUSY;
	}
};
class rest_lust : public BaseAction {
public:
	rest_lust_runner runner;
	rest_lust(const std::string &name)
		: runner(rest_lust_runner())
		, BaseAction(name, &runner)
	{
	}
#if 0
	bool can_run(const ECS::Blackboard &state, bool debug = false) override
	{
		return true;
	}
	void _plan_effects(ECS::Blackboard &state) override
	{
		state.clear_flag(ECS::Blackboard::LOW_LUST);
		state.set_flag(ECS::Blackboard::HIGH_LUST);
		state.set_flag(ECS::Blackboard::FULL_LUST);
	}
#endif
};
struct rest_runner : public BaseActionExec {
	rest_runner()
		: BaseActionExec(0, ECS::Blackboard::LOW_HEALTH |
					    ECS::Blackboard::LOW_STAMINA)
	{
	}
	int _execute(ECS::Blackboard &state)
	{
		state.health += 10;
		state.stamina += 10;
		if (state.health > 100)
			state.health = 100;
		if (state.stamina > 100)
			state.stamina = 100;
		std::cout << ">>> " << state.me.name() << " is resting"
			  << std::endl;
		bool ok = !state.get_flag(ECS::Blackboard::LOW_HEALTH) &&
			  !state.get_flag(ECS::Blackboard::LOW_STAMINA);
		if (ok)
			return BaseAction::OK;
		else
			return BaseAction::BUSY;
	}
	void _enter(ECS::Blackboard &state) override
	{
	}
	void _exit(ECS::Blackboard &state) override
	{
	}
	bool _can_run(const ECS::Blackboard &state, bool debug = false) override
	{
		bool ret = !state.get_flag(ECS::Blackboard::FULL_HEALTH) ||
			   !state.get_flag(ECS::Blackboard::FULL_STAMINA);
		return ret;
	}
};

class rest : public BaseAction {
	rest_runner runner;

public:
	rest(const std::string &name)
		: runner(rest_runner())
		, BaseAction(name, &runner)
	{
	}
#if 0
	bool can_run(const ECS::Blackboard &state, bool debug = false) override
	{
		bool ret = !state.get_flag(ECS::Blackboard::FULL_HEALTH) ||
			   !state.get_flag(ECS::Blackboard::FULL_STAMINA);
		return ret;
	}
	void _plan_effects(ECS::Blackboard &state) override
	{
		state.clear_flag(ECS::Blackboard::LOW_HEALTH);
		state.clear_flag(ECS::Blackboard::LOW_STAMINA);
	}
	int get_cost(const ECS::Blackboard &state) const override
	{
		return 1;
	}
#endif
};
struct fuck_target_runner : public BaseActionExec {
	flecs::entity e;
	std::string m_name;
	fuck_target_runner(const std::string &name, flecs::entity e)
		: BaseActionExec(ECS::Blackboard::LOW_LUST |
					 ECS::Blackboard::LOW_STAMINA,
				 ECS::Blackboard::HIGH_LUST |
					 ECS::Blackboard::FULL_LUST)
		, e(e)
		, m_name(name)
	{
	}
	const std::string &get_name()
	{
		return m_name;
	}
	bool has_target(const ECS::Blackboard &state)
	{
		return state.get_flag(ECS::Blackboard::HAS_TARGET) &&
		       e.has<Locked>() && e.get<Locked>().e == state.me;
	}
	bool lock_target(ECS::Blackboard &state)
	{
		if (e.has<Locked>() && e.get<Locked>().e != state.me)
			return false;
		if (e.has<Locked>() && e.get<Locked>().e == state.me)
			return true;
		e.set<Locked>({ state.me });
		state.set_flag(ECS::Blackboard::HAS_TARGET);
		return true;
	}
	bool reach_target(ECS::Blackboard &state, float distance)
	{
		float d = state.me.get<Position>().position.squaredDistance(
			e.get<Position>().position);
		if (d < distance) {
			state.me.remove<Velocity>();
			state.set_flag(ECS::Blackboard::REACHED_TARGET);
			return true;
		}
		Ogre::Vector2 v = e.get<Position>().position -
				  state.me.get<Position>().position;
		state.me.ensure<Velocity>().velocity =
			v.normalisedCopy() * 0.5f;
		state.me.modified<Velocity>();
		return false;
	}
	bool unlock_target(ECS::Blackboard &state)
	{
		if (e.has<Locked>() && e.get<Locked>().e != state.me)
			return false;
		if (!e.has<Locked>())
			return false;
		e.remove<Locked>();
		return true;
	}
	int _execute(ECS::Blackboard &state)
	{
		if (!has_target(state)) {
			if (!lock_target(state))
				return BaseAction::ABORT;
			std::cout << ">>> " << e.name() << " is locked by "
				  << state.me.name() << std::endl;
			return BaseAction::BUSY;
		}
		if (state.stamina <= 0)
			return BaseAction::ABORT;
		if (!reach_target(state, 0.04f)) {
			if (state.stamina > 0)
				state.stamina -= 1;
			return BaseAction::BUSY;
		}
		ECS::get_mut<ECS::GameWorld>().command(
			"fuck", std::vector<ECS::GameWorld::Parameter *>());
		if (state.lust >= 5)
			state.lust -= 5;
		if (state.stamina >= 5)
			state.stamina -= 5;
		if (e.has<ECS::Blackboard>()) {
			if (e.get_mut<ECS::Blackboard>().lust > 0) {
				e.get_mut<ECS::Blackboard>().lust -= 5;
				if (e.get_mut<ECS::Blackboard>().stamina > 0)
					e.get_mut<ECS::Blackboard>().stamina -=
						5;
				e.modified<ECS::Blackboard>();
			}
		}
		std::cout << ">>> " << e.name() << " was fucked by "
			  << state.me.name() << std::endl;
		std::cout << ">>> " << state.me.name() << " lust is "
			  << state.lust << std::endl;
		if (state.lust <= 0) {
			unlock_target(state);
			std::cout << ">>> " << get_name() << " completed by "
				  << state.me.name() << std::endl;
			return BaseAction::OK;
		}
		return BaseAction::BUSY;
	}
	void _enter(ECS::Blackboard &state) override
	{
		std::cout << ">>> " << get_name() << " entered by "
			  << state.me.name() << std::endl;
	}
	void _exit(ECS::Blackboard &state) override
	{
		unlock_target(state);
		std::cout << ">>> " << get_name() << " exited by "
			  << state.me.name() << std::endl;
	}
	int _get_cost(const ECS::Blackboard &state) const override
	{
		float d = state.me.get<Position>().position.squaredDistance(
			e.get<Position>().position);
		int cost = d;
		cost += state.lust;
		if (e.has<Locked>() && e.get<Locked>().e != state.me)
			cost += 1000000;
		return cost + 1;
	}
	bool _can_run(const ECS::Blackboard &state, bool debug = false) override
	{
		bool mighty = !state.get_flag(ECS::Blackboard::LOW_STAMINA);
		bool healthy = !state.get_flag(ECS::Blackboard::LOW_HEALTH);
		bool has_target = state.get_flag(ECS::Blackboard::HAS_TARGET);
		bool lusty = !state.get_flag(ECS::Blackboard::LOW_LUST);
		if (is_active(state)) {
			if (e.has<Locked>() && e.get<Locked>().e == state.me)
				return mighty && healthy && has_target && lusty;
			return false;
		}
		bool reached_target =
			state.get_flag(ECS::Blackboard::REACHED_TARGET);
		bool is_male = state.me.has<ECS::Male>();
		bool ret = is_male && mighty && healthy && lusty;
		if (debug) {
			if (!ret && is_male) {
				std::cout << state.me.name() << ": ";
				if (!mighty)
					std::cout << " low stamina "
						  << state.stamina;
				if (!healthy)
					std::cout << " low health ";
				if (!has_target)
					std::cout << " no target ";
				if (!reached_target)
					std::cout << " target is not reached ";
				if (!is_male)
					std::cout << " not male ";
			}
			if (!ret && is_male)
				std::cout << std::endl;
		}
		if (!ret && is_male && debug) {
			if (!mighty)
				std::cout << " low stamina " << state.stamina;
			if (!healthy)
				std::cout << " low health ";
			if (!lusty)
				std::cout << " low lust ";
			std::cout << " " << get_name() << ":" << state.me.name()
				  << ": " << ret << std::endl;
		}
		return ret;
	}
};
flecs::entity create_npc(flecs::world ecs, const char *name, bool male,
			 const Ogre::Vector2 &position)
{
	flecs::entity e = ecs.entity(name);
	e.set<ECS::Blackboard>({ e, 100, 100, 100, 0 });
	if (male)
		e.add<ECS::Male>();
	else
		e.add<ECS::Female>();
	e.set<Position>({ position });
	return e;
}

namespace ECS
{
struct CharacterAIExtraModule {
	CharacterAIExtraModule(flecs::world &ecs)
	{
		ecs.module<CharacterAIExtraModule>();
		ecs.component<Position>();
		ecs.component<Locked>();
		ecs.system<Blackboard, Position, const Velocity>(
			   "UpdateCharacterPosition")
			.kind(flecs::OnUpdate)
			.each([&](flecs::entity e, Blackboard &bb, Position &p,
				  const Velocity &v) {
				p.position +=
					v.velocity * e.world().delta_time();
				std::cout << e.name()
					  << " moved: " << p.position
					  << std::endl;
			});
		ecs.system<Blackboard>("UpdateBaseCharacters")
			.kind(flecs::OnUpdate)
			.without<ActionTarget>()
			.each([&](flecs::entity e, Blackboard &bb) {
				ActionTarget &ac = e.ensure<ActionTarget>();
				if (e.has<Female>()) {
					ac.actions.push_back(
						new DeclareAction<
							fuck_target_runner>(
							"fuck_target",
							"fuck_target", e));
				}
				std::cout << "created targets for: " << e.name()
					  << std::endl;
			});
		ecs.system<Blackboard>("DisplayCharacterData")
			.kind(flecs::OnUpdate)
			.rate(10)
			.each([&](flecs::entity e, Blackboard &bb) {
				std::cout << e.name()
					  << " health: " << bb.health
					  << " stamina: " << bb.stamina
					  << std::endl;
			});
	}
};
}
int main()
{
	//	new Ogre::LogManager;
	//	new Ogre::ArchiveManager;
	//	Ogre::FileSystemLayer fsLayer("Ogre3D");
	//	new Ogre::ResourceGroupManager;
	Ogre::Root *ogre = new Ogre::Root("", "");
	Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
		"Characters", true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./characters", "FileSystem", "Characters", true, true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../characters", "FileSystem", "Characters", true, true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../../characters", "FileSystem", "Characters", true, true);

	Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
		"LuaScripts", true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./lua-scripts", "FileSystem", "LuaScripts", true, true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../lua-scripts", "FileSystem", "LuaScripts", true, true);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../../lua-scripts", "FileSystem", "LuaScripts", true, true);
	//	Ogre::ResourceGroupManager &mgr =
	//		Ogre::ResourceGroupManager::getSingleton();
	flecs::world ecs = ECS::get();
	ECS::setup_minimal();
	ecs.import <ECS::LuaModule>();
	ecs.import <ECS::GameWorldModule>();
	ecs.import <ECS::CharacterModule>();
	ecs.import <ECS::CharacterAIModule>();
	ecs.import <ECS::CharacterAIExtraModule>();
	ecs.import <ECS::GameWorldModule>();
	ecs.set<ECS::EngineData>({ nullptr, 0.0f, 5.0f, 0, 0, false });
	CopulationGoal goal_copulation;
	SustainabilityGoal goal_sustainability;
	ECS::Planner &planner = ecs.ensure<ECS::Planner>();
	struct FuckCommand : public ECS::GameWorld::Command {
		int operator()(const std::vector<ECS::GameWorld::Parameter *>
				       &args) override
		{
			OgreAssert(false, "fuck called");
			return 0;
		}
	};
	FuckCommand fc;
	ecs.get_mut<ECS::GameWorld>().add_command<FuckCommand>("fuck");
	ecs.modified<ECS::GameWorld>();
#if 0
	ecs.get_mut<ECS::GameWorld>().command(
		"fuck", std::vector<ECS::GameWorld::Parameter *>());
#endif
	planner.path.resize(100);
	planner.goals.push_back(&goal_copulation);
	planner.goals.push_back(&goal_sustainability);
	planner.actions.push_back(new DeclareAction<rest_runner>("rest"));
	planner.actions.push_back(
		new DeclareAction<rest_lust_runner>("rest_lust"));
	std::vector<flecs::entity> entities = {
		create_npc(ecs, "m1", true, { -100, -100 }),
		create_npc(ecs, "m2", true, { 0, -100 }),
		create_npc(ecs, "m3", true, { 100, -100 }),
		create_npc(ecs, "f1", false, { -100, 100 }),
		create_npc(ecs, "f2", false, { 0, 100 }),
		create_npc(ecs, "f3", false, { -100, 100 }),
		create_npc(ecs, "f4", false, { 0, 50 })
	};

	bool stop_this = false;
	// AI stuff
	while (true) {
#if 0
		if (ecs.get<ECS::LuaBase>().setup_called ||
		    ecs.get<ECS::LuaBase>().startup_called)
			break;
#endif
		ecs.progress(0.16f);
		if (stop_this)
			break;
	}

	return 0;
}
