#include "material.h"
#include "m3derror.h"
#include "model.h"
#include "materiallayer.h"
#include "texture.h"
#include "openglstate.h"
#include "m3dml.h"
#include "texturelayer.h"
#include "colorlayer.h"
#include "floatlayer.h"
#include "shaderprogramm.h"
#include "engineinterface.h"

namespace M3D
{

Material::Material() :
		_source_file_ ( "" ),
		_shader_path_ ( "" ),
		_shader_ ( 0 ),
		_shader_id_( 0, 0 ),
		_layer_count_ ( 0 )
{}


Material::~Material()
{}

void Material::unlink_model ( Model *obj )
{
	_models_.erase ( obj );
}
void Material::link_model ( Model * obj )
{
	_models_.insert ( obj );
}

std::set< Model * > * Material::get_associated_models()
{
	return & _models_;
}
void Material::set_material( OpenGLState *state )
{
	if(_shader_)state->use_program( _shader_->get_shader_id() );
	else THROW( "Material: " + _source_file_ + ": Shader \"" + _shader_path_ + "\" is corrupted!" );
	std::map<uint16_t, MaterialLayer*>::iterator it;
	for ( it = _material_layers_.begin(); it != _material_layers_.end(); ++it ) it->second->set_layer( state );
}
void Material::load( const std::string &base_dir, const std::string &fname, EngineInterface *engine )
{
	_source_file_ = base_dir + fname;
	M3DML material;
	material.load( _source_file_ );
	_shader_path_ = material.get_shader_path();
	_layer_count_ = material.get_header()->layers_number;

	std::map<M3DML_BLOCK_TYPE, M3DML_Layer>::const_iterator begin = material.data_begin();
	std::map<M3DML_BLOCK_TYPE, M3DML_Layer>::const_iterator end = material.data_end();
	std::map<M3DML_BLOCK_TYPE, M3DML_Layer>::const_iterator it;

	for ( it = begin; it != end; ++it )
	{
		if ( it->first == M3DML_NOBLOCK ) continue;
		MaterialLayer *layer;
		if ( it->second.data_block->block_data_type == M3DML_TEXTURE ) layer = new TextureLayer;
		else if ( it->second.data_block->block_data_type == M3DML_COLOR ) layer = new ColorLayer;
		else if ( it->second.data_block->block_data_type == M3DML_FLOAT ) layer = new FloatLayer;
		else THROW( "Invalid block type" );
		layer->load( base_dir, it->first, &it->second, engine );

		std::map<uint16_t, MaterialLayer*>::iterator mit = _material_layers_.find ( it->first );
		if ( mit != _material_layers_.end() ) _material_layers_.erase( mit );
		_material_layers_.insert( std::make_pair( it->first, layer ) );
	}

	_shader_id_ = engine->create_shader_program();
	_shader_ = engine->_get_shader( _shader_id_ );
	_shader_->load( base_dir + _shader_path_ );
}

}




