#include "main_code.h"

#ifdef TARGET_ANDROID
#include "android_api.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#include "include/Engine.h"

#include "main_code.h"

const int CONST_TUBE_COUNT = 6;

const int CONST_MAX_BOXES = 50;

const float CONST_VELOCITY = 2.f;

int countTubes = 0;

TFlexModel GenerateFlexModel(int i)
{
	countTubes++;

	if (countTubes <= 5)
	{
		return ResourceManager->FlexModelManager.InstanciateModel("tube1.xml");
	}
	else
	{
		return ResourceManager->FlexModelManager.InstanciateModel("tube"+tostr(i+1)+".xml");
	}
}

TFlexModel GeneratePhysicsModel(int i)
{
	countTubes++;

	if (countTubes <= 5)
	{
		return ResourceManager->FlexModelManager.InstanciateModel("tube1_physics.xml");
	}
	else
	{
		return ResourceManager->FlexModelManager.InstanciateModel("tube"+tostr(i+1)+"_physics.xml");
	}
}


void TMyApplication::InnerInit()
{
	
	*Console<<"Inner init go!\n";

#ifdef TARGET_ANDROID
    ST::PathToResources = "";
#endif
#ifdef TARGET_WIN32
#ifdef NDEBUG
	ST::PathToResources = "resources/";
#else
	ST::PathToResources = "../../../assets/";
#endif
#endif
#ifdef TARGET_IOS
    ST::PathToResources = "assets/";
#endif

	ResourceManager->TexList.AddTexture(CONST_CONSOLE_TEX_NAME);

	ResourceManager->ShaderManager.AddShader("DefaultShader", "gui_transparent.vertex", "gui_transparent.fragment");
    
    Renderer->PushShader("DefaultShader");

	ResourceManager->FontManager.AddFont("droid_sans14", "droid_sans14_font_bitmap.bmp32", "droid_sans14_font_charmap.txt");
	ResourceManager->FontManager.PushFont("droid_sans14");

	//ResourceManager->SoundManager.LoadMusic("level1ogg.ogg");
	
    //ResourceManager->SoundManager.LoadSound("shot.wav");
	
	//Renderer->MovePhi(pi/6);

	Renderer->Camera = TPitCamera();

	ResourceManager->LightManager.SetLightDirection(vec3(-1,0,-1));
	ResourceManager->LightManager.SetLightColor(vec4(1,0,0,1));

	ResourceManager->TexList.AddTexture("pit.png");
	ResourceManager->TexList.AddTexture("tube.png");
	ResourceManager->TexList.AddTexture("tube_i.png");
	//ResourceManager->FlexModelManager.LoadModelFromXml("model.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("box.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("pit.xml");

	ResourceManager->FlexModelManager.LoadModelFromXml("tube1.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube2.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube3.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube4.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube5.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube6.xml");

	ResourceManager->FlexModelManager.LoadModelFromXml("tube1_physics.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube2_physics.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube3_physics.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube4_physics.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube5_physics.xml");
	ResourceManager->FlexModelManager.LoadModelFromXml("tube6_physics.xml");

	FlexModel = ResourceManager->FlexModelManager.InstanciateModel("pit.xml");

	
	//FlexModel.MoveModel(vec3(0, -36.f, -105.f)); //Landscape screen
	FlexModel.MoveModel(vec3(0, -23.f, -50.f)); //Portrait screen
	FlexModel.ScaleModel(40.f);
	FlexModel.RefreshBuffer();

	TubeModelArr.resize(6);
	TubeBodyArr.resize(6);

	for (size_t i = 0; i < TubeModelArr.size(); ++i)
	{
		int modelNum = rand() % CONST_TUBE_COUNT;
		TubeModelArr[i] = GenerateFlexModel(modelNum);
		
		TubeModelArr[i].PushModelDataTriangleList();
		TubeModelArr[i].ScaleModel(25.4f);
		TubeModelArr[i].MoveModel(vec3(0, 0, -2500.f*i));

		TubeModelArr[i].RefreshBuffer();


		auto model = GeneratePhysicsModel(modelNum);
		TubeBodyArr[i].AssignFromFlexModel(model);
		TubeBodyArr[i].Scale(25.4f);
		TubeBodyArr[i].Move(vec3(0, 0, -2500.f*i));


		
	}
	/*
	for (int i=0; i< CONST_MAX_BOXES; i++)
	{
		BoxList.push_back(GenerateBox(i));
	}*/

	//Uncomment to start music playing
	//ResourceManager->SoundManager.PlayMusic("level1ogg.ogg");
	
	Velocity = CONST_VELOCITY;

	PrevCamVec = boost::get<TPitCamera>(Renderer->Camera).CamVec;

	RotateShift = vec2(0.f, 0.f);

	*Console<<"Inner init end!\n";
}

void TMyApplication::InnerDeinit()
{
}


void TMyApplication::InnerDraw()
{
    
	glClearColor(0,0,0,1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	Renderer->PushPerspectiveProjectionMatrix(pi/6, Renderer->GetMatrixWidth() / Renderer->GetMatrixHeight(), 1.f, 8500.f);

	Renderer->PushMatrix();

    Renderer->SetGLCamView();
	
	for (size_t i = 0; i < TubeModelArr.size(); ++i)
	{
		TubeModelArr[i].Draw();
	}
	
	Renderer->PopMatrix();

	//Renderer->PopProjectionMatrix();

	glClear(GL_DEPTH_BUFFER_BIT);

	Renderer->PushMatrix();

	vec4 q;

	vec3 camVec = boost::get<TPitCamera>(Renderer->Camera).CamVec;

	vec4 camQuat = boost::get<TPitCamera>(Renderer->Camera).CameraQuat;

	vec3 n = Normalize(CrossProduct(camVec, PrevCamVec));

	float cos_a = min(DotProduct(camVec, PrevCamVec), 1.f);

	n = InverseQuat(camQuat) * vec4(n) * (camQuat);

	float sin_a = sqrtf(1 - cos_a*cos_a);

	if (sin_a > 0.001f)
	{

		q = vec4(n.v[0] * sin_a, n.v[1] * sin_a, n.v[2] * sin_a, cos_a);
		
		Renderer->RotateMatrix(q);
	}
	FlexModel.Draw();

	Renderer->PopMatrix();

	Renderer->PopProjectionMatrix();

	
}


void TMyApplication::InnerUpdate(cardinal dt)
{
	vec3 camShift = boost::get<TPitCamera>(Renderer->Camera).CamShift;

	vec3 camVec = boost::get<TPitCamera>(Renderer->Camera).CamVec;

	camShift += camVec * (static_cast<float>(dt)*Velocity);
	
	while (camShift.v[2] < -2500.f)
	{
		camShift.v[2] += 2500.f;

		TubeModelArr.erase(TubeModelArr.begin());
		TFlexModel model;
		model = GenerateFlexModel(rand() % CONST_TUBE_COUNT);
		model.ScaleModel(25.4f);
		TubeModelArr.push_back(model);

		for (size_t i = 0; i < TubeModelArr.size()-1; ++i)
		{
			TubeModelArr[i].MoveModel(vec3(0, 0, 2500.f));
			TubeModelArr[i].RefreshBuffer();
		}

		TubeModelArr[TubeModelArr.size()-1].MoveModel(vec3(0, 0, -2500.f*(TubeModelArr.size()-1)));
		TubeModelArr[TubeModelArr.size()-1].RefreshBuffer();



		TubeBodyArr.erase(TubeBodyArr.begin());
		TVolumeBody body;
		auto model1=GeneratePhysicsModel(rand() % CONST_TUBE_COUNT);
		body.AssignFromFlexModel(model1);
		body.Scale(25.4f);
		TubeBodyArr.push_back(body);
		
		for (size_t i = 0; i < TubeBodyArr.size()-1; ++i)
		{
			TubeBodyArr[i].Move(vec3(0, 0, 2500.f));
		}

		TubeBodyArr[TubeBodyArr.size()-1].Move(vec3(0, 0, -2500.f*(TubeBodyArr.size()-1)));
	
	}

	boost::get<TPitCamera>(Renderer->Camera).CamShift = camShift;

	if (Velocity != 0)
	{

		vec3 a = boost::get<TPitCamera>(Renderer->Camera).CamShift;
		vec3 b = a + 100.f * boost::get<TPitCamera>(Renderer->Camera).CamVec;

		if (a != b)
		{
			BOOST_FOREACH(TVolumeBody& body, TubeBodyArr)
			{
				if (body.CheckCollision(a,b))
				{
					Velocity = 0.f;
				}
			}
			/*
			BOOST_FOREACH(auto& i, BoxList)
			{
				if (i.second.CheckCollision(a,b))
				{
					Velocity = 0.f;
				}
			}*/
		}
	}


	//const float CONST_ROTATE_SPEED = 0.0004f;
	const float CONST_ROTATE_SPEED = 0.001f;

	float rotateShift = Length(RotateShift) * 0.01f / 2.f;

	vec4 quat = vec4(RotateAxis.v[0] * sin(rotateShift*dt*CONST_ROTATE_SPEED), RotateAxis.v[1] * sin(rotateShift*dt*CONST_ROTATE_SPEED), RotateAxis.v[2] * sin(rotateShift*dt*CONST_ROTATE_SPEED), cos(rotateShift*dt*CONST_ROTATE_SPEED));

	boost::get<TPitCamera>(Renderer->Camera).RotateByQuat(quat);

	boost::get<TPitCamera>(Renderer->Camera).CalcCamVec();

	UpdatePitVector(dt);


	OnUpdateSignal();

	OnUpdateSignal.disconnect_all_slots();

}


void TMyApplication::InnerOnMove(vec2 shift)
{
	
	RotateShift += shift;

	//float len = Length(shift);

	vec3 rotation = Normalize(vec3(RotateShift, 0)); //Inverse

	//rotation.v[1] =-rotation.v[1]; //Inverse

	vec3 z = vec3(0,0,-1);

	vec4 startQuat = boost::get<TPitCamera>(Renderer->Camera).CameraQuat;

	RotateAxis = Normalize(CrossProduct(rotation, z));

	RotateAxis = startQuat * vec4(RotateAxis) * InverseQuat(startQuat);
	
	//RotateShift = len * 0.01f / 2.f;

	UpdatePitVector(0);
}

void TMyApplication::InnerOnTapUp(vec2 p)
{
	RotateShift = vec2(0.f, 0.f);

	RotateAxis = vec3(0,0,0);

	if (p.v[0] < 50 && p.v[1] < 50 && Velocity == 0)
	{
		OnUpdateSignal.connect(boost::bind(&TMyApplication::RecreateLevel, this));
	}
}

void TMyApplication::InnerOnTapUpAfterMove(vec2 p)
{
	RotateShift = vec2(0.f, 0.f);

	RotateAxis = vec3(0,0,0);
}

void TMyApplication::OnMouseWheel(short int delta)
{
	OnUpdateSignal.connect(boost::bind(&TMyApplication::RecreateLevel, this));
}

void TMyApplication::UpdatePitVector(cardinal dt)
{
	
	//Update pit vector
	vec3 camVec = boost::get<TPitCamera>(Renderer->Camera).CamVec;

	vec4 q;

	vec3 n = -Normalize(CrossProduct(camVec, PrevCamVec));

	float min_cos_a = min(DotProduct(camVec, PrevCamVec), 1.f);

	const float diff_alpha = pi/96;
	
	float angle = acos(min_cos_a);

	
	if (angle >= diff_alpha)
	{
		//Correcting PrevCamVec;

		min_cos_a = cosf(diff_alpha);

		vec4 quat(-n.v[0]*sin(diff_alpha/2.f), -n.v[1]*sin(diff_alpha/2.f), -n.v[2]*sin(diff_alpha/2.f), cos(diff_alpha/2.f));

		PrevCamVec = quat * vec4(camVec) * InverseQuat(quat);
	}


	//Catch up

	const float omega = 0.0002f;

	if (angle < omega*dt)
	{
		PrevCamVec = camVec;
	}
	else
	{
		vec4 delta_quat(n.v[0]*sin(omega * dt / 2.f), n.v[1]*sin(omega * dt / 2.f), n.v[2]*sin(omega * dt / 2.f), cos(omega * dt / 2.f));

		PrevCamVec = delta_quat * vec4(PrevCamVec) * InverseQuat(delta_quat);
	}


	
}

void TMyApplication::RecreateLevel()
{
	Velocity = CONST_VELOCITY;
	
	boost::get<TPitCamera>(Renderer->Camera).CamShift = ZeroVec3;

	for (size_t i = 0; i < 3; ++i)
	{
		TubeModelArr[i] = GenerateFlexModel(0);

		TubeModelArr[i].PushModelDataTriangleList();
		TubeModelArr[i].ScaleModel(25.4f);
		TubeModelArr[i].MoveModel(vec3(0, 0, -2500.f*i));

		TubeModelArr[i].RefreshBuffer();

		auto model2= GeneratePhysicsModel(0);
		TubeBodyArr[i].AssignFromFlexModel(model2);
		TubeBodyArr[i].Scale(25.4f);
		TubeBodyArr[i].Move(vec3(0, 0, -2500.f*i));

	}
}