#include <iostream>

#include <Ogre.h>
#include <OgreApplicationContext.h>
#include <OgrePageManager.h>
#include "physics.h"
#include "main.h"

class KeyHandler : public OgreBites::InputListener {
	bool keyPressed(const OgreBites::KeyboardEvent &evt) override
	{
		if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
			Ogre::Root::getSingleton().queueEndRendering();
		}
		return true;
	}
	void frameRendered(const Ogre::FrameEvent &evt) override
	{
		JoltPhysicsWrapper::getSingleton().update(
			evt.timeSinceLastFrame);
	}
};

class App : public OgreBites::ApplicationContext {
	Ogre::SceneNode *mCameraNode;
	Ogre::SceneManager *mScnMgr;
	KeyHandler mKeyHandler;

public:
	App();
	virtual ~App();

	void setup();
	void locateResources();
	void initCamera();
	Ogre::SceneManager *getSceneManager()
	{
		return mScnMgr;
	}
	void createContent();
	void createCharacter();
	void setupWorld();
};

App::App()
	: OgreBites::ApplicationContext("JoltPhysics")
{
}
void App::setup()
{
	OgreBites::ApplicationContext::setup();
	Ogre::Root *root = getRoot();
	Ogre::SceneManager *scnMgr = root->createSceneManager();
	mScnMgr = scnMgr;
}
void App::locateResources()
{
	OgreBites::ApplicationContext::locateResources();
}
App::~App()
{
}
void App::initCamera()
{
	// also need to tell where we are
	mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
	mCameraNode->setPosition(0, 2, 3);
	mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT);

	// create the camera
	Ogre::Camera *cam = mScnMgr->createCamera("tps_camera");
	cam->setNearClipDistance(0.1f); // specific to this sample
	cam->setAutoAspectRatio(true);
	mCameraNode->attachObject(cam);

	// and tell it to render into the main window
	getRenderWindow()->addViewport(cam);
}
void App::setupWorld()
{
	addInputListener(&mKeyHandler);
}
void App::createCharacter()
{
	Ogre::Camera *cam = static_cast<Ogre::Camera *>(
		mCameraNode->getAttachedObject("tps_camera"));
}
void App::createContent()
{
	// without light we would just get a black screen
	Ogre::Light *light = mScnMgr->createLight("MainLight");
	Ogre::SceneNode *lightNode =
		mScnMgr->getRootSceneNode()->createChildSceneNode();
	//    lightNode->setPosition(0, 10, 15);
	lightNode->setDirection(
		Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
	lightNode->attachObject(light);
	light->setType(Ogre::Light::LT_DIRECTIONAL);
	light->setDiffuseColour(Ogre::ColourValue::White);
	light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
	mScnMgr->setSkyBox(true, "Skybox", 490);
	Ogre::MeshPtr mesh =
		Ogre::MeshManager::getSingleton().load("world.glb", "General");
	new JoltPhysicsWrapper(mScnMgr, mScnMgr->getRootSceneNode());
	JPH::ShapeRefC shape =
		JoltPhysicsWrapper::getSingleton().createMeshShape(mesh);
	JPH::ObjectLayer layer = Layers::NON_MOVING;
	JPH::ObjectLayer layerMoving = Layers::MOVING;
	JPH::ShapeRefC shapeBox1 =
		JoltPhysicsWrapper::getSingleton().createBoxShape(
			{ 0.5f, 1.7f, 0.5f });
	JPH::ShapeRefC shapeBox2 =
		JoltPhysicsWrapper::getSingleton().createBoxShape(
			{ 0.5f, 1.7f, 0.5f });
	JPH::ShapeRefC shapeBox3 =
		JoltPhysicsWrapper::getSingleton().createBoxShape(
			{ 0.5f, 1.7f, 0.5f });
	JoltPhysics::CompoundShapeBuilder builder;
	builder.addShape(shapeBox1, { -0.1f, 0.0f, 0.0f },
			 Ogre::Quaternion::IDENTITY);
	builder.addShape(shapeBox2, { 0.0f, 0.0f, 0.0f },
			 Ogre::Quaternion::IDENTITY);
	builder.addShape(shapeBox3, { 0.1f, 0.0f, 0.0f },
			 Ogre::Quaternion::IDENTITY);
	JPH::ShapeRefC compound = builder.build();
	auto body = JoltPhysicsWrapper::getSingleton().createBody(
		shape.GetPtr(), 0, Ogre::Vector3::ZERO, Ogre::Quaternion::IDENTITY,
		JPH::EMotionType::Static, layer);
	auto compoundBody = JoltPhysicsWrapper::getSingleton().createBody(
		compound.GetPtr(), 0, Ogre::Vector3(0, 0, -3),
		Ogre::Quaternion::IDENTITY, JPH::EMotionType::Kinematic,
		layerMoving);
	JoltPhysicsWrapper::getSingleton().addBody(
		body, JPH::EActivation::DontActivate);
	JoltPhysicsWrapper::getSingleton().addBody(
		compoundBody, JPH::EActivation::DontActivate);
}
int main(int argc, char *argv[])
{
	App ctx;
	ctx.initApp();
	// get a pointer to the already created root
	Ogre::Root *root = ctx.getRoot();
	Ogre::SceneManager *scnMgr = ctx.getSceneManager();

	// register our scene with the RTSS
	Ogre::RTShader::ShaderGenerator *shadergen =
		Ogre::RTShader::ShaderGenerator::getSingletonPtr();
	shadergen->addSceneManager(scnMgr);
	ctx.setWindowGrab(true);
	ctx.createContent();
	ctx.initCamera();
	ctx.setupWorld();
	ctx.createCharacter();
	// register for input events
	//    KeyHandler keyHandler;
	//    ctx.addInputListener(&keyHandler);

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