#include <iostream>
#include <Ogre.h>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Body/BodyID.h>
#include <Jolt/Physics/Character/CharacterBase.h>
#include <Jolt/Physics/Character/Character.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhase.h>
#include <Jolt/Physics/Body/BodyLock.h>
#include <Jolt/Physics/Collision/ContactListener.h>
#include "Components.h"
#include "GameData.h"
#include "CharacterModule.h"
#include "WaterModule.h"
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "LuaData.h"
#include "physics.h"
#include "loader.h"
#include "EventModule.h"
#include "TerrainModule.h"
#include "PhysicsModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
struct PhysicsShape {
	JPH::ShapeRefC shape;
};
struct ConvexHull {};
struct WaterBody {
	std::set<JPH::BodyID> inWater;
	bool isInWater(const JPH::BodyID &id) const;
};
struct TriggerBody {
	void *foo;
};
PhysicsModule::PhysicsModule(flecs::world &ecs)
{
    ZoneScoped;
	ecs.module<PhysicsModule>();
	ecs.import <EventModule>();
	ecs.import <EventTriggerModule>();
	ecs.import <BoatModule>();
	flecs::entity PhysicsPreUpdate =
		ecs.entity().add(flecs::Phase).depends_on(flecs::OnUpdate);
	flecs::entity PhysicsUpdate =
		ecs.entity().add(flecs::Phase).depends_on(PhysicsPreUpdate);
	flecs::entity PhysicsPostUpdate =
		ecs.entity().add(flecs::Phase).depends_on(PhysicsUpdate);
	ecs.component<Physics>().add(flecs::Singleton);
	ecs.component<JPH::BodyID>().member<uint32_t>("mID");
	/* for terrain */
	ecs.component<PhysicsBody>();
	ecs.component<PhysicsShape>();
	ecs.component<PhysicsNode>();
	ecs.component<PhysicsMeshName>();
	ecs.component<PhysicsMeshPtr>();
	ecs.component<PhysicsHeightfieldData>();

    ecs.component<CharacterBody>().on_remove([](flecs::entity e,
                            CharacterBody &body) {
        std::shared_ptr<JPH::Character> ch =
            std::static_pointer_cast<JPH::Character>(body.ch);
        if (ch) {
            if (e.has<JPH::BodyID>())
                e.remove<JPH::BodyID>();
            JoltPhysicsWrapper::getSingleton().destroyCharacter(ch);
            body.ch = nullptr;
        }
    });
    ecs.component<TriggerBody>();
	ecs.component<CharacterVelocity>();
	ecs.component<WaterBody>().add(flecs::Singleton);
	ecs.component<CachedMass>();
	ecs.import <TerrainModule>();
	ecs.import <WaterModule>();
	ecs.system<EngineData, Physics>("physics_update")
		.kind(PhysicsUpdate)
		.each([&](EngineData &e, Physics &ph) {
            ZoneScopedN("physics_update");
			ph.physics->update(e.delta);
		});
	ecs.observer<const EngineData, PhysicsMeshName>(
		   "create_shape_mesh_name")
		.event(flecs::OnSet)
		.without<PhysicsShape>()
		.with<Physics>()
		.write<PhysicsShape>()
		.each([&](flecs::entity e, const EngineData &eng,
			  PhysicsMeshName &name) {
            ZoneScopedN("create_shape_mesh_name");
			Ogre::DefaultHardwareBufferManagerBase dmgr;
			Ogre::MeshPtr mesh =
				Ogre::MeshManager::getSingleton().getByName(
					name.meshName);
			mesh->setHardwareBufferManager(&dmgr);
			mesh->load();
			JPH::ShapeRefC shape =
				JoltPhysicsWrapper::getSingleton()
					.createMeshShape(mesh);
			PhysicsShape &s = e.ensure<PhysicsShape>();
			s.shape = shape;
			e.modified<PhysicsShape>();
		});
	ecs.observer<const EngineData, PhysicsMeshPtr>("create_shape_mesh_ptr")
		.event(flecs::OnSet)
		.without<PhysicsShape>()
		.with<Physics>()
		.write<PhysicsShape>()
		.each([&](flecs::entity e, const EngineData &eng,
			  PhysicsMeshPtr &meshPtr) {
            ZoneScopedN("create_shape_mesh_ptr");
            Ogre::DefaultHardwareBufferManager dmgr;
			Ogre::MeshPtr mesh = meshPtr.mesh;
			if (!mesh->isLoaded()) {
				mesh->setHardwareBufferManager(&dmgr);
				mesh->load();
			}
			JPH::ShapeRefC shape =
				JoltPhysicsWrapper::getSingleton()
					.createMeshShape(mesh);
			PhysicsShape &s = e.ensure<PhysicsShape>();
			s.shape = shape;
			e.modified<PhysicsShape>();
		});
	ecs.observer<const EngineData, PhysicsHeightfieldData>(
		   "create_shape_heightfield")
		.event(flecs::OnSet)
		.without<PhysicsShape>()
		.with<Physics>()
		.write<PhysicsShape>()
		.each([&](flecs::entity e, const EngineData &eng,
			  PhysicsHeightfieldData &hfd) {
            ZoneScopedN("create_shape_heightfield");
            JPH::ShapeRefC shape =
				JoltPhysicsWrapper::getSingleton()
					.createHeightfieldShape(
						hfd.samples, hfd.offset,
						hfd.scale, hfd.sampleCount);
			PhysicsShape &s = e.ensure<PhysicsShape>();
			s.shape = shape;
			e.modified<PhysicsShape>();
			e.remove<PhysicsHeightfieldData>();
		});
#if 1
	ecs.observer<const EngineData, const PhysicsShape, const PhysicsNode,
		     const PhysicsBody>("create_body_from_shape")
		.event(flecs::OnSet)
		.without<JPH::BodyID>()
		.with<Physics>()
		.write<PhysicsShape>()
		.write<JPH::BodyID>()
		.each([&](flecs::entity e, const EngineData &eng,
			  const PhysicsShape &shape, const PhysicsNode &node,
			  const PhysicsBody &body) {
            ZoneScopedN("create_body_from_shape");
            JPH::BodyID id =
				JoltPhysicsWrapper::getSingleton().createBody(
					shape.shape.GetPtr(), 0.0f, node.node,
					(JPH::EMotionType)body.motion,
					(JPH::ObjectLayer)body.layer);
			e.set<JPH::BodyID>(id);
			JoltPhysicsWrapper::getSingleton().addBody(
				id, JPH::EActivation::Activate);
		});
#endif
	ecs.observer<const JPH::BodyID>("remove_body")
		.event(flecs::OnRemove)
		.each([&](flecs::entity e, const JPH::BodyID &id) {
            ZoneScopedN("remove_body");
            JoltPhysicsWrapper::getSingleton().removeBody(id);
            if (e.has<CharacterBase>() || e.has<Character>())
                return;
            if (JoltPhysicsWrapper::getSingleton().bodyIsCharacter(
                    id))
                return;
            JoltPhysicsWrapper::getSingleton().destroyBody(id);
			std::cout << "body destroyed" << std::endl;
		});
    ecs.system<const EngineData, const CharacterBase>("SetupCharacterPh")
        .kind(flecs::OnUpdate)
		.with<Character>()
        .without<CharacterBody>()
        .without<JPH::BodyID>()
        .write<CharacterBody>()
        .write<JPH::BodyID>()
		.each([](flecs::entity e, const EngineData &eng,
			 const CharacterBase &base) {
            ZoneScopedN("SetupCharacterPh");
            CharacterBody &b = e.ensure<CharacterBody>();
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
			b.ch.reset(JoltPhysicsWrapper::getSingleton()
                       .createCharacter(n, 1.75f, 0.23f));
			if (!e.has<CharacterDisablePhysics>())
				static_cast<JPH::Character *>(b.ch.get())
					->AddToPhysicsSystem(
						JPH::EActivation::Activate);
			e.set<JPH::BodyID>(
				static_cast<JPH::Character *>(b.ch.get())
					->GetBodyID());
			e.modified<CharacterBody>();
		});
	ecs.observer<const EngineData, const EventTrigger>(
		   "CreateTriggerPhysics")
		.event(flecs::OnSet)
		.without<JPH::BodyID>()
		.each([](flecs::entity e, const EngineData &eng,
			 const EventTrigger &trigger) {
            ZoneScopedN("CreateTriggerPhysics");
            JPH::ShapeRefC shape =
				JoltPhysicsWrapper::getSingleton()
					.createCylinderShape(trigger.halfheight,
							     trigger.radius);
			JPH::BodyID id =
				JoltPhysicsWrapper::getSingleton().createSensor(
					shape.GetPtr(), trigger.node,
					JPH::EMotionType::Kinematic,
					Layers::MOVING);
			e.set<JPH::BodyID>(id);
			// JoltPhysicsWrapper::getSingleton().setDebugDraw(true);
			JoltPhysicsWrapper::getSingleton().addBody(
				id, JPH::EActivation::Activate);
			JoltPhysicsWrapper::getSingleton().addContactListener(
				id,
				[](const JoltPhysics::ContactListener::
					   ContactReport &report) -> void {
					flecs::entity trigger_e =
						ECS::get()
							.query_builder<
								const JPH::
									BodyID>()
							.with<EventTrigger>()
							.build()
							.find([&](const JPH::BodyID
									  &id) {
								return id == report.id1 ||
								       id == report.id2;
							});
					flecs::entity parent_e =
						trigger_e.parent();
					const JPH::BodyID &trigger_body =
						trigger_e.get<JPH::BodyID>();
					const JPH::BodyID &other_body =
						(trigger_body == report.id1) ?
							report.id2 :
							report.id1;
					flecs::entity other_e =
						ECS::get()
							.query_builder<
								const JPH::
									BodyID>()
							.without<EventTrigger>()
							.build()
							.find([&](const JPH::BodyID
									  &id) {
								return id ==
								       other_body;
							});
					if (!trigger_e.is_valid() ||
					    !other_e.is_valid())
						return;
					if (parent_e.is_valid() &&
					    parent_e == other_e)
						return;
					OgreAssert(
						trigger_e.is_valid(),
						"trigger entity is not valid");
					OgreAssert(other_e.is_valid(),
						   "other entity is not valid");
					const EventTrigger &trg =
						trigger_e.get<EventTrigger>();
					if (report.entered) {
						trigger_e.get_mut<EventData>()
							.add(trigger_e,
							     trg.event +
								     "_enter",
							     trigger_e,
							     other_e);
						other_e.add<InTrigger>(
							trigger_e);
						trigger_e.add<TriggeredBy>(
							other_e);
					} else {
						trigger_e.get_mut<EventData>()
							.add(trigger_e,
							     trg.event +
								     "_exit",
							     trigger_e,
							     other_e);
					}
					ECS::modified<LuaEvent>();
				});
		});
    // FIXME: convert to normal configure to prevent multiple instances
	ecs.system<const EngineData>("init_water")
		.kind(PhysicsPreUpdate)
		.with<TerrainReady>()
		.with<WaterAlmostReady>()
		.without<WaterBody>()
		.each([this](const EngineData &eng) {
            ZoneScopedN("init_water");
            ECS::get().set<WaterBody>({});
		});
	ecs.system<const EngineData, WaterBody>("update_water")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterAlmostReady>()
		.each([this](const EngineData &eng, WaterBody &body) {
            ZoneScopedN("update_water");
            const WaterSurface &water = ECS::get<WaterSurface>();
			body.inWater.clear();
			JoltPhysicsWrapper::getSingleton().broadphaseQuery(
				eng.delta,
				water.mWaterNode->_getDerivedPosition(),
				body.inWater);
			ECS::get().add<WaterReady>();
		});
	ecs.system<const JPH::BodyID, const WaterBody>("update_water_status1")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<InWater>()
		.each([this](flecs::entity e, const JPH::BodyID &id,
			     const WaterBody &body) {
            ZoneScopedN("update_water_status1");
            if (!body.isInWater(id)) {
				e.remove<InWater>();
                ZoneTextF("in water");
            }
		});
	ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.without<InWater>()
		.each([this](flecs::entity e, const JPH::BodyID &id,
			     const WaterBody &body) {
            ZoneScopedN("update_water_status2");
            if (body.isInWater(id)) {
				e.add<InWater>();
                ZoneTextF("not in water");
            }
		});
	ecs.system<const CharacterBody, const WaterBody>(
		   "update_water_character1")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<InWater>()
		.each([this](flecs::entity e, const CharacterBody &ch,
			     const WaterBody &body) {
            ZoneScopedN("update_water_character1");
            JPH::Character *chptr =
				static_cast<JPH::Character *>(ch.ch.get());
            if (!body.isInWater(chptr->GetBodyID())) {
				e.remove<InWater>();
                ZoneTextF("not in water");
            }
		});
	ecs.system<const CharacterBody, const WaterBody>(
		   "update_water_character2")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.without<InWater>()
		.each([this](flecs::entity e, const CharacterBody &ch,
			     const WaterBody &body) {
            ZoneScopedN("update_water_character2");
            JPH::Character *chptr =
				static_cast<JPH::Character *>(ch.ch.get());
            if (body.isInWater(chptr->GetBodyID())) {
				e.add<InWater>();
                ZoneTextF("in water");
            }
		});
	ecs.system<const EngineData, const BoatBase, const WaterBody,
		   const JPH::BodyID>("update_water_boat_enable")
		.kind(PhysicsPreUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.each([this](flecs::entity e, const EngineData &eng,
			     const BoatBase &boat, const WaterBody &body,
			     const JPH::BodyID &id) {
            ZoneScopedN("update_water_boat_enable");
            if (!JoltPhysicsWrapper::getSingleton().isAdded(id))
				JoltPhysicsWrapper::getSingleton().addBody(
					id, JPH::EActivation::Activate);
		});
	ecs.system<const EngineData, const BoatBase, const WaterBody,
		   const JPH::BodyID>("update_water_boat_activation")
		.kind(PhysicsPreUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<InWater>()
		.each([this](flecs::entity e, const EngineData &eng,
			     const BoatBase &boat, const WaterBody &body,
			     const JPH::BodyID &id) {
            ZoneScopedN("update_water_boat_activation");
            if (!JoltPhysicsWrapper::getSingleton().isActive(id))
				JoltPhysicsWrapper::getSingleton().activate(id);
		});
	ecs.system<const EngineData, const BoatBase, const WaterBody,
		   const JPH::BodyID, const CachedMass>(
		   "update_water_boat_buoyancy")
		.kind(PhysicsPreUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<InWater>()
		.each([this](flecs::entity e, const EngineData &eng,
			     const BoatBase &boat, const WaterBody &body,
			     const JPH::BodyID &id, const CachedMass &mass) {
            ZoneScopedN("update_water_boat_buoyancy");
            const WaterSurface &water = ECS::get<WaterSurface>();
			float b = 1.0f, drag = 0.5f, adrag = 0.5f;
			float level = 0.25f;
			float my = JoltPhysicsWrapper::getSingleton()
					   .getPosition(id)
					   .y;
			float myv = JoltPhysicsWrapper::getSingleton()
					    .getLinearVelocity(id)
					    .y;
			/* max = 2.7; min = 1.7 */
			if (my < level)
				b = 2.2f;
			else if (my > level)
				b = 0.9f;
			// std::cout << my << std::endl;
			JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse(
				id, water.mWaterNode->_getDerivedPosition(),
				Ogre::Vector3::UNIT_Y, b, drag, adrag,
				Ogre::Vector3::ZERO, eng.delta);
			/* stabilisation */
			Ogre::Vector3 currentAngularVelocity =
				JoltPhysicsWrapper::getSingleton()
					.getAngularVelocity(id);
			Ogre::Quaternion rotation =
				JoltPhysicsWrapper::getSingleton().getRotation(
					id);
			Ogre::Vector3 rotComp =
				rotation.Inverse() * Ogre::Vector3::UNIT_Y;
			rotComp.y = 0;
			if (Ogre::Math::Abs(rotComp.x) > 0.1f ||
			    Ogre::Math::Abs(rotComp.z) > 0.1f) {
				Ogre::Vector3 localAngularVelocity =
					rotation.Inverse() *
					currentAngularVelocity;
				Ogre::Vector3 localAngularVelocityChange(
					-localAngularVelocity.x, 0,
					-localAngularVelocity.z);
				localAngularVelocityChange -=
					rotComp * 20.0f * eng.delta;
				Ogre::Vector3 angularVelocityChange =
					rotation * localAngularVelocityChange;
				Ogre::Vector3 angularImpulse =
					mass.mass * angularVelocityChange;
				JoltPhysicsWrapper::getSingleton()
					.addAngularImpulse(id, angularImpulse);
			}
		});
	ecs.system<const EngineData, const CharacterBody, const WaterBody>(
		   "update_water_character_buoyancy")
		.kind(PhysicsPreUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<InWater>()
		.without<CharacterDisablePhysics>()
		.with<CharacterBuoyancy>()
		.each([this](flecs::entity e, const EngineData &eng,
			     const CharacterBody &ch, const WaterBody &body) {
            ZoneScopedN("update_water_character_buoyancy");
            JPH::Character *chptr =
				static_cast<JPH::Character *>(ch.ch.get());
			JPH::BodyID id = chptr->GetBodyID();
			if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
				const WaterSurface &water =
					ECS::get<WaterSurface>();
				float my = JoltPhysicsWrapper::getSingleton()
						   .getPosition(id)
						   .y;
				float myv = JoltPhysicsWrapper::getSingleton()
						    .getLinearVelocity(id)
						    .y;
				float b = 1.0f;
				float drag = 0.5f;
				float adrag = 0.05f;
				float level = -1.3f;
				float mdec = 1.0f;
				float minc = 1.0f;
				float h = -my + level;
				if (h < 0)
					h = 0;
				if (my < level - 0.1f)
					b *= 1.1f * (1.0f + 1.2f * h);
				else if (my > level + 0.1f) {
					b *= 0.8f;
					if (myv > 0.1f)
						b *= 0.9f;
					if (my > level + 0.4f)
						b *= 0.5f;
				}
				if (myv < 0.0f)
					drag = 0.7f;
				JoltPhysicsWrapper::getSingleton()
					.applyBuoyancyImpulse(
						id,
						water.mWaterNode
							->_getDerivedPosition(),
						Ogre::Vector3::UNIT_Y, b, drag,
						adrag, Ogre::Vector3::ZERO,
						eng.delta);
			}
		});
    ecs.system<const EngineData, const CharacterBody>(
           "UpdateCharacterPhysicsState")
		.kind(flecs::OnUpdate)
		.with<CharacterUpdatePhysicsState>()
		.write<CharacterUpdatePhysicsState>()
		.each([](flecs::entity e, const EngineData &eng,
			 const CharacterBody &body) {
            ZoneScopedN("UpdateCharacterPhysicsState");
            if (e.has<CharacterDisablePhysics>())
				PhysicsModule::controlPhysics(e, false);
			else
				PhysicsModule::controlPhysics(e, true);
			e.remove<CharacterUpdatePhysicsState>();
		});
	ecs.system<const EngineData, const CharacterBase, const CharacterBody,
		   CharacterVelocity>("HandleVelocity")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.without<CharacterDisablePhysics>()
		.without<CharacterUpdatePhysicsState>()
		.each([this](flecs::entity e, const EngineData &eng,
			     const CharacterBase &chbase,
			     const CharacterBody &body, CharacterVelocity &gr) {
            ZoneScopedN("HandleVelocity");
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
            if (e.has<InWater>() &&
                n->_getDerivedPosition().y > -0.5f)
				e.remove<InWater>();
			Ogre::Vector3 v = gr.velocity;
			v.y = 0.0f;
			JPH::Character *ch =
				static_cast<JPH::Character *>(body.ch.get());
			if (!e.has<InWater>()) {
				if (ch->IsSupported()) {
					v.y = gr.velocity.y;
					gr.gvelocity.y = 0;
				} else {
					v.y = gr.velocity.y;
					v.y += gr.gvelocity.y;
					gr.gvelocity.y += -9.8f * eng.delta;
				}
			} else {
				v = JoltPhysics::convert(
					ch->GetLinearVelocity());
				v.x = gr.velocity.x;
				v.z = gr.velocity.z;
			}
			// gr.velocity.y = 0.0f;
			// v.y = 0.0f;
            OgreAssert(v.squaredLength() < 1000.0f,
                   "shitty velocity setting");
            ch->SetLinearVelocity(
                JoltPhysics::convert<JPH::Vec3>(v));
			gr.velocity = Ogre::Vector3::ZERO;
		});
    ecs.system<CharacterBase>("HandleSubmerge")
        .kind(flecs::OnUpdate)
        .with<TerrainReady>()
        .with<WaterReady>()
        .with<InWater>()
        .each([this](flecs::entity e, CharacterBase &ch) {
            ZoneScopedNC("HandleSubmerge", 0xFF3030);
            float full_subm = 2.0f;
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
            Ogre::Vector3 pos = n->getPosition();
            float current_subm = -Ogre::Math::Clamp(
                pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
                            130.0f) *
                        0.07f,
                -full_subm, 0.0f);
            if (current_subm > 0.9f)
                ch.is_submerged = true;
            else if (current_subm < 0.8f)
                ch.is_submerged = false;
            ZoneTextF("is submerged: %d", (int)ch.is_submerged);
        });
    ecs.system<const EngineData, CharacterBase, const CharacterBody,
		   CharacterVelocity>("HandleVelocityNoPhysics")
		.kind(PhysicsPostUpdate)
		.with<TerrainReady>()
		.with<WaterReady>()
		.with<CharacterDisablePhysics>()
		.without<CharacterUpdatePhysicsState>()
		.each([this](flecs::entity e, const EngineData &eng,
			     CharacterBase &ch, const CharacterBody &body,
			     CharacterVelocity &gr) {
            ZoneScopedNC("HandleVelocityNoPhysics", 0xFF4040);
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
            Ogre::Vector3 v = gr.velocity;
			// v.y = 0.0f;
            n->_setDerivedPosition(n->_getDerivedPosition() +
                           v * eng.delta);
			gr.velocity = Ogre::Vector3::ZERO;
			if (e.has<JPH::BodyID>())
				JoltPhysicsWrapper::getSingleton()
					.setPositionAndRotation(
						e.get<JPH::BodyID>(),
                        n->_getDerivedPosition(),
                        n->_getDerivedOrientation(),
						false);
			if (e.has<InWater>() &&
                n->_getDerivedPosition().y > -0.5f) {
				e.remove<InWater>();
				ch.is_submerged = false;
                ZoneTextF("remove in water");
            }
            if (!e.has<InWater>() && ch.is_submerged) {
				ch.is_submerged = false;
                ZoneTextF("not submerged");
            }
		});
}
flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,
						    float *samples,
						    const Ogre::Vector3 &offset,
						    const Ogre::Vector3 &scale,
						    int sampleCount)
{
    ZoneScoped;
	flecs::entity e = ECS::get().entity();
	e.set<PhysicsHeightfieldData>({ samples, offset, scale, sampleCount });
	e.set<PhysicsBody>({ (uint32_t)JPH::EMotionType::Static,
			     (uint32_t)Layers::NON_MOVING });
	e.set<PhysicsNode>({ node });

	return e;
}
void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
{
    ZoneScoped;
	if (enable) {
		if (e.has<CharacterBase>()) {
			e.remove<CharacterDisablePhysics>();
			OgreAssert(e.has<CharacterBody>(), "No body component");
			OgreAssert(e.has<JPH::BodyID>(),
				   "No body id in entity");
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
            if (!JoltPhysicsWrapper::getSingleton().isAdded(
                    e.get<JPH::BodyID>())) {
                Ogre::Vector3 position =
                    n->_getDerivedPosition();
                Ogre::Quaternion orientation =
                    n->_getDerivedOrientation();
                if (position.y >= -0.5f)
                    e.remove<InWater>();
                JoltPhysicsWrapper::getSingleton()
                    .setPositionAndRotation(
                        e.get<JPH::BodyID>(), position,
                        orientation, false);
                JoltPhysicsWrapper::getSingleton().addBody(
                    e.get<JPH::BodyID>(),
                    JPH::EActivation::Activate);
            }
        }
    } else {
		if (e.has<CharacterBase>()) {
			e.add<CharacterDisablePhysics>();
			if (!e.has<CharacterBody>())
				return;
			OgreAssert(e.has<CharacterBody>(), "No body component");
			OgreAssert(e.has<JPH::BodyID>(),
				   "No body id in entity");
            Ogre::SceneNode *n =
                ECS::get<CharacterModule>().characterNodes.at(
                    e);
            if (JoltPhysicsWrapper::getSingleton().isAdded(
                    e.get<JPH::BodyID>()))
                JoltPhysicsWrapper::getSingleton().removeBody(
                    e.get<JPH::BodyID>());
            Ogre::Vector3 position = n->_getDerivedPosition();
            e.remove<InWater>();
        }
	}
}
bool PhysicsModule::raycastQuery(const Ogre::Vector3 &startPos,
				 const Ogre::Vector3 &endPos,
                 Ogre::Vector3 &position, JPH::BodyID &id)
{
    ZoneScoped;
	return JoltPhysicsWrapper::getSingleton().raycastQuery(startPos, endPos,
                                   position, id);
}

void PhysicsModule::setDebugDraw(bool enable)
{
    JoltPhysicsWrapper::getSingleton().setDebugDraw(enable);
}

bool WaterBody::isInWater(const JPH::BodyID &id) const
{
    ZoneScoped;
#if 0
	flecs::entity e =
		ECS::get().query_builder<const JPH::BodyID>().build().find(
			[&](const JPH::BodyID &bodyid) {
				return bodyid == id;
			});
#endif
	return inWater.find(id) != inWater.end();
}
}
