#include <string>
#include <iostream>
#include <GL/glew.h>
#include <GL/glfw.h>
#include "me_time.h"
#include "m3dme.h"
#include <stdlib.h>

M3D::M3DME mesh;
GLuint vbo_id;
GLuint ibo_id;
GLuint vao_id;
GLuint vsh, fsh, spr;
const GLchar *vshader_source =
{
    "attribute vec3 a_Vertex;"
    "attribute vec3 a_Normal;"
    "varying vec3 v_Normal;"
    "void main()"
    "{"
        "gl_Position = gl_ModelViewProjectionMatrix * vec4(a_Vertex,1);"
        "v_Normal = a_Normal;"
    "}"
};

const GLchar *fshader_source =
{
    "varying vec3 v_Normal;"
    "void main()"
    "{"
        "gl_FragColor = vec4(1,1,1,1);"
    "}"
};

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 );
	GLenum err = glewInit();
	if ( err != GLEW_OK ) return false;
	glClearColor ( 0, 0, 0, 0 );
	glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glEnableClientState ( GL_VERTEX_ARRAY );
	glEnableClientState ( GL_NORMAL_ARRAY );
	glEnableClientState ( GL_INDEX_ARRAY );
	glEnable( GL_DEPTH_TEST );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_BLEND ); 
	
	glViewport ( 0, 0, ( GLsizei ) ( w ), ( GLsizei ) ( h ) );

	glMatrixMode ( GL_PROJECTION );
	glPushMatrix ();
	glLoadIdentity ();
	gluPerspective ( fov * ( ( float ) h / ( float ) w ) , ( float ) w / ( float ) h, 0.1, 10000 );
	glMatrixMode ( GL_MODELVIEW );
	glPushMatrix ();
	glLoadIdentity ();
//Соберем шейдер
    vsh = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vsh, 1, &vshader_source, NULL);
    glCompileShader(vsh);
    
    fsh = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fsh, 1, &fshader_source, NULL);
    glCompileShader(fsh);
    
    spr = glCreateProgram();
    glAttachShader(spr, vsh);
    glAttachShader(spr, fsh);
    
    glBindAttribLocation(spr, 0, "a_Vertex");
    glBindAttribLocation(spr, 1, "a_Color");
    
    glLinkProgram(spr);
    return 0;
}
void load_res(char *fname)
{
	mesh.load( fname );
	const char *vertex_data_pos = (char*)mesh.get_vertex_data();
	const char *index_data_pos = (char*)mesh.get_index_data();
	
	//Создадим вершинный массив
	glGenBuffers(1, &vbo_id);
	glGenBuffers(1, &ibo_id);
	glBindBuffer( GL_ARRAY_BUFFER, vbo_id );
	glBufferData( GL_ARRAY_BUFFER, mesh.get_header()->vertex_data_size, vertex_data_pos, GL_STATIC_DRAW );

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(M3D::M3DME_Vertex_Data), 0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(M3D::M3DME_Vertex_Data), reinterpret_cast<GLvoid*>(5*sizeof(float)));
	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_id );
	glBufferData( GL_ELEMENT_ARRAY_BUFFER, mesh.get_header()->index_data_size, index_data_pos, GL_STATIC_DRAW );
}

void draw(int range)
{

	Time tm;
	tm.reset();
	int frame_num = 0;
	for(;;)
	{
		glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		glMatrixMode ( GL_MODELVIEW ); //сменим матрицу на всякий случай
		glPushMatrix();
		glLoadIdentity();
		glColor3f(1.0,1.0,1.0);
		gluLookAt ( 0, 0, 0, 0, 0, 1, 0, 1, 0 );
		
		glTranslatef ( 0, 0, range );
		glUseProgram(spr);
		
		glBindBuffer( GL_ARRAY_BUFFER, vbo_id );
		glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(M3D::M3DME_Vertex_Data), 0);
		glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(M3D::M3DME_Vertex_Data), reinterpret_cast<GLvoid*>(5*sizeof(float)));
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo_id );
		
		glDrawElements ( GL_TRIANGLES, mesh.get_header()->index_data_size/sizeof(M3D::M3DME_Index_Data), GL_UNSIGNED_INT, 0 );
		
		glPopMatrix();
		glfwSwapBuffers();
		if(frame_num==10000)
		{
			double dt = tm.get_elapsed_time();
			std::cerr << frame_num << " frames in " << dt << " seconds. FPS:" << double ( frame_num ) / dt << std::endl;
			frame_num = 0;
		}
		frame_num++;
		
		
	}
}

int main(int argc, char *argv[])
{
	if ( argc < 3 )
	{
		std::cerr << "Usage: " << argv[ 0 ] << " <object name> <range>" << std::endl;
		return 1;
	}
	int w, h;
	w = 1280;
	h = 800;
	float fov = 60.0;
	int range = atol(argv[2]);
	init ( w, h, fov );
	load_res(argv[1]);
	draw(range);
	return 0;
}
