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

#include "CCPCSystemDisc.h"

#include "CError.h"
/// Indique que le bloc correspond au boot CPM
unsigned int CCPCSystemDisc::boot =(0-1);

CCPCSystemDisc::CCPCSystemDisc()
  :CCPCDisc()
{
}

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

CCPCSystemDisc::~CCPCSystemDisc()
{
}

/// Affiche l'occupation du disc
void CCPCSystemDisc::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;
	  b = (b < 9) ? CCPCSystemDisc::boot : (b-9);

	  if (b == CCPCSystemDisc::boot)
	    {
	      io_os << "B\t";
	    }
	  else
	    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 CCPCSystemDisc::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;
	b = (b < 9) ? CCPCSystemDisc::boot : (b-9);
	int entry = getCatalogueEntryForBlock(b);
	if (((c*_geometry.dg_sectors + s) % 2) != 1)
	  if (b!=0 && b!=1 && b!=CCPCSystemDisc::boot && entry == -1)
	    o_block.push_back(b);
      }
}

/// Pour une entree du catalogue, affiche la position des donnees sur le disc
void CCPCSystemDisc::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;
	  b = (b < 9) ? CCPCSystemDisc::boot : (b-9);

	  if (b == CCPCSystemDisc::boot)
	    {
	      io_os << "B\t";
	    }
	  else
	    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 CCPCSystemDisc::readCatalogueBuffer()
{
  readBlock(0,_catalogueBuffer);
  readBlock(1,_catalogueBuffer+(_geometry.dg_secsize*2));
}
/// Ecrit le catalogue sur le disc
void CCPCSystemDisc::writeCatalogueBuffer()
{
  writeBlock(0,_catalogueBuffer);
  writeBlock(1,_catalogueBuffer+(_geometry.dg_secsize*2));
}

/// Lit un secteur sur le disc (buffer deja alloue !)
void CCPCSystemDisc::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 " << 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_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 CCPCSystemDisc::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 CCPCSystemDisc::readBlock(const unsigned char i_idBlock, void* o_buffer) const
{
  unsigned int bloc = i_idBlock+9;

  unsigned char track1 = (bloc*2 - (bloc*2 % _geometry.dg_sectors)) / _geometry.dg_sectors;
  unsigned char sector1 = _geometry.dg_secbase+(bloc*2 % _geometry.dg_sectors);
  unsigned char track2 = ((bloc*2+1) - ((bloc*2+1) % _geometry.dg_sectors)) / _geometry.dg_sectors;
  unsigned char sector2 = _geometry.dg_secbase+((bloc*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 CCPCSystemDisc::writeBlock(const unsigned char i_idBlock, const void* i_buffer)
{
  unsigned int bloc = i_idBlock+9;

  unsigned char track1 = (bloc*2 - (bloc*2 % _geometry.dg_sectors)) / _geometry.dg_sectors;
  unsigned char sector1 = _geometry.dg_secbase+(bloc*2 % _geometry.dg_sectors);
  unsigned char track2 = ((bloc*2+1) - ((bloc*2+1) % _geometry.dg_sectors)) / _geometry.dg_sectors;
  unsigned char sector2 = _geometry.dg_secbase+((bloc*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 CCPCSystemDisc::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 == 0x41) , "Error opening dsk : not a System CPC format");
}

/// Ouverture en creation d'un DSK
void CCPCSystemDisc::create(const std::string &i_filename, int i_inside)
{
  dsk_err_t e;
  
  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_CPCSYS , NULL, NULL);
  ASSERTMSG( (e==DSK_ERR_OK) , "Error creating dsk :" << std::string(dsk_strerror(e)));  
}

/// Formattage du DSK
void CCPCSystemDisc::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)));
	}
    }
}
