#include <iostream>

#include <Ogre.h>
#include <OgreApplicationContext.h>
#include <OgreOverlaySystem.h>
#include <OgreOverlayManager.h>
#include <OgreImGuiOverlay.h>
#include <OgreImGuiInputListener.h>
#include <OgreTrays.h>
#include <OgreTimer.h>
#include <OgreMeshLodGenerator.h>
#include <OgreTerrain.h>

#include "water/water.h"
class App;
class SkyRenderer : public Ogre::SceneManager::Listener {
protected:
	Ogre::SceneManager *mSceneManager;
	virtual void _updateRenderQueue(Ogre::RenderQueue *queue) = 0;

public:
	enum BoxPlane {
		BP_FRONT = 0,
		BP_BACK = 1,
		BP_LEFT = 2,
		BP_RIGHT = 3,
		BP_UP = 4,
		BP_DOWN = 5
	};

	SkyRenderer(Ogre::SceneManager *owner)
		: mSceneManager(owner)
		, mSceneNode(0)
		, mEnabled(false)
	{
	}

	virtual ~SkyRenderer()
	{
		setEnabled(false);
		if (mSceneNode)
			mSceneManager->destroySceneNode(mSceneNode);
	}

	Ogre::SceneNode *mSceneNode;
	bool mEnabled;

	void setEnabled(bool enable)
	{
		if (enable == mEnabled)
			return;
		mEnabled = enable;
		enable ? mSceneManager->addListener(this) :
			 mSceneManager->removeListener(this);
	}
	void
	postFindVisibleObjects(Ogre::SceneManager *source,
			       Ogre::SceneManager::IlluminationRenderStage irs,
			       Ogre::Viewport *vp) override
	{
		// Queue skies, if viewport seems it
		if (!vp->getSkiesEnabled() ||
		    irs == Ogre::SceneManager::IRS_RENDER_TO_TEXTURE)
			return;

		if (!mEnabled || !mSceneNode)
			return;

		// Update nodes
		// Translate the box by the camera position (constant distance)
		mSceneNode->setPosition(vp->getCamera()->getDerivedPosition());
		_updateRenderQueue(source->getRenderQueue());
	}
};
class SkyBoxRenderer : public SkyRenderer {
	std::unique_ptr<Ogre::ManualObject> mSkyBoxObj;

	Ogre::Quaternion mSkyBoxOrientation;
	void _updateRenderQueue(Ogre::RenderQueue *queue) override
	{
		if (mSkyBoxObj->isVisible()) {
			mSkyBoxObj->_updateRenderQueue(queue);
		}
	}

public:
	SkyBoxRenderer(Ogre::SceneManager *owner)
		: SkyRenderer(owner)
	{
	}
	Ogre::SceneManager::SkyBoxGenParameters mSkyBoxGenParameters;
	void create(const Ogre::String &materialName, Ogre::Real distance,
		    uint8_t renderQueue, const Ogre::Quaternion &orientation,
		    const Ogre::String &groupName)
	{
		Ogre::MaterialPtr m =
			Ogre::MaterialManager::getSingleton().getByName(
				materialName, groupName);
		OgreAssert(m, "Sky box material '" + materialName +
				      "' not found.");
		// Ensure loaded
		m->load();

		bool valid = m->getBestTechnique() &&
			     m->getBestTechnique()->getNumPasses();
#if 0
		if (valid) {
			Pass *pass = m->getBestTechnique()->getPass(0);
			valid = valid && pass->getNumTextureUnitStates() &&
				pass->getTextureUnitState(0)->getTextureType() ==
					TEX_TYPE_CUBE_MAP;
		}

		if (!valid) {
			LogManager::getSingleton().logWarning(
				"skybox material " + materialName +
				" is not supported, defaulting");
			m = MaterialManager::getSingleton().getDefaultSettings();
		}
#endif
		OgreAssert(valid, "Bad material" + materialName);

		// Create node
		mSceneNode = mSceneManager->createSceneNode();

		// Create object
		mSkyBoxObj = std::make_unique<Ogre::ManualObject>("SkyBox");
		mSkyBoxObj->setCastShadows(false);
		mSceneNode->attachObject(mSkyBoxObj.get());

		mSkyBoxObj->setRenderQueueGroup(renderQueue);
		mSkyBoxObj->begin(materialName,
				  Ogre::RenderOperation::OT_TRIANGLE_STRIP,
				  groupName);

		// rendering cube, only using 14 vertices
		const Ogre::Vector3 cube_strip[14] = {
			{ -1.f, 1.f, 1.f }, // Front-top-left
			{ 1.f, 1.f, 1.f }, // Front-top-right
			{ -1.f, -1.f, 1.f }, // Front-bottom-left
			{ 1.f, -1.f, 1.f }, // Front-bottom-right
			{ 1.f, -1.f, -1.f }, // Back-bottom-right
			{ 1.f, 1.f, 1.f }, // Front-top-right
			{ 1.f, 1.f, -1.f }, // Back-top-right
			{ -1.f, 1.f, 1.f }, // Front-top-left
			{ -1.f, 1.f, -1.f }, // Back-top-left
			{ -1.f, -1.f, 1.f }, // Front-bottom-left
			{ -1.f, -1.f, -1.f }, // Back-bottom-left
			{ 1.f, -1.f, -1.f }, // Back-bottom-right
			{ -1.f, 1.f, -1.f }, // Back-top-left
			{ 1.f, 1.f, -1.f } // Back-top-right
		};

		for (const auto &vtx : cube_strip) {
			mSkyBoxObj->position(orientation * (vtx * distance));
			// Note UVs mirrored front/back
			mSkyBoxObj->textureCoord(vtx.normalisedCopy() *
						 Ogre::Vector3(1, 1, -1));
		}

		mSkyBoxObj->end();
		mSkyBoxGenParameters.skyBoxDistance = distance;
	}
};

class EditUI : public Ogre::RenderTargetListener {
	App *m_app;
	Ogre::SceneManager *mScnMgr;
	Ogre::ImGuiOverlay *mGuiOverlay;
	OgreBites::ImGuiInputListener *mGuiListener;
	void
	preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override
	{
		preview(evt);
	}

public:
	EditUI(App *app)
		: Ogre::RenderTargetListener()
		, m_app(app)
	{
	}

	void position_editor(Ogre::SceneNode *node)
	{
		Ogre::Vector3 position = node->getPosition();
		float v[3] = { position.x, position.y, position.z };
		ImGui::InputFloat3("position", v);
		position.x = v[0];
		position.y = v[1];
		position.z = v[2];
		node->setPosition(position);
	}
	void orientation_editor(Ogre::SceneNode *node)
	{
		Ogre::Quaternion q = node->getOrientation();
		float yaw = Ogre::Radian(q.getYaw()).valueDegrees();
		float pitch = Ogre::Radian(q.getPitch()).valueDegrees();
		float roll = Ogre::Radian(q.getRoll()).valueDegrees();
		bool m1 = ImGui::InputFloat("yaw", &yaw);
		bool m2 = ImGui::InputFloat("pitch", &pitch);
		bool m3 = ImGui::InputFloat("roll", &roll);
		if (m1 || m2 || m3) {
			Ogre::Quaternion q1(Ogre::Radian(Ogre::Degree(yaw)),
					    Ogre::Vector3::UNIT_Y);
			Ogre::Quaternion q2(Ogre::Degree(pitch),
					    Ogre::Vector3::UNIT_X);
			Ogre::Quaternion q3(Ogre::Degree(roll),
					    Ogre::Vector3::UNIT_Z);
			node->setOrientation(q1 * q2 * q3);
		}
	}
	void attachments_editor(Ogre::SceneNode *node)
	{
		const Ogre::SceneNode::ObjectMap &pmap =
			node->getAttachedObjects();
		int i;
		for (i = 0; i < pmap.size(); i++) {
			const Ogre::MovableObject *mobj = pmap[i];
			const Ogre::String &pname = mobj->getName();
			ImGui::Text("Name: %s", pname.c_str());
		}
	}
	std::vector<Ogre::String> glb_names;
	void init_glb_list()
	{
		int i;

		const std::vector<Ogre::String> &groups =
			Ogre::ResourceGroupManager::getSingleton()
				.getResourceGroups();
		for (i = 0; i < groups.size(); i++) {
			std::vector<Ogre::String> names =
				*Ogre::ResourceGroupManager::getSingleton()
					 .findResourceNames(groups[i], "*.glb");
			glb_names.insert(glb_names.end(), names.begin(),
					 names.end());
		}
	}
	void buildings_editor();
	void buttons_panel();
	void preview(const Ogre::RenderTargetViewportEvent &evt);
	float panel_width;
	void initGui();
};
class App : public OgreBites::ApplicationContext {
	Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
	Ogre::Camera *mCamera;
	Ogre::Real mPivotPitch;
	Ogre::SceneManager *mScnMgr;
	Ogre::Viewport *mViewport;
	EditUI m_edit_ui;
	Ogre::Light *mSun;
	SkyBoxRenderer *sky;
	Water m_water;
	//	OgreBites::TrayManager *mTrayMgr;
	class KeyboardListener : public OgreBites::InputListener,
				 public Ogre::FrameListener {
		App *mApp;

	public:
		Ogre::Vector3 motion;
		bool gui_active;
		Ogre::Timer fps_timer;
		bool fast;
		KeyboardListener(App *app)
			: OgreBites::InputListener()
			, Ogre::FrameListener()
			, mApp(app)
			, gui_active(false)
			, fast(false)
		{
		}
		bool keyPressed(const OgreBites::KeyboardEvent &evt) override
		{
			if (gui_active)
				return false;
			if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
				gui_active = true;
				// std::cout << "Escape!\n";
				// Ogre::Root::getSingleton().queueEndRendering();
				mApp->setWindowGrab(false);
			}
			if (evt.keysym.sym == OgreBites::SDLK_SPACE)
				mApp->dump_water();
			if (evt.keysym.sym == 'w')
				motion.z = -1.0f;
			if (evt.keysym.sym == 's')
				motion.z = 1.0f;
			if (evt.keysym.sym == 'a')
				motion.x = -1.0f;
			if (evt.keysym.sym == 'd')
				motion.x = 1.0f;
			if (evt.keysym.sym == OgreBites::SDLK_LSHIFT)
				fast = true;
			// std::cout << "motion: " << motion << "\n";
			return true;
		}
		bool keyReleased(const OgreBites::KeyboardEvent &evt) override
		{
			if (gui_active)
				return false;
			if (evt.keysym.sym == 'w' && motion.z < 0.0f)
				motion.z = 0.0f;
			if (evt.keysym.sym == 's' && motion.z > 0.0f)
				motion.z = 0.0f;
			if (evt.keysym.sym == 'a' && motion.x < 0.0f)
				motion.x = 0.0f;
			if (evt.keysym.sym == 'd' && motion.x > 0.0f)
				motion.x = 0.0f;
			if (evt.keysym.sym == OgreBites::SDLK_LSHIFT)
				fast = false;
			return true;
		}
		bool mouseMoved(const OgreBites::MouseMotionEvent &evt)
		{
			if (gui_active)
				return false;
			// update camera goal based on mouse movement
			mApp->updateCameraGoal(-0.18f * evt.xrel,
					       -0.12f * evt.yrel, 0);
			return true;
		}
		bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt)
		{
			if (gui_active)
				return false;
			// update camera goal based on mouse movement
			mApp->updateCameraGoal(0, 0, -0.15f * evt.y);
			return true;
		}
		void update(float delta)
		{
			return;
			//			float fade = (1.0f - delta) * 0.98f;
			//			motion *= fade;
		}
		void frameRendered(const Ogre::FrameEvent &evt) override
		{
			if (fps_timer.getMilliseconds() > 1000.0f) {
				std::cout << "FPS: "
					  << mApp->getRenderWindow()
						     ->getStatistics()
						     .lastFPS
					  << " ";
				std::cout << "Draw calls: "
					  << mApp->getRenderWindow()
						     ->getStatistics()
						     .batchCount
					  << " ";
				fps_timer.reset();
				std::cout << "Drops: "
					  << mApp->getRenderWindow()
						     ->getStatistics()
						     .vBlankMissCount
					  << "\n";
				fps_timer.reset();
			}
			update(evt.timeSinceLastFrame);
			if (!gui_active) {
				mApp->updateMotion(evt.timeSinceLastFrame);
				mApp->updateCamera(evt.timeSinceLastFrame);
				mApp->updateSun(evt.timeSinceLastFrame);
				mApp->updateTerrain(evt.timeSinceLastFrame);
				mApp->updateWater(evt.timeSinceLastFrame);
			}
		}
	};
	KeyboardListener mKbd;

public:
	App()
		: OgreBites::ApplicationContext("ChoroEditor")
		, mKbd(this)
		, m_edit_ui(this)
	{
	}
	virtual ~App()
	{
	}
	void setup()
	{
		OgreBites::ApplicationContext::setup();
		Ogre::Root *root = getRoot();
		Ogre::SceneManager *scnMgr = root->createSceneManager();
		mScnMgr = scnMgr;
		Ogre::OverlaySystem *pOverlaySystem = getOverlaySystem();
		mScnMgr->addRenderQueueListener(pOverlaySystem);
		//		mTrayMgr = new OgreBites::TrayManager("AppTrays",
		//						      getRenderWindow());
	}
	void locateResources() override
	{
		OgreBites::ApplicationContext::locateResources();
	}
	void loadResources() override
	{
	}
	void dump_water()
	{
		m_water.dump_textures();
	}

	void initCamera()
	{
		mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
			"CameraNode");
		mCameraNode->setPosition(0, 2, 3);
		mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
				    Ogre::Node::TS_PARENT);

		// create the camera
		mCamera = mScnMgr->createCamera("fps_camera");
		mCamera->setNearClipDistance(0.05f);
		mCamera->setAutoAspectRatio(true);
		mCameraNode->attachObject(mCamera);

		// and tell it to render into the main window
		mViewport = getRenderWindow()->addViewport(mCamera);
		mCameraPivot =
			mScnMgr->getRootSceneNode()->createChildSceneNode(
				"FPSCameraPivot");
		mCameraGoal = mCameraPivot->createChildSceneNode(
			"FPSCameraGoal", Ogre::Vector3(0, 2, 3));
		mCameraNode->setPosition(mCameraPivot->getPosition() +
					 mCameraGoal->getPosition());
		mCameraPivot->setFixedYawAxis(true);
		mCameraGoal->setFixedYawAxis(true);
		mCameraNode->setFixedYawAxis(true);
		// our model is quite small, so reduce the clipping planes
		mCamera->setNearClipDistance(0.1f);
		mCamera->setFarClipDistance(800);
		mPivotPitch = 0;
	}
	void configure()
	{
		std::cout << "Startup" << "\n";
		initApp();
		std::cout << "Set up RTSS" << "\n";
		Ogre::Root *root = getRoot();
		Ogre::SceneManager *scnMgr = getSceneManager();

		// register our scene with the RTSS
		Ogre::RTShader::ShaderGenerator *shadergen =
			Ogre::RTShader::ShaderGenerator::getSingletonPtr();
		shadergen->addSceneManager(scnMgr);
		setWindowGrab(true);
		std::cout << "Init camera" << "\n";
		initCamera();
		std::cout << "Set up water" << "\n";
#if 0
		m_water.createWater(getRenderWindow(), mCamera,
				    mDynWorld.get());
#endif
		std::cout << "Set up cursor" << "\n";
		Ogre::ResourceGroupManager::getSingleton()
			.initialiseAllResourceGroups();
		// OgreBites::ApplicationContext::loadResources();
		setupCursor();
		std::cout << "Create content" << "\n";
		createContent();
		std::cout << "Setup terrain" << "\n";
		m_water.init();
	}
	void setupCursor()
	{
		//		mKeyDirection = Ogre::Vector3::ZERO;
		//		mVerticalVelocity = 0;
		Ogre::ManualObject *mobj =
			mScnMgr->createManualObject("cursorGen");

#if 0
		Ogre::MaterialPtr mat =
			Ogre::static_pointer_cast<Ogre::Material>(
				Ogre::MaterialManager::getSingleton()
					.createOrRetrieve("Debug/Red2",
							  "General")
					.first);
		mat->removeAllTechniques();
		Ogre::Technique *tech = mat->createTechnique();
		tech->setDiffuse(Ogre::ColourValue(1, 0, 0, 1));
		tech->setName("Debug/S");
		tech->setDepthCheckEnabled(false);
		tech->setLightingEnabled(false);
		mobj->begin(mat, Ogre::RenderOperation::OT_LINE_LIST);
#endif
		mobj->begin("Debug/Red", Ogre::RenderOperation::OT_LINE_LIST);
		mobj->position(0, 0, 0);
		mobj->colour(Ogre::ColourValue(1, 0, 0));
		mobj->position(0, 2, 0);
		mobj->colour(Ogre::ColourValue(1, 0, 0));
		mobj->position(-1, 1, 0);
		mobj->colour(Ogre::ColourValue(1, 0, 0));
		mobj->position(1, 1, 0);
		mobj->colour(Ogre::ColourValue(1, 0, 0));
		mobj->index(0);
		mobj->index(1);
		mobj->index(2);
		mobj->index(3);
		mobj->end();
		mCameraPivot->attachObject(mobj);
	}
	Ogre::SceneManager *getSceneManager()
	{
		return mScnMgr;
	}
	void updateMotion(float delta)
	{
#if 0
		if (delta == 0.0f)
			return;
		Ogre::Vector3 move(mCameraNode->getOrientation().zAxis() *
				   mKbd.motion.z);
		move += Ogre::Vector3(mCameraNode->getOrientation().xAxis() *
				      mKbd.motion.x);
		move.y = 0.0f;
		float speed = 15.0f;
		if (mKbd.fast)
			speed = 40.0f;
		Ogre::Vector3 opos = mCameraPivot->getPosition();
		Ogre::Vector3 pos =
			mCameraPivot->getPosition() + move * speed * delta;
		float y = m_terrain.get_height(pos);
		if (y < 0)
			y = 0.0f;
		pos.y = y;

		mCameraPivot->translate(pos - opos);
		// mKbd.motion = Ogre::Vector3(0, 0, 0);
		// if (move.squaredLength() > 0)
		// 	std::cout << move << "\n";
#endif
	}
	void updateCamera(Ogre::Real delta)
	{
		if (delta == 0.0f)
			return;
		// place the camera pivot roughly at the character's shoulder
		// mCameraPivot->setPosition(mBodyNode->getPosition() +
		//			  Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
		// move the camera smoothly to the goal
		Ogre::Vector3 goalOffset = mCameraGoal->_getDerivedPosition() -
					   mCameraNode->getPosition();
		float l = goalOffset.squaredLength();
		Ogre::Vector3 v = goalOffset * 10.0 * delta;
		if (v.squaredLength() > l * 0.5f - delta)
			v = goalOffset * delta;
		mCameraNode->translate(v);
		//		mCameraNode->translate(goalOffset);
		// always look at the pivot
		mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(),
				    Ogre::Node::TS_PARENT);
	}
	void updateCameraGoal(Ogre::Real deltaYaw, Ogre::Real deltaPitch,
			      Ogre::Real deltaZoom)
	{
		mCameraPivot->yaw(Ogre::Degree(deltaYaw),
				  Ogre::Node::TS_PARENT);
		if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
		    !(mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) {
			mCameraPivot->pitch(Ogre::Degree(deltaPitch),
					    Ogre::Node::TS_LOCAL);
			mPivotPitch += deltaPitch;
		}
		Ogre::Real dist = mCameraGoal->_getDerivedPosition().distance(
			mCameraPivot->_getDerivedPosition());
		Ogre::Real distChange = deltaZoom * dist * 0.5f;
		if (distChange > 0 && dist + distChange > 30)
			distChange *= 0.3f;
		if (distChange < 0 && dist + distChange < 5)
			distChange *= 0.2f;

		// bound the zoom
		if (!(dist + distChange < 3 && distChange < 0) &&
		    !(dist + distChange > 60 && distChange > 0))
			mCameraGoal->translate(0, 0, distChange,
					       Ogre::Node::TS_LOCAL);
		Ogre::Vector3 mh = mCameraGoal->_getDerivedPosition();
#if 0
		float h = m_terrain.get_height(mh);
		if (h + 10 > mh.y)
			mCameraGoal->translate(0, 10.0f * deltaZoom, distChange,
					       Ogre::Node::TS_LOCAL);
#endif
	}
	Ogre::SceneNode *mSunGoal;
	Ogre::SceneNode *mSunNode;
	Ogre::SceneNode *mSunTarget;
	Ogre::Timer mSunUpdate;
	void createSun()
	{
		Ogre::Light *light = mScnMgr->createLight("Sun");
		mSunNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
			"SunPivot");
		mSunGoal = mScnMgr->getRootSceneNode()->createChildSceneNode(
			"SunGoal");
		mSunTarget = mSunGoal->createChildSceneNode(
			"SunGoalTarget",
			Ogre::Vector3(100.0f, -400.0f, -400.0f),
			Ogre::Quaternion::IDENTITY);
		// mSunNode->setAutoTracking(true, sun_target);
		//    lightNode->setPosition(0, 10, 15);
		mSunNode->attachObject(light);
		light->setType(Ogre::Light::LT_DIRECTIONAL);
		light->setDiffuseColour(Ogre::ColourValue::White);
		light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
		mSunNode->setDirection(Ogre::Vector3(100.0f, -400.0f, -400.f));
		mSun = light;
	}
	void updateSun(float delta)
	{
		static const float sun_speed = 1.0f;
		float uangle = M_PI * 2.0f / 24.0f / 60.0f;
		mSunNode->pitch(Ogre::Radian(uangle) * sun_speed * delta);
		if (mSunUpdate.getMilliseconds() > 1000) {
			Ogre::TerrainGlobalOptions::getSingleton()
				.setCompositeMapAmbient(
					mScnMgr->getAmbientLight());
			Ogre::TerrainGlobalOptions::getSingleton()
				.setCompositeMapDiffuse(
					mSun->getDiffuseColour());
			Ogre::TerrainGlobalOptions::getSingleton()
				.setLightMapDirection(
					mSun->getDerivedDirection());
			std::cout << "sun pitch: "
				  << mSunNode->getOrientation().getPitch()
				  << "\n";
			mSunUpdate.reset();
		}
		if (mSunNode->getOrientation().getPitch().valueRadians() > 0)
			mScnMgr->setAmbientLight(
				Ogre::ColourValue(0.1f, 0.1f, 0.4f, 1.0f));
		else
			mScnMgr->setAmbientLight(
				Ogre::ColourValue(0.2f, 0.2f, 0.2f, 1.0f));
	}
	Ogre::Timer mTerrainUpd;
	void updateTerrain(float delta)
	{
		// mDbgDraw->update();
#if 0
		if (mTerrainUpd.getMilliseconds() > 1000) {
			m_terrain.create_colliders();
			mTerrainUpd.reset();
		}
#endif
	}
	void updateWater(float delta)
	{
		m_water.updateWater(delta);
	}
	void createContent()
	{
		int i;
		m_edit_ui.init_glb_list();
		m_edit_ui.initGui();
		createSun();
		getRoot()->addFrameListener(&mKbd);
		// mTrayMgr->showCursor();

		//		OgreBites::TrayManager *mTrayMgr = new OgreBites::TrayManager(
		//			"InterfaceName", getRenderWindow());
		// mScnMgr->setSkyBox(true, "Skybox/Dynamic", 490);
		// /* mCamera->getCameraToViewportRay(left, top); */
#if 1
		sky = new SkyBoxRenderer(getSceneManager());
		bool drawFirst = true;
		uint8_t renderQueue = drawFirst ?
					      Ogre::RENDER_QUEUE_SKIES_EARLY :
					      Ogre::RENDER_QUEUE_SKIES_LATE;
		sky->create("Skybox/Dynamic", 450, renderQueue,
			    Ogre::Quaternion::IDENTITY,
			    Ogre::ResourceGroupManager::
				    AUTODETECT_RESOURCE_GROUP_NAME);
		sky->setEnabled(true);

		Ogre::MaterialPtr m =
			Ogre::MaterialManager::getSingleton().getByName(
				"Skybox/Dynamic", "General");
		OgreAssert(m, "Sky box material not found.");
		m->load();
#endif
	}
	void create_entity_node(const Ogre::String &name, int key)
	{
		Ogre::Entity *ent = mScnMgr->createEntity(name);
		Ogre::SceneNode *pnode =
			mScnMgr->getRootSceneNode()->createChildSceneNode(
				"ent:" + name +
					Ogre::StringConverter::toString(key),
				mCameraPivot->getPosition(),
				mCameraPivot->getOrientation());
		pnode->attachObject(ent);
		Ogre::Quaternion q = pnode->getOrientation();
		Ogre::Radian yaw = q.getYaw();
		Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0));
		pnode->setOrientation(nq);
		mKbd.gui_active = false;
		setWindowGrab(true);
	}
	bool get_gui_active()
	{
		return mKbd.gui_active;
	}
	void set_gui_active(bool active)
	{
		mKbd.gui_active = active;
	}
	Ogre::Camera *getCamera()
	{
		return mCamera;
	}
};

void EditUI::buildings_editor()
{
	int i;
	ImVec2 size = ImGui::GetMainViewport()->Size;
	float window_width = size.x * 0.2f;
	if (window_width > panel_width)
		window_width = panel_width;
	float window_height = size.y * 0.5 - 20;
	ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
	ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
				 ImGuiCond_Always);
	ImGui::Begin("Droppings...");
	for (i = 0; i < glb_names.size(); i++) {
		Ogre::String id_button = "Create entity: " + glb_names[i] +
					 "##ent:" + glb_names[i];
		if (ImGui::Button(id_button.c_str())) {
			m_app->create_entity_node(glb_names[i], i);
		}
	}
	ImGui::End();
}
void EditUI::buttons_panel()
{
	ImVec2 size = ImGui::GetMainViewport()->Size;
	float window_width = size.x * 0.2f;
	if (window_width > panel_width)
		window_width = panel_width;
	float window_height = size.y * 0.5f - 20;
	ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20),
				ImGuiCond_Always);
	ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
				 ImGuiCond_Always);
	ImGui::Begin("Dumb and Stupid");
	if (!m_app->get_gui_active())
		m_app->setWindowGrab(true);
	if (ImGui::Button("Shitty Quit button"))
		Ogre::Root::getSingleton().queueEndRendering();
	if (ImGui::Button("Chick-chick")) {
		m_app->set_gui_active(false);
		m_app->setWindowGrab(true);
	}
	ImGui::Text("We do stoopid...");
	ImGui::End();
}
void EditUI::preview(const Ogre::RenderTargetViewportEvent &evt)
{
	int i;
	Ogre::ImGuiOverlay::NewFrame();
	if (m_app->get_gui_active()) {
		buttons_panel();
		buildings_editor();
		ImVec2 size = ImGui::GetMainViewport()->Size;
		float window_width = size.x * 0.2f;
		if (window_width > panel_width)
			window_width = panel_width;
		float window_height = size.y * 0.5f - 20;
		ImGui::SetNextWindowPos(ImVec2(size.x - window_width,
					       size.y * 0.5f + 20),
					ImGuiCond_Always);
		ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
					 ImGuiCond_Always);
		// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
		ImGui::Begin("Panel...");
		std::deque<Ogre::SceneNode *> tree_input_queue,
			tree_output_queue;
		std::vector<Ogre::SceneNode *> tree_list;
		tree_input_queue.push_back(mScnMgr->getRootSceneNode());
		tree_input_queue.push_back(nullptr);
		std::set<Ogre::SceneNode *> visited;
		while (true) {
			int new_nodes_count = 0;
			while (!tree_input_queue.empty()) {
				int child;
				Ogre::SceneNode *item =
					tree_input_queue.front();
				tree_input_queue.pop_front();
				if (item && visited.find(item) ==
						    visited.end()) { // new node
					new_nodes_count++;
					tree_output_queue.push_back(item);
					visited.insert(item);
					const Ogre::Node::ChildNodeMap
						&children = item->getChildren();
					for (child = 0; child < children.size();
					     child++) {
						tree_output_queue.push_back(
							static_cast<Ogre::SceneNode
									    *>(
								children[child]));
						tree_output_queue.push_back(
							nullptr);
					}
				} else
					tree_output_queue.push_back(item);
			}
			if (new_nodes_count == 0)
				break;
			tree_input_queue = tree_output_queue;
			tree_output_queue.clear();
		}
		tree_list.insert(tree_list.begin(), tree_output_queue.begin(),
				 tree_output_queue.end());
		int count = 0;
		int depth = 0;
		std::vector<int> check_depth;
		int max_depth = 0;
		check_depth.push_back(0);
		for (count = 0; count < tree_list.size(); count++) {
			int t;

			Ogre::SceneNode *node = tree_list[count];
			if (node && max_depth >= depth) {
				Ogre::String name = node->getName();
				if (name.length() == 0) {
					name = "Node #" +
					       Ogre::StringConverter::toString(
						       count);
				}
				if (ImGui::TreeNode(name.c_str())) {
					check_depth.push_back(max_depth);
					max_depth++;
					ImGui::Text(
						"%s",
						(name + "##caption").c_str());
					position_editor(node);
					ImGui::Separator();
					orientation_editor(node);
					ImGui::Separator();
					ImGui::Text("Attachments");
					attachments_editor(node);
				}
			} else if (!node && max_depth >= depth) {
				max_depth = check_depth.back();
				check_depth.pop_back();
				ImGui::TreePop();
			}
			if (tree_list[count])
				depth++;
			else
				depth--;
		}
		ImGui::Spacing();
		ImGui::End();
	}
}
void EditUI::initGui()
{
	mScnMgr = m_app->getSceneManager();
	float vpScale = m_app->getDisplayDPI() / 96 *
			(float)m_app->getRenderWindow()->getWidth() / 1280;
	panel_width =
		380.0f * (float)m_app->getRenderWindow()->getWidth() / 1280;
	Ogre::OverlayManager::getSingleton().setPixelRatio(vpScale);
	mGuiOverlay = m_app->initialiseImGui();
	//		float vpScale =
	//			Ogre::OverlayManager::getSingleton().getPixelRatio();
	ImGui::GetIO().FontGlobalScale = std::round(vpScale);
	mGuiOverlay->setZOrder(300);
	mGuiOverlay->show();
	m_app->getRenderWindow()->addListener(this);
}
int main()
{
	App ctx;
	ctx.configure();
	// ctx.runRenderingSettingsDialog();
	// get a pointer to the already created root
	// register for input events
	//    KeyHandler keyHandler;
	//    ctx.addInputListener(&keyHandler);

	ctx.getRoot()->startRendering();
	ctx.setWindowGrab(false);
	ctx.closeApp();
	return 0;
}
