#include "model.h"
#include "mesh.h"
#include "texture.h"
#include "material.h"
#include "openglstate.h"
#include "m3dm.h"
#include "m3derror.h"
#include "matrix.h"
#include "structures.h"
#include <math.h>
#include <string.h>
#include <iostream>

namespace M3D
{

Model::Model() :
		_mesh_id_ ( 0, 0 ),
		_mesh_ ( 0 ),
		_source_file_ ( "" ),
		_material_file_ ( "" ),
		_mesh_file_ ( "" ),
		_base_dir_ ( "" ),
		_location_(),
		_rotation_(),
		_scale_(),
		_model_name_ ( "" ),
		_material_interface_ ( 0, 0 ),
		_material_ ( 0 ),
		_is_drawable_ ( false ),
		_last_frame_drawed_ ( 0 )
{
}


Model::~Model()
{}

void Model::set_mesh ( MeshInterface mesh_id, Mesh *mesh )
{
	_mesh_id_ = mesh_id;
	_mesh_ = mesh;
	_mesh_->link_model ( this );
}

void Model::set_model_name ( const std::string &model_name )
{
	_model_name_ = model_name;
}

void Model::set_location ( const Coords & location )
{
	_location_ = location;
}

void Model::set_rotation ( const Rotation & rotation )
{
	_rotation_ = rotation;
}

void Model::set_scale ( const Scale & scale )
{
	_scale_ = scale;
}

void Model::load ( const std::string &fname, EngineInterface *engine )
{
	if ( !_mesh_ || !_material_ ) THROW ( "Trying to load uninitiated model!" );
	std::cout<<"Loading model from file: "<<fname<<std::endl;
	_source_file_ = fname;
	M3DM model;
	model.load ( fname );
	_model_name_ = model.get_data()->model_name;
	_base_dir_ = model.get_data()->base_path;
	_material_file_ = model.get_data()->material_name;
	_mesh_file_ = model.get_data()->mesh_name;
	_material_->load ( _base_dir_, _material_file_, engine );
	_mesh_->load ( _material_->get_shader(), _base_dir_, _mesh_file_ );
}
Material *Model::get_material()
{
	return _material_;
}
void Model::set_drawable ( bool is_drawable )
{
	_is_drawable_ = is_drawable;
}

bool Model::is_drawable() const
{
	return _is_drawable_;
}

MaterialInterface Model::get_material_interface() const
{
	return _material_interface_;
}

void Model::set_material ( MaterialInterface mat_id, Material * material )
{
	_material_ = material;
	_material_interface_ = mat_id;
	_material_->link_model ( this );
}
void Model::draw ( uint32_t frame_num, OpenGLState *gl_state )
{
	if ( !_is_drawable_ ) return;
	if ( _last_frame_drawed_ == frame_num ) return;
	_last_frame_drawed_ = frame_num;

	_model_view_matrix_.identity();
	if ( _rotation_.z_angle != 0.0 )_model_view_matrix_.rotate( _rotation_.z_angle, 0, 0, 1 );
	if ( _rotation_.y_angle != 0.0 )_model_view_matrix_.rotate( _rotation_.y_angle, 0, 1, 0 );
	if ( _rotation_.x_angle != 0.0 )_model_view_matrix_.rotate( _rotation_.x_angle, 1, 0, 0 );
	if ( _scale_.x_scale != 1.0 || _scale_.y_scale != 1.0 || _scale_.z_scale != 1.0 )
		_model_view_matrix_.scale( _scale_.x_scale, _scale_.y_scale, _scale_.z_scale );
	_model_view_matrix_.translate(_location_.x_pos, _location_.y_pos, _location_.z_pos); 
	
	int programID = gl_state->get_current_program();
	GLint location = glGetUniformLocation ( programID, "ModelViewMatrix" );
	if(location != -1)glUniformMatrix4fv ( location, 1, GL_FALSE, _model_view_matrix_.get_matrix4x4() );
	location = glGetUniformLocation ( programID, "NormalMatrix" );
	if(location != -1)glUniformMatrix3fv ( location, 1, GL_FALSE, _model_view_matrix_.get_matrix3x3_rotate() );

	//Отрисуем объекты
	_mesh_->draw ( frame_num, gl_state );
}
}






