#include <string>
#include <iostream>
#include <GL/glew.h>
#include <GL/glfw.h>
#include "engine_structs.h"
#include "engineinterface.h"
#include "structures.h"
#include "magic3d.h"
#include "me_time.h"
#include "m3derror.h"

#define LOG_INFO std::cerr
M3D::EngineInterface *engine;
M3D::EngineLimits limits;

int init ( int w, int h, float fov )
{
	bool FullScreen = false;
	bool _mouse_ = true;

	if ( glfwInit() != GL_TRUE ) return false;
	int ret;
	ret = glfwOpenWindow ( w, h, 8, 8, 8, 8, 32, 0, ( FullScreen ) ? GLFW_FULLSCREEN : GLFW_WINDOW );
	if ( ret != GL_TRUE ) return false;
	std::string _caption_ = "magic3d_test";
	glfwSetWindowTitle ( _caption_.c_str() );
	if ( !_mouse_ ) glfwDisable ( GLFW_MOUSE_CURSOR );
	glfwEnable ( GLFW_KEY_REPEAT );


	limits.camera_count = 10;
	limits.indeces_count = 10000;
	limits.mesh_count = 1000;
	limits.model_count = 1000;
	limits.shader_count = 100;
	limits.texture_count = 1000;
	limits.texture_size = 4096;
	limits.vertices_count = 1024 * 1024;
	limits.material_count = limits.model_count;
	engine = M3D::create_engine ( &limits, w, h, fov );

	return 0;
}

void draw ( char *fname, int w, int h, float fov )
{
//	M3D::ShaderProgramInterface shader = engine->create_shader_program();
//	shader.load ( "res/shaders/simple.shader" );
//	engine->override_shader_program ( shader );

	M3D::ModelInterface obj = engine->create_model();
	obj.load ( fname );
	engine->add_model ( obj );

	M3D::LightInterface light = engine->create_light();
	M3D::Coords light_coords;
	light_coords.x_pos = 10.0;
	light.set_location ( light_coords );
	engine->set_light ( light );

	M3D::CameraInterface camera = engine->create_camera();
	M3D::CameraParams params ( fov* ( ( float ) h / ( float ) w ), 0.1, 10000.0, ( float ) ( ( float ) w / ( float ) h ) );
	engine->switch_camera ( camera );
	M3D::Rotation camera_rotation;
	camera_rotation.y_angle = 180.0;
	M3D::Coords camera_coords;
	camera.set_location ( camera_coords );
	camera.set_rotation ( camera_rotation );
	camera.set_camera_params ( params );

	M3D::Rotation rotation;
	M3D::Coords coords, clone_coords;
	M3D::Scale scale;
	coords.z_pos = 10.0;
	int frame_num = 0;
	Time timer, timer1;
	double dt;
	M3D::ModelInterface clone ( 0, 0 );
	int ocounter = 1;
	while ( !glfwGetKey ( GLFW_KEY_ESC ) && glfwGetWindowParam ( GLFW_OPENED ) )
	{
		dt = timer.get_elapsed_time();
		if ( !glfwGetKey ( GLFW_KEY_LSHIFT ) )
		{
			if ( glfwGetKey ( GLFW_KEY_LEFT ) ) rotation.y_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_RIGHT ) ) rotation.y_angle += 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_UP ) ) rotation.x_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_DOWN ) ) rotation.x_angle += 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_DEL ) ) rotation.z_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_PAGEDOWN ) ) rotation.z_angle += 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_KP_ADD ) ) coords.z_pos -= 1. * dt;
			if ( glfwGetKey ( GLFW_KEY_KP_SUBTRACT ) ) coords.z_pos += 1. * dt;
		}
		else if ( glfwGetKey ( GLFW_KEY_LSHIFT ) )
		{
			if ( glfwGetKey ( GLFW_KEY_LEFT ) ) camera_rotation.y_angle += 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_RIGHT ) ) camera_rotation.y_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_UP ) ) camera_rotation.x_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_DOWN ) ) camera_rotation.x_angle += 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_DEL ) ) camera_rotation.z_angle -= 60. * dt;
			if ( glfwGetKey ( GLFW_KEY_PAGEDOWN ) ) camera_rotation.z_angle += 60. * dt;

			if ( glfwGetKey ( GLFW_KEY_KP_SUBTRACT ) )
			{
				scale.x_scale -= 0.5 * dt;
				scale.y_scale -= 0.5 * dt;
				scale.z_scale -= 0.5 * dt;
			}
			if ( glfwGetKey ( GLFW_KEY_KP_ADD ) )
			{
				scale.x_scale += 0.5 * dt;
				scale.y_scale += 0.5 * dt;
				scale.z_scale += 0.5 * dt;
			}
			camera.set_rotation ( camera_rotation );
		}

		if ( glfwGetKey ( GLFW_KEY_LALT ) )
		{
			if ( glfwGetKey ( 'T' ) ) engine->set_draw_mode ( M3D::DRAW_TEXTURE );
			if ( glfwGetKey ( 'W' ) ) engine->set_draw_mode ( M3D::DRAW_WIREFRAME );
		}
		else
		{
			if ( glfwGetKey ( 'W' ) ) camera_coords.z_pos += 1.*dt;
			if ( glfwGetKey ( 'S' ) ) camera_coords.z_pos -= 1.*dt;
			if ( glfwGetKey ( 'A' ) ) camera_coords.x_pos -= 1.*dt;
			if ( glfwGetKey ( 'D' ) ) camera_coords.x_pos += 1.*dt;
			if ( glfwGetKey ( 'R' ) ) camera_coords.y_pos -= 1.*dt;
			if ( glfwGetKey ( 'F' ) ) camera_coords.y_pos += 1.*dt;
		}
		if ( glfwGetKey ( 'C' ) )
		{
			if ( ocounter < 11 )
			{
				clone = obj.clone();
				clone_coords = coords;
				clone_coords.x_pos += 0.1;
				clone_coords.y_pos += 0.1;
				clone.set_location ( clone_coords );
				clone.set_rotation ( rotation );
				engine->add_model ( clone );
				ocounter++;
			}
		}

		obj.set_location ( coords );
		obj.set_rotation ( rotation );
		obj.set_scale ( scale );
		camera.set_location ( camera_coords );

		engine->draw();

		glfwSwapBuffers();

		if ( frame_num >= 10000 )
		{
			double dt = timer1.get_elapsed_time();
			std::cerr << frame_num << " frames in " << dt << " seconds. FPS:" << float ( frame_num ) / dt << std::endl;
			std::cerr << "Object count:" << ocounter << std::endl;
			frame_num = 0;
		}
		frame_num ++;

	}
}
int main ( int argc, char *argv[] )
{
	try
	{
		if ( argc < 2 )
		{
			std::cerr << "Usage: " << argv[ 0 ] << " <obejct name" << std::endl;
		}
		int w, h;
		w = 1280;
		h = 800;
		float fov = 60.0;
		init ( w, h, fov );
		draw ( argv[1], w, h, fov );
		glfwTerminate();
	}
	catch ( M3DError error )
	{
		std::cerr << "====ERROR====" << std::endl;
		std::cerr << error.get_file() << ":" << error.get_line() << " in function \"" << error.get_function() << "\"" << std::endl;
		std::cerr << error.get_reason() << std::endl;
		std::cerr << "=============" << std::endl;
	}
}
