#include <iostream>
#include "Components.h"
#include "GameData.h"
#include "CharacterModule.h"
#include "BoatModule.h"
#include "WaterModule.h"
#include "BoatModule.h"
#include "SlotsModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
SlotsModule::SlotsModule(flecs::world &ecs)
{
    ZoneScoped;
    ecs.module<SlotsModule>();
	ecs.import <CharacterModule>();
	ecs.component<ParentSlot>();
	ecs.component<ParentSlotData>();
	ecs.component<ObjectSlots>();
	ecs.import <WaterModule>();
	ecs.import <BoatModule>();
	ecs.observer<const EngineData, const BoatBase>("CreateBoatSlots")
		.event(flecs::OnSet)
		.each([&](flecs::entity e, const EngineData &eng,
              const BoatBase &boat) {
            ZoneScoped;
            createBoatSlots(e);
        });
#if 1
	ecs.system<const EngineData, const CharacterBase, ParentSlot>(
		   "UpdateSlotData")
		.kind(flecs::OnUpdate)
		.with<CharacterDisablePhysics>()
		.without<ParentSlotData>()
		.each([&](flecs::entity e, const EngineData &eng,
			  const CharacterBase &ch, ParentSlot &slot) {
            ZoneScoped;
			if (slot.slot_name == "") {
				slot.removeSlot(e);
				return;
			}

			if (slot.parentIsValid()) {
				if (!slot.check()) {
					slot.removeSlot(e);
					return;
				}
                Ogre::SceneNode *n =
                    ECS::get<CharacterModule>()
                        .characterNodes.at(e);
                slot.addChild(n);
				slot.createSlotData(e);
				std::cout << "base: "
					  << slot.getSlotBase()->getName();
				e.remove<ParentSlot>();
				e.modified<ParentSlotData>();
			}
		});
#endif
#if 0
	ecs.system<const EngineData, const CharacterBase, ParentSlot>(
		   "UpdateSlotCharacterTransforms")
		.kind(flecs::OnUpdate)
		.with<CharacterDisablePhysics>()
		.without<ParentSlotData>()
		.with<WaterReady>()
		.each([&](flecs::entity e, const EngineData &eng,
			  const CharacterBase &ch, ParentSlot &slot) {
			std::cout << e.name() << std::endl;
			if (!slot.parentIsValid())
				return;
			OgreAssert(slot.parent_e.has<ObjectSlots>(),
				   "parent has no slots");
			OgreAssert(e.has<Character>(), "not a character");
			if (slot.parentIsValid()) {
				if (!slot.check()) {
					slot.removeSlot(e);
					OgreAssert(false, "bad slot");
					return;
				}
				slot.createSlot(e);
				slot.createSlotData(e);
			}
		});
#endif
}
void SlotsModule::createBoatSlots(flecs::entity e)
{
    ZoneScoped;
    const EngineData &eng = e.world().get<EngineData>();
	const BoatBase &boat = e.get<BoatBase>();
	int i;
	std::vector<Ogre::Node *> slots = boat.mNode->getChildren();
	for (i = 0; i < slots.size(); i++) {
		Ogre::Any any = static_cast<Ogre::SceneNode *>(slots[i])
					->getUserObjectBindings()
					.getUserAny("type");
		if (!any.has_value())
			continue;
		Ogre::String obj_type = Ogre::any_cast<Ogre::String>(any);
		std::cout << "child type: " << obj_type << std::endl;
	}
	if (slots.size() > 0) {
		ObjectSlots &vs = e.ensure<ObjectSlots>();
		for (i = 0; i < slots.size(); i++) {
			Ogre::Any any = static_cast<Ogre::SceneNode *>(slots[i])
						->getUserObjectBindings()
						.getUserAny("type");
			if (!any.has_value())
				continue;
			Ogre::String obj_type =
				Ogre::any_cast<Ogre::String>(any);
			any = static_cast<Ogre::SceneNode *>(slots[i])
				      ->getUserObjectBindings()
				      .getUserAny("name");
			if (!any.has_value())
				continue;
			Ogre::String obj_name =
				Ogre::any_cast<Ogre::String>(any);
			vs.slots[obj_name] = { obj_type,
					       static_cast<Ogre::SceneNode *>(
						       slots[i]) };
		}
	}
}
void ParentSlot::createSlot(flecs::entity e)
{
    ZoneScoped;
    if (e.has<CharacterBase>()) {
		createCharacterSlot(e);
	}
}
void ParentSlot::createCharacterSlot(flecs::entity e)
{
}
void ParentSlot::removeSlot(flecs::entity e)
{
    ZoneScoped;
    if (e.has<ParentSlot>())
		e.remove<ParentSlot>();
	if (e.has<ParentSlot>())
		e.remove<ParentSlotData>();
}
bool ParentSlot::check() const
{
    ZoneScoped;
    if (!parent_e.has<ObjectSlots>())
		return false;
	const ObjectSlots &slots = parent_e.get<ObjectSlots>();
	if (!slots.exists(slot_name))
		return false;
	return true;
}
bool ParentSlot::parentIsValid()
{
    ZoneScoped;
    if (!parent_e.has<ObjectSlots>())
		return false;
	return true;
}
Ogre::SceneNode *ParentSlot::getSlotBase() const
{
    ZoneScoped;
    if (!check())
		return nullptr;
	const ObjectSlots &slots = parent_e.get<ObjectSlots>();
	Ogre::SceneNode *slot_base = slots.slots.at(slot_name).second;
	return slot_base;
}
void ParentSlot::addChild(Ogre::SceneNode *childNode)
{
    ZoneScoped;
    Ogre::SceneNode *parentNode = getSlotBase();
	if (childNode->getParentSceneNode())
		childNode->getParentSceneNode()->removeChild(childNode);
	parentNode->addChild(childNode);
	childNode->_setDerivedPosition(parentNode->_getDerivedPosition());
	childNode->_setDerivedOrientation(parentNode->_getDerivedOrientation());
}
void ParentSlot::createSlotData(flecs::entity e)
{
    ZoneScoped;
    const ObjectSlots &slots = parent_e.get<ObjectSlots>();
	ParentSlotData &psdata = e.ensure<ParentSlotData>();
	Ogre::SceneNode *slot_base = getSlotBase();
	// Ogre::Vector3 position = slot_base->_getDerivedPosition();
	// Ogre::Quaternion orientation = slot_base->_getDerivedOrientation();
	// ch.mBodyNode->_setDerivedPosition(position);
	// ch.mBodyNode->_setDerivedOrientation(orientation);
	psdata.parent_e = parent_e;
	psdata.parentNode = slot_base;
	psdata.slot_name = slot_name;
	e.remove<ParentSlot>();
	e.modified<ParentSlotData>();
}
bool ObjectSlots::exists(const Ogre::String &name) const
{
    ZoneScoped;
    return slots.find(name) != slots.end();
}
}
