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

namespace M3D
{

	M3DT::M3DT()
	{
	}


	M3DT::~M3DT()
	{
	}

	void M3DT::save ( std::string fname )
	{
		File fdesc ( fname.c_str(), true );

		//Сохраним заголовок
		if ( write ( fdesc, _header_, sizeof ( M3DT_Header) ) != sizeof( M3DT_Header) )
		{
			THROW ( strerror ( errno ) );
		}
		//Вычислим смещение до первого блока данных
		//Оно вычисляет как размер заголовка+размер всех лод заголовков. Кол-во ЛОД
		//заголовков всегда на 1 больше чем указано в хедере
		uint32_t offset = sizeof(M3DT_Header)+sizeof(M3DT_Lod) * (_header_->lod_number+1);

		//Сохраним LOD-заголовки
		std::map<int, M3DT_Lod_Data>::iterator lod_iterator;
		for ( lod_iterator = _lod_data_.begin(); lod_iterator != _lod_data_.end(); ++lod_iterator )
		{
			lod_iterator->second.lod_info->data_offset = offset;
			offset += lod_iterator->second.lod_info->data_size;
			if ( write ( fdesc, lod_iterator->second.lod_info, sizeof ( M3DT_Lod ) ) != sizeof( M3DT_Lod) )
			{
				THROW ( strerror ( errno ) );
			}
		}

		//Сохраним сами LOD-уровни
		for ( lod_iterator = _lod_data_.begin(); lod_iterator != _lod_data_.end(); ++lod_iterator )
		{
			int written;
			if ( (written=write ( fdesc, lod_iterator->second.data_pointer, lod_iterator->second.lod_info->data_size )) != (int32_t)lod_iterator->second.lod_info->data_size)
			{
				THROW ( strerror ( errno ) );
			}
		}
	}

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

		//Получим заголовок
		_header_ = ( M3DT_Header* ) mapped_file_pointer;

		//Перейдем на начало LOD-заголовков
		char *lod_data = mapped_file_pointer + sizeof ( M3DT_Header );
		//Заполним LOD-информацию
		for ( int a=0;a <= _header_->lod_number;a++ )
		{
			M3DT_Lod *lod = ( M3DT_Lod* ) ( lod_data + sizeof(M3DT_Lod) * a );
			char *data_pointer = mapped_file_pointer + lod->data_offset;
			_lod_data_.insert ( std::make_pair ( a, M3DT_Lod_Data ( lod, data_pointer ) ) );
		}
	}


	void M3DT::set_lod_data ( int lod, const M3DT_Lod_Data &lod_data )
	{
		std::map<int, M3DT_Lod_Data>::iterator lod_iterator = _lod_data_.find ( lod );
		//Если нет такого уровня то вставим его. Если есть то заменим
		if ( lod_iterator == _lod_data_.end() )
			_lod_data_.insert ( std::make_pair ( lod, lod_data ) );
		else
			lod_iterator->second = lod_data;
	}

	const M3DT_Lod_Data * M3DT::get_lod_data ( int lod ) const
	{
		std::map<int, M3DT_Lod_Data>::const_iterator lod_iterator = _lod_data_.find ( lod );
		if ( lod_iterator == _lod_data_.end() ) return 0;
		return & ( lod_iterator->second );
	}
        void M3DT::set_header_(M3DT_Header * value)
        {
            _header_ = value;
	    memcpy(_header_->signature, M3DT_SIGNATURE, strlen(M3DT_SIGNATURE));
            _header_->version = M3DT_VER_MAGIC;

        }

}

