/**********************************************************************//**
*	Implementace tridy souboru spritu.
*	Trida umoznuje vytvoreni souboru spritu na zaklade dodanych parametru.
*	Druho funkci ne nacteni informaci ze souboru. Na zaklade techto informaci
*	je mozne ze souboru extrahovat jednotlive sninky video sekvence.
*
*
*	Date: 22.12.2008
*	Author: Michal Jirous
*	File: spritefile.cpp
***************************************************************************/

#include "spritefile.h"
#include <fstream>


using namespace std;


SpriteFile::SpriteFile()
{
	clearDatabase();
}

void SpriteFile::clearDatabase()
{
	m_Parameters.clear();
	m_Files.clear();
}

/** @param name Jmeno parametru.
*	@param value Hodnota parametru.
*/
void SpriteFile::addParameter(std::string name, std::string value)
{
	m_Parameters.push_back( Parameter() );
	m_Parameters.back().name = name;
	m_Parameters.back().value = value;
}

/** @param name Jmeno souboru.
*	@param offset Offset k datum v souboru.
*	@param size Celkovy pocet bytu dat v souboru.
*/
void SpriteFile::addFile(std::string name, size_t offset, size_t size)
{
	m_Files.push_back( SingleFile() );
	m_Files.back().filename = name;
	m_Files.back().offset = offset;
	m_Files.back().size = size;
}

/** @param filename Cesta k souboru.
*   @return Pouze nazev souboru.
*/
std::string SpriteFile::skipDirectories( std::string filename )
{
	size_t pos = filename.find_last_of("/\\");
	return filename.substr( pos+1 );
}

size_t SpriteFile::getFileSize( const char *filename )
{
	ifstream fin( filename );
	if( !fin.is_open() )
		return 0;

	fin.seekg( 0,ios::end );
	size_t size = fin.tellg();

	fin.close();
	return size;
}

void SpriteFile::writeString( std::string str, std::ofstream &fout )
{
	size_t size = str.length();
	fout.write( (char*)&size, sizeof( size ) );
	fout.write( str.c_str(), size );
}


/** @param filename Cesta k souboru, kam se maji ulozit data spritu.
*	@return V pripade uspesneho ulozeni true, jinak false.
*/
bool SpriteFile::saveSprite(std::string filename, std::string directory)
{
	ofstream fout( ( directory + filename).c_str(), ios::out | ios::binary | ios::trunc );
	if( !fout.is_open() )
		return false;


	fout.write( FILE_IDENTIFIER.c_str(), FILE_IDENTIFIER.length() );	//identifikator souboru
	size_t size = m_Parameters.size();
	fout.write( (char*)&size, sizeof(size) );			//pocet parametru

	for( list<Parameter>::iterator iter = m_Parameters.begin(); iter != m_Parameters.end(); iter++ )
	{
		writeString( (*iter).name, fout );
		writeString( (*iter).value, fout );
	}

	size_t filetableoffset = 0;
	for( list<SingleFile>::iterator iter = m_Files.begin(); iter != m_Files.end(); iter++ )
	{
		(*iter).size = getFileSize( (*iter).filename.c_str() );
		filetableoffset += (*iter).size;
	}

	filetableoffset += fout.tellp();
	filetableoffset += sizeof( filetableoffset );
	fout.write( (char*)&filetableoffset, sizeof( filetableoffset ) );	//write file table offset

	//now write all frames data
	for( list<SingleFile>::iterator iter = m_Files.begin(); iter != m_Files.end(); iter++ )
	{
		ifstream fin( (*iter).filename.c_str(), ios::in | ios::binary );
		if( !fin.is_open() )
		{
		    fin.clear();
		    fin.open( (directory + (*iter).filename).c_str(), ios::in | ios::binary );
		    if( !fin.is_open() )
			//error
			continue;
		}

		(*iter).offset = fout.tellp();

		fin.seekg( 0, ios::end );
		size_t filesize = fin.tellg();
		fin.seekg( 0, ios::beg );

		char *buffer = new char[filesize];
		fin.read( buffer, filesize );
		fin.close();

		fout.write( buffer, filesize );
		delete [] buffer;
	}

	//at last..we save file table
	size_t numFiles = m_Files.size();
	fout.write( (char*)&numFiles, sizeof( numFiles ) );

	for( list<SingleFile>::iterator iter = m_Files.begin(); iter != m_Files.end(); iter++ )
	{
		writeString( skipDirectories( (*iter).filename ), fout );
		fout.write( (char*)&(*iter).offset, sizeof( (*iter).offset ) );
		fout.write( (char*)&(*iter).size, sizeof( (*iter).size ) );
	}


	fout.close();

	return true;
}

std::string SpriteFile::readString( ifstream &fin )
{

	size_t size = 0;
	fin.read( (char*)&size, sizeof(size) );
	char *buf = new char[size+1];
	fin.read( buf, size );
	buf[size] = 0;
	std::string str(buf);
	delete [] buf;
	return str;
}


/** @param filename Cesta k souboru s daty spritu.
*	@return True pokud se data nactou v poradku, false v pripade chyb (soubor neexistuje, spatny typ souboru atd).
*/

bool SpriteFile::loadSprite( std::string filename, std::string directory  )
{
	clearDatabase();
	ifstream fin( (directory+filename).c_str(), ios::in | ios::binary );
	if( !fin.is_open() )
		return false;

	char identifier[6];	//identifikator souboru
	fin.read( identifier, sizeof( identifier ) );
	if( memcmp( identifier, FILE_IDENTIFIER.c_str(), sizeof( identifier ) ) != 0 )
	{
		//error spatny typ souboru
		return false;
	}

	//nacteme pocet parametru
	size_t numParameters = 0;
	fin.read( (char*)&numParameters, sizeof( numParameters ) );

	//nacteme a ulozime jednotlive parametry spritu
	for( unsigned int i = 0; i < numParameters; i++ )
	{
		m_Parameters.push_back( Parameter() );
		m_Parameters.back().name = readString( fin );
		m_Parameters.back().value = readString( fin );
	}


	//zjistime, kde je ulozena tabulka souboru
	size_t filetableoffset = 0;
	fin.read( (char*)&filetableoffset, sizeof( filetableoffset ) );
	fin.seekg( filetableoffset );	//a posuneme se na tabulku

	//zjistime pocet souboru
	size_t numFiles = 0;
	fin.read( (char*)&numFiles, sizeof( numFiles ) );

	for( size_t i = 0; i < numFiles; i++ )
	{
		m_Files.push_back( SingleFile() );
		m_Files.back().filename = directory + readString( fin );
		fin.read( (char*)&(m_Files.back().offset), sizeof( m_Files.back().offset ) );
		fin.read( (char*)&(m_Files.back().size), sizeof( m_Files.back().size ) );
	}

	fin.close();
	return true;
}

