#include <iostream>
#include <Ogre.h>
#include <OgreTerrainGroup.h>
#include <OgreImGuiOverlay.h>
#include <OgreRTShaderSystem.h>
#include <Procedural.h>
#include <nlohmann/json.hpp>
#include "Components.h"
#include "GameData.h"
#include "EditorGizmoModule.h"
#include "TerrainModule.h"
#include "StaticGeometryModule.h"
#include "physics.h"
#include "PhysicsModule.h"
#include "items.h"
#include "temple.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
namespace Items
{
void createTempleItem()
{
    ZoneScoped;
	Ogre::Vector3 itemPosition =
		ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
	Ogre::Quaternion itemOrientation =
		ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
	flecs::entity e = StaticGeometryModule::createItem(
		itemPosition, itemOrientation, "temple");
	Ogre::String prop = StaticGeometryModule::getItemProperties(e);
	nlohmann::json j = nlohmann::json::parse(prop);
	j["size"] = 40.0f;
	j["form"] = "rect";
	j["pillarHeight"] = 16.0f;
	j["pillarRadius"] = 0.5f;
	j["pillarCount"] = 20;
	StaticGeometryModule::setItemProperties(e, j.dump());
	StaticGeometryModule::saveItems();
}
void createTempleMenu()
{
    ZoneScoped;
    if (ImGui::MenuItem("Create"))
		createTempleItem();
}
void createTemplePopup(const std::pair<flecs::entity, Ogre::String> item)
{
    ZoneScoped;
    float size = 40.0f;
	float pillarRadius = 0.5f;
	float pillarHeight = 16.0f;
	int pillarCount = 20;
	Ogre::String form;

	Ogre::String prop = StaticGeometryModule::getItemProperties(item.first);
	nlohmann::json j = nlohmann::json::parse(prop);
	bool changed = false;
	if (j.find("size") != j.end())
		size = j["size"].get<float>();
	if (j.find("pillarRadius") != j.end())
		pillarRadius = j["pillarRadius"].get<float>();
	if (j.find("pillarHeight") != j.end())
		pillarHeight = j["pillarHeight"].get<float>();
	if (j.find("pillarCount") != j.end())
		pillarCount = j["pillarCount"].get<int>();
	if (ImGui::SliderFloat("Temple Size", &size, 40.0f, 100.0f))
		changed = true;
	if (ImGui::SliderFloat("Temple Pillar Radius", &pillarRadius, 0.5f,
			       2.0f))
		changed = true;
	if (ImGui::SliderFloat("Temple Pillar Height", &pillarHeight, 16.0f,
			       60.0f))
		changed = true;
	if (ImGui::SliderInt("Pillar Count", &pillarCount, 1, 200))
		changed = true;
	if (changed) {
		j["size"] = size;
		j["pillarRadius"] = pillarRadius;
		j["pillarHeight"] = pillarHeight;
		j["pillarCount"] = pillarCount;
		StaticGeometryModule::setItemProperties(item.first, j.dump());
		StaticGeometryModule::saveItems();
		StaticGeometryModule::destroyItemGeometry(item.first);
		StaticGeometryModule::createItemGeometry(item.first);
	}
	ImGui::Text("%s", j.dump(4).c_str());
}
}
namespace Geometry
{
void createTemple(flecs::entity e, Ogre::SceneNode *sceneNode,
		  Ogre::StaticGeometry *geo)
{
    ZoneScoped;
    Ogre::String props = e.get<TerrainItem>().properties;
	nlohmann::json jp = nlohmann::json::parse(props);
	float size = 20.0f;
	int pillarCount = 4;
	float pillarRadius = 0.5f, pillarHeight = 10.0f;
	if (jp.find("size") != jp.end())
		size = jp["size"].get<float>();
	if (jp.find("pillarCount") != jp.end())
		pillarCount = jp["pillarCount"].get<int>();
	if (jp.find("pillarHeight") != jp.end())
		pillarHeight = jp["pillarHeight"].get<float>();
	if (jp.find("pillarCount") != jp.end())
		size = jp["size"].get<float>();
	Procedural::TriangleBuffer tb, colliderTb;
	float elevation = 0.0f;
	Ogre::MaterialPtr templeMaterial;
	templeMaterial = Ogre::MaterialManager::getSingleton().getByName(
		"proceduralMaterialTemple" +
		Ogre::StringConverter::toString(e.id()));
	if (!templeMaterial) {
		Procedural::TextureBuffer colorAtlas(1024);
		Procedural::RectangleTexture drawAtlas(&colorAtlas);
		Ogre::ColourValue normalGreen(0.6f, 0.8f, 0.5f, 1);
		drawAtlas.setRectangle(Ogre::RealRect(0.0f, 0.0f, 0.4f, 1.0f))
			.setColour(normalGreen)
			.process();
		Ogre::TexturePtr pierTexture = colorAtlas.createTexture(
			"proceduralTextureTemple" +
			Ogre::StringConverter::toString(e.id()));
		colorAtlas.saveImage("tmp2.png");
		templeMaterial = Ogre::MaterialManager::getSingletonPtr()->create(
			"proceduralMaterialTemple" +
				Ogre::StringConverter::toString(e.id()),
			Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		templeMaterial->getTechnique(0)->getPass(0)->setShininess(0);
		templeMaterial->getTechnique(0)->getPass(0)->setDiffuse(
			Ogre::ColourValue::White);
		templeMaterial->getTechnique(0)->getPass(0)->setSpecular(
			Ogre::ColourValue(1.0f, 1.0f, 0.9f));
		templeMaterial->getTechnique(0)
			->getPass(0)
			->createTextureUnitState(
				"proceduralTextureTemple" +
				Ogre::StringConverter::toString(e.id()));
		if (Ogre::RTShader::ShaderGenerator::initialize()) {
			templeMaterial->prepare();
			Ogre::RTShader::ShaderGenerator *mShaderGenerator =
				Ogre::RTShader::ShaderGenerator::
					getSingletonPtr();
			mShaderGenerator->createShaderBasedTechnique(
				*templeMaterial,
				Ogre::MaterialManager::DEFAULT_SCHEME_NAME,
				Ogre::RTShader::ShaderGenerator::
					DEFAULT_SCHEME_NAME);
			Ogre::RTShader::RenderState *pMainRenderState =
				mShaderGenerator->getRenderState(
					Ogre::RTShader::ShaderGenerator::
						DEFAULT_SCHEME_NAME,
					*templeMaterial);
		}
	}
	Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition();
	Ogre::Quaternion worldOrientation = sceneNode->_getDerivedOrientation();
	Procedural::BoxGenerator()
		.setSizeX(size)
		.setSizeY(20)
		.setSizeZ(size)
		.setEnableNormals(true)
		.setPosition(
			Ogre::Vector3(0.0f, -20.0f / 2.0f + elevation, 0.0f))
		.addToTriangleBuffer(tb);
	float pillarStep = 0.0f;
	float pillarStepSize = size * 4.0f / (float)pillarCount;
	int pcount = pillarCount;
	while (pillarStep < size - pillarRadius * 2.0f && pcount > 0) {
		float s = pillarStep - size / 2.0f + pillarStepSize / 2.0f;
		Procedural::CylinderGenerator()
			.setHeight(pillarHeight)
			.setRadius(pillarRadius)
			.setNumSegBase(8)
			.setPosition(s, 0.0f, -size / 2.0f + pillarRadius)
			.addToTriangleBuffer(tb);
		Procedural::CylinderGenerator()
			.setHeight(pillarHeight)
			.setRadius(pillarRadius)
			.setNumSegBase(8)
			.setPosition(s, 0.0f, size / 2.0f - pillarRadius)
			.addToTriangleBuffer(tb);
		Procedural::CylinderGenerator()
			.setHeight(pillarHeight)
			.setRadius(pillarRadius)
			.setNumSegBase(8)
			.setPosition(-size / 2.0f + pillarRadius, 0.0f, s)
			.addToTriangleBuffer(tb);
		Procedural::CylinderGenerator()
			.setHeight(pillarHeight)
			.setRadius(pillarRadius)
			.setNumSegBase(8)
			.setPosition(size / 2.0f - pillarRadius, 0.0f, s)
			.addToTriangleBuffer(tb);
		pillarStep += pillarStepSize;
		pcount--;
		pcount--;
		pcount--;
		pcount--;
	}
	OgreAssert(tb.getVertices().size() > 8, "bad box");
	for (auto &v : tb.getVertices()) {
#if 0
            float c = 1.0 + 1.0 / (v.mPosition.y + baseHeight +
                           elevation);
            v.mPosition.x *= c;
            v.mPosition.z *= c;
#endif
		v.mUV *= 0.08f;
		v.mUV.x += 0.01f;
		v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.0f, 0.4f);
		v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
	}
	Ogre::String meshName =
		"Temple" + Ogre::StringConverter::toString(e.id());
	Ogre::MeshPtr mesh =
		Ogre::MeshManager::getSingleton().getByName(meshName);
	if (mesh)
		Ogre::MeshManager::getSingleton().remove(mesh);
	mesh = tb.transformToMesh(meshName);
	Ogre::LodConfig config(mesh);
	Geometry::setupLods(config);
	Ogre::Entity *ent = ECS::get<EngineData>().mScnMgr->createEntity(
		"Ent" + meshName, mesh);
	ent->setMaterial(templeMaterial);
	geo->addEntity(ent, worldPosition, worldOrientation,
		       Ogre::Vector3::UNIT_SCALE);
	ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
	// TODO set altar position
	createMeshGeometry("altar.glb", e, sceneNode->createChildSceneNode(),
			   geo);
	geo->build();
	JPH::ShapeRefC shape =
		JoltPhysicsWrapper::getSingleton().createMeshShape(mesh);
	JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody(
		shape.GetPtr(), 0, sceneNode, JPH::EMotionType::Static,
		Layers::NON_MOVING);
	e.set<JPH::BodyID>(id);
	JoltPhysicsWrapper::getSingleton().addBody(id,
						   JPH::EActivation::Activate);
}

}
}
