/**
 * Classe permettant la gestion d'un fichier OCP
*/                                                                                                           

#include "CCPCFileManager.h"
#include "CError.h"
#include <fstream>

//
// Gestion d'un fichier CPC
// 
/// Charge un fichier
void CCPCFileManager::loadRawData(std::string &i_filename, unsigned char* &o_data, unsigned int &o_size)
{
	std::ifstream file(i_filename.c_str(),std::ios::binary);

	ASSERTMSG(file.good(),"Unable to open file " << i_filename);

	file.seekg(0,std::ios::end);
	int size = file.tellg();
	file.seekg(0,std::ios::beg);

	unsigned char *data = new unsigned char[size];
	file.read((char*)data,size);

	if (CCPCFileManager::removeFileHeader(data,size,o_data,o_size))
	{
		//INFO("Header removed in " << i_filename);
	}
	delete[] data;
}

bool CCPCFileManager::removeFileHeader(unsigned char *i_data, unsigned int i_size, unsigned char* &o_data, unsigned int &o_size)
{
	bool findChecksum = false;
	if (i_size > 0x80)
	{
		unsigned int checksum = i_data[0x43]+i_data[0x44]*256;
		findChecksum = (checksum == CCPCFileManager::checkSum(i_data));
	}

	if (!findChecksum)
	{
		o_data = new unsigned char[i_size];
		o_size = i_size;
		memcpy(o_data,i_data,o_size);
		return false;
	}
	
	o_data = new unsigned char[i_size-0x80];
	o_size = i_size-0x80;
	memcpy(o_data,i_data+0x80,o_size);

	return true;
}

unsigned int CCPCFileManager::checkSum(const unsigned char *i_data)
{	
	unsigned int checksum=0;
	for (unsigned int i=0;i<0x42;i++)
		checksum+=i_data[i];
	return checksum;
}


//
// Gestion d'un fichier OCP
// 
bool COCPFileManager::loadPalette(std::string &i_filename, int &o_mode, unsigned int o_palette[16])
{
	unsigned char *data;
	unsigned int size;

	CCPCFileManager::loadRawData(i_filename,data,size);

	bool read = readPalette(data,size,o_mode,o_palette);

	delete[] data;
	return read;
}
bool COCPFileManager::loadScreen(std::string &i_filename, unsigned char* &o_data, unsigned int &o_size)
{
	unsigned char *data;
	unsigned int size;

	CCPCFileManager::loadRawData(i_filename,data,size);

	bool read = readScreen(data,size,o_data,o_size);

	delete[] data;
	return read;
}
bool COCPFileManager::loadWindow(std::string &i_filename, unsigned char* &o_data, unsigned int &o_width, unsigned int &o_widtho, unsigned int &o_height)
{
	unsigned char *data;
	unsigned int size;

	CCPCFileManager::loadRawData(i_filename,data,size);

	bool read = readWindow(data,size,o_data,o_width,o_widtho,o_height);

	delete[] data;
	return read;
}

bool COCPFileManager::readPalette(unsigned char *i_data, unsigned int i_size, int &o_mode, unsigned int o_paletteHard[16])
{
	if (i_size != (3+16*12+44))
		return false;

	int p=0;

	o_mode = i_data[p++];
	p++;
	p++;
	
	for (int i=0;i<16;i++)
	{
		int c = i_data[p++];
		o_paletteHard[i]=c-64;
		p+= 11;
	}

	return true;
}
bool COCPFileManager::readScreen(unsigned char *i_data, unsigned int i_size, unsigned char* &o_data, unsigned int &o_size)
{
	int piData, poData;
	unsigned int iSize,oSize,rSize;

	o_size = 0x4000;
	o_data = new unsigned char[o_size];
	memset(o_data,0,o_size);	

	piData = 0;
	poData = 0;
	iSize = i_size;

	if (readCompressedBlock(i_data+piData,iSize,rSize,o_data+poData,oSize))
	{
		piData+=rSize;
		iSize-=rSize;
		poData+=oSize;
		while (iSize > 0)
		{
			if (readCompressedBlock(i_data+piData,iSize,rSize,o_data+poData,oSize))
			{
				piData+=rSize;
				iSize-=rSize;
				poData+=oSize;
			}
		}
		return true;
	}

	memcpy(o_data,i_data,(i_size<o_size) ? i_size : o_size);

	return false;
}
bool COCPFileManager::readWindow(unsigned char *i_data, unsigned int i_size, unsigned char* &o_data, unsigned int &o_width, unsigned int &o_widtho, unsigned int &o_height)
{
	int piData, poData;
	unsigned int iSize,oSize,rSize;

	int outSize;

	piData = i_size-4;
	o_width = (int)i_data[piData++];
	o_width += (int)i_data[piData++] * 256;

	o_widtho = ((o_width % 8) == 0) ? (o_width / 8) : ((o_width/8) + 1);

	o_height = (int)i_data[piData++];

	outSize = o_widtho * o_height;
	if (outSize > 0x4000)
	{
		o_data = new unsigned char[i_size];
		memcpy(o_data,i_data,i_size);
		o_width = 1;
		o_widtho = 1;
		o_height = 1;
		return false;
	}
	o_data = new unsigned char[outSize];
	
	memset(o_data,0,outSize);
	poData = 0;
	piData = 0;
	iSize = i_size-5;

	if (readCompressedBlock(i_data+piData,iSize,rSize,o_data+poData,oSize))
	{
		piData+=rSize;
		iSize-=rSize;
		poData+=oSize;
		while (iSize > 0)
		{
			if (readCompressedBlock(i_data+piData,iSize,rSize,o_data+poData,oSize))
			{
				piData+=rSize;
				iSize-=rSize;
				poData+=oSize;
			}
		}
		return true;
	}

	if (i_size-5 != outSize)
	{
		unsigned char *oldData = o_data;
		delete[] oldData;
		o_data = new unsigned char[i_size];
		memcpy(o_data,i_data,i_size);
		o_width = 2*8;
		o_widtho = 2;
		o_height = 8;
		return false;
	}

	memcpy(o_data,i_data,outSize);
	
	return true;
}

bool COCPFileManager::readCompressedBlock(unsigned char *i_data, unsigned int i_size, unsigned int &o_readData, unsigned char* o_data,unsigned int &o_size)
{
	int p=0;
	
	if (strncmp("MJH",(char*)i_data,3) == 0)
	{
		p+=3;
		o_size = i_data[p++];
		o_size += i_data[p++]*256;

		int i = 0;
		while (i<o_size)
		{
			unsigned char m,c,b;
			m = i_data[p++];
			if (m == 1)
			{
				c = i_data[p++];
				b = i_data[p++];
				int nb = ((unsigned int)c == 0) ? 256 : (unsigned int)c;
				while (i<o_size && nb!=0)
				{
					o_data[i++] = b;
					nb--;
				}
			}
			else
			{
				o_data[i++] = m;
			}
		}
		o_readData = p;
		return true;
	}

	return false;
}
