#include "m3dml.h"
#include "file.h"
#include "m3derror.h"
#include <string.h>
#include <errno.h>
#include <iostream>

namespace M3D
{

M3DML::M3DML()
{}


M3DML::~M3DML()
{}

void M3DML::load( std::string fname )
{
        _mapped_file_.mmap ( fname.c_str());
        char *mapped_file_pointer = _mapped_file_.get_pointer();

	//Получим заголовок
        _header_ = ( M3DML_Header* ) mapped_file_pointer;
        _shader_path_ = mapped_file_pointer + _header_->shader_path_offset;
        
        char *current_pos = mapped_file_pointer + sizeof(M3DML_Header);
        //Загрузим слои
        _layer_data_.clear();
        for(int a=0;a<_header_->layers_number;++a)
        {
                M3DML_Layer layer;
                layer.data_block = (M3DML_Data_Block*)current_pos;
                current_pos += sizeof(M3DML_Data_Block);
                switch(layer.data_block->block_data_type)
                {
                        case M3DML_TEXTURE:
                        {
                                layer.texture_block = (M3DML_Texture_Block*)(mapped_file_pointer + layer.data_block->block_offset);
                                layer.texture_path = mapped_file_pointer + layer.data_block->block_offset + sizeof(M3DML_Texture_Block);
                                break;
                        }
                        case M3DML_COLOR:
                        {
                                layer.color_block = (M3DML_Color_Block*)(mapped_file_pointer + layer.data_block->block_offset);
                                break;
                        }
                        case M3DML_FLOAT:
                        {
                                layer.float_block = (M3DML_Float_Block*)(mapped_file_pointer + layer.data_block->block_offset);
                                break;
                        }
                        default:
                        {
                                THROW( "Unknown data type." );
                        }
                }
                _layer_data_.insert(std::make_pair((M3DML_BLOCK_TYPE)layer.data_block->block_type, layer));
        }

}
void M3DML::save( std::string fname )
{
	//Перестроим заголовок
	_header_->layers_number = _layer_data_.size();
	int base_offset = sizeof( M3DML_Header ) + sizeof( M3DML_Data_Block ) * _header_->layers_number;
	_header_->shader_path_offset = base_offset;
	base_offset += _header_->shader_path_length;
        _header_->data_offset = base_offset;

	for ( LAYER_MAP::iterator it = _layer_data_.begin(); _layer_data_.end() != it; ++it )
	{
		it->second.data_block->block_offset = base_offset;
		switch ( it->second.data_block->block_data_type )
		{
				case M3DML_TEXTURE:
				{
					base_offset += sizeof( M3DML_Texture_Block );
					base_offset += it->second.texture_block->texture_path_length;
					break;
				}
				case M3DML_COLOR:
				{
					base_offset += sizeof( M3DML_Color_Block );
					break;
				}
				case M3DML_FLOAT:
				{
					base_offset += sizeof( M3DML_Float_Block );
					break;
				}
				default:
				{
					THROW( "Unknown data type." );
				}
		}
	}

	//Запишем заголовок и подзаголовки слоев
	File fdesc ( fname.c_str(), true );
	fdesc.write( _header_, sizeof ( M3DML_Header ) );
	for ( LAYER_MAP::iterator it = _layer_data_.begin(); _layer_data_.end() != it; ++it )
	{
		fdesc.write( it->second.data_block, sizeof( M3DML_Data_Block ));
	}
	//Запишем данные
	fdesc.write( _shader_path_, _header_->shader_path_length );
	
	for ( LAYER_MAP::iterator it = _layer_data_.begin(); _layer_data_.end() != it; ++it )
	{
		switch ( it->second.data_block->block_data_type )
		{
			case M3DML_TEXTURE:
			{
				fdesc.write( it->second.texture_block, sizeof(M3DML_Texture_Block));
				fdesc.write( it->second.texture_path, it->second.texture_block->texture_path_length);
				break;
			}
			case M3DML_COLOR:
			{
				fdesc.write(it->second.color_block, sizeof(M3DML_Color_Block));
				break;
			}
			case M3DML_FLOAT:
			{
				fdesc.write( it->second.float_block, sizeof(M3DML_Float_Block));
				break;
			}
			default:
			{
				THROW( "Unknown data type." );
			}
		}
	}


}

void M3DML::set_layer_data( M3DML_BLOCK_TYPE layer, const M3DML_Layer & layer_data )
{
	LAYER_MAP::iterator layer_iterator = _layer_data_.find ( layer );
	//Если нет такого уровня то вставим его. Если есть то заменим
	if ( _layer_data_.end() == layer_iterator )
		_layer_data_.insert ( std::make_pair ( layer, layer_data ) );
	else
		layer_iterator->second = layer_data;
}

const M3DML_Layer* M3DML::get_layer_data( M3DML_BLOCK_TYPE layer ) const
{
	LAYER_MAP::const_iterator layer_iterator = _layer_data_.find ( layer );
	if ( _layer_data_.end() == layer_iterator ) return 0;
	return & ( layer_iterator->second );
}

void M3DML::set_header_( M3DML_Header * value )
{
	_header_ = value;
	memcpy( _header_->signature, M3DML_SIGNATURE, strlen( M3DML_SIGNATURE ) );
	_header_->version = M3DML_VER_MAGIC;
}

}
