/**
* @class CCPCDataDisc
* Classe permettant la gestion d'un disc CPC Data (utilisation de la sous-lib DSK)
* @author Thierry JOUIN
* @version 1.1
* @date 31/10/2001
*/

#include "CCPCDataDisc.h"

#include "CError.h"

CCPCDataDisc::CCPCDataDisc()
:CCPCDisc()
{
}

CCPCDataDisc::CCPCDataDisc(DSK_PDRIVER i_driver, const DSK_GEOMETRY &i_geometry)
:CCPCDisc(i_driver,i_geometry)
{
}

CCPCDataDisc::~CCPCDataDisc()
{
}

/// Affiche l'occupation du disc
void CCPCDataDisc::scanBlock(std::ostream &io_os) const
{
	for (unsigned int c=0;c<_geometry.dg_cylinders;c++)
    {
		io_os << "Track " << c << " :\t";
		for (unsigned int s=0;s<_geometry.dg_sectors;s++)
		{
			unsigned int b = (c*_geometry.dg_sectors + s) >> 1;
			if (b == 0 || b == 1)
			{
				io_os << "C\t";
			}
			else
			{
				int entry = getCatalogueEntryForBlock(b);
				if (entry == -1)
				{
					io_os << "*\t";
				}
				else
				{
					io_os << entry << "\t";
				}
			}
		}
		io_os << std::endl;
    }
}

/// Renvoie la liste des block libres
void CCPCDataDisc::getEmptyBlock(std::vector<unsigned int> &o_block) const
{
	for (unsigned int c=0;c<_geometry.dg_cylinders;c++)
		for (unsigned int s=0;s<_geometry.dg_sectors;s++)
		{
			unsigned int b = (c*_geometry.dg_sectors + s) >> 1;
			int entry = getCatalogueEntryForBlock(b);
			if (((c*_geometry.dg_sectors + s) % 2) != 1)
				if (b != 0 && b != 1 && entry == -1)
					o_block.push_back(b);
		}
}

/// Pour une entree du catalogue, affiche la position des donnees sur le disc
void CCPCDataDisc::printBlockForEntry(std::ostream &io_os, unsigned int i_entry) const
{
	for (unsigned int c=0;c<_geometry.dg_cylinders;c++)
    {
		io_os << "Track " << c << " :\t";
		for (unsigned int s=0;s<_geometry.dg_sectors;s++)
		{
			unsigned int b = (c*_geometry.dg_sectors + s) >> 1;
			if (b == 0 || b == 1)
			{
				io_os << "C";
			}
			else
			{
				int entry = getCatalogueEntryForBlock(b);
				if (entry == (int)i_entry)
				{
					io_os << "O";
				}
				else
				{
					if (entry == -1)
					{
						io_os << "*";
					}
					else
					{
						io_os << "-";
					}
				}
			}
		}
		io_os << std::endl;
    }
}

/// Lit le catalogue sur le disc
void CCPCDataDisc::readCatalogueBuffer()
{
	readBlock(0,_catalogueBuffer);
	readBlock(1,_catalogueBuffer+(_geometry.dg_secsize*2));
}
/// Ecrit le catalogue sur le disc
void CCPCDataDisc::writeCatalogueBuffer()
{
	writeBlock(0,_catalogueBuffer);
	writeBlock(1,_catalogueBuffer+(_geometry.dg_secsize*2));
}

/// Lit un secteur sur le disc (buffer deja alloue !)
void CCPCDataDisc::readSector(const unsigned char i_cylinder, const unsigned char i_head, const unsigned char i_sectorID, void* o_buffer) const
{
	ASSERTMSG( (i_cylinder<_geometry.dg_cylinders) , "Invalide track ID " << (int)i_cylinder << " for " << (int)_geometry.dg_cylinders);
	ASSERTMSG( (i_sectorID>=_geometry.dg_secbase && (i_sectorID<(_geometry.dg_secbase+_geometry.dg_sectors))) , "Invalide sector ID " << i_sectorID);
	ASSERTMSG( (i_head == 0) , "Invalide head");
	dsk_err_t e;
	e = dsk_pread(_driver,&_geometry,o_buffer,i_cylinder,i_head,i_sectorID);
	
	ASSERTMSG( (e == DSK_ERR_OK) , "Error writing sector :" << std::string(dsk_strerror(e)));
}
/// Ecrit un secteur sur le disc
void CCPCDataDisc::writeSector(const unsigned char i_cylinder, const unsigned char i_head, const unsigned char i_sectorID, const void* i_buffer)
{
	ASSERTMSG( (i_cylinder<_geometry.dg_cylinders) , "Invalide track ID " << i_cylinder);
	ASSERTMSG( (i_sectorID>=_geometry.dg_secbase && (i_sectorID<(_geometry.dg_secbase+_geometry.dg_sectors))) , "Invalide sector ID " << i_sectorID);
	ASSERTMSG( (i_head == 0) , "Invalide head");
	dsk_err_t e;
	e = dsk_pwrite(_driver,&_geometry,i_buffer,i_cylinder,i_head,i_sectorID);
	
	ASSERTMSG( (e == DSK_ERR_OK) , "Error writing sector :" << std::string(dsk_strerror(e)));
}
/// Lit un bloc sur le disc (buffer deja alloue !)
void CCPCDataDisc::readBlock(const unsigned char i_idBlock, void* o_buffer) const
{
	unsigned char track1 = (i_idBlock*2 - (i_idBlock*2 % _geometry.dg_sectors)) / _geometry.dg_sectors;
	unsigned char sector1 = _geometry.dg_secbase+(i_idBlock*2 % _geometry.dg_sectors);
	unsigned char track2 = ((i_idBlock*2+1) - ((i_idBlock*2+1) % _geometry.dg_sectors)) / _geometry.dg_sectors;
	unsigned char sector2 = _geometry.dg_secbase+((i_idBlock*2+1) % _geometry.dg_sectors);
	
	readSector(track1,0,sector1,o_buffer);
	readSector(track2,0,sector2,(char*)o_buffer+_geometry.dg_secsize);
	
}
/// Ecrit un secteur sur le disc
void CCPCDataDisc::writeBlock(const unsigned char i_idBlock, const void* i_buffer)
{
	unsigned char track1 = (i_idBlock*2 - (i_idBlock*2 % _geometry.dg_sectors)) / _geometry.dg_sectors;
	unsigned char sector1 = _geometry.dg_secbase+(i_idBlock*2 % _geometry.dg_sectors);
	unsigned char track2 = ((i_idBlock*2+1) - ((i_idBlock*2+1) % _geometry.dg_sectors)) / _geometry.dg_sectors;
	unsigned char sector2 = _geometry.dg_secbase+((i_idBlock*2+1) % _geometry.dg_sectors);
	
	writeSector(track1,0,sector1,i_buffer);
	writeSector(track2,0,sector2,(char*)i_buffer+_geometry.dg_secsize);
	
}

/// Ouverture d'un DSK deja existant
void CCPCDataDisc::open(const std::string &i_filename, int i_inside)
{  
	dsk_err_t e;
	
	e = dsk_open(&_driver,i_filename.c_str(), NULL, NULL);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error opening dsk :" << std::string(dsk_strerror(e)));
	
	e = dsk_set_forcehead(_driver, i_inside);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error opening dsk :" << std::string(dsk_strerror(e)));
	
	e = dsk_getgeom(_driver,&_geometry);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error opening dsk :" << std::string(dsk_strerror(e)));
	
	ASSERTMSG ( (_geometry.dg_secbase == 0xc1) , "Error opening dsk : not a Data CPC format");
}
/// Ouverture en creation d'un DSK
void CCPCDataDisc::create(const std::string &i_filename, int i_inside)
{
	dsk_err_t e;
	
	if (i_filename == std::string("/dev/fd0"))
		e = dsk_creat(&_driver,i_filename.c_str(), "floppy", NULL);
	else
		e = dsk_creat(&_driver,i_filename.c_str(), "dsk", NULL);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error creating dsk :" << std::string(dsk_strerror(e)));
	
	e = dsk_set_forcehead(_driver, i_inside);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error creating dsk :" << std::string(dsk_strerror(e)));
	
	e = dg_stdformat(&_geometry, FMT_CPCDATA , NULL, NULL);
	ASSERTMSG( (e==DSK_ERR_OK) , "Error creating dsk :" << std::string(dsk_strerror(e)));  
}

/// Formattage du DSK
void CCPCDataDisc::format()
{
	dsk_err_t e = DSK_ERR_OK;
	
	printf("Formating\n");
	for (unsigned int cyl = 0; cyl < _geometry.dg_cylinders; cyl++)
    {
		for (unsigned int head = 0; head < _geometry.dg_heads; head++)
		{
			printf("Cyl %02d/%02d Head %d/%d\r", cyl +1, _geometry.dg_cylinders, head+1, _geometry.dg_heads);
			fflush(stdout);
			
			ASSERTMSG( (e==DSK_ERR_OK) , "Error while formating :" << std::string(dsk_strerror(e)));
			e = dsk_apform(_driver, &_geometry, cyl, head, 0xE5);
			ASSERTMSG( (e==DSK_ERR_OK) , "Error while formating :" << std::string(dsk_strerror(e)));
		}
    }
}
