#include "sffisico.h"
#include "errores.h"

#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

/* Dependencias del sistema */

#ifdef __MSDOS__

#include <io.h>
#include <sys/stat.h>
#define PERMISOS_DISCO (S_IREAD|S_IWRITE)
#define MODO (O_RDWR|O_BINARY)

#else

#include <unistd.h>
#define PERMISOS_DISCO  0644
#define MODO O_RDWR

#endif


enum Errores Error_SF;

/* Variables internas del módulo */

static int Disco = -1;
static WORD TamBloqueFisico = 0;
static DWORD NumBloquesFisicos = 0;



/* Instalación de un disco */


int FabricaDisco (const char* nombre_fichero,WORD tam_bloque,DWORD num_bloques)
{
	struct Superbloque bl;
	DWORD tam_total = tam_bloque*num_bloques;
	int err;
	int fd;

	if (    tam_total>MAX_TAMDISCO || tam_bloque>MAX_TAMBLOQUE
	     || tam_total<num_bloques )
	{
	  Error_SF = ERR_RANGO;
	  return -1;
	}

#ifdef __MSDOS__
	_fmode=O_BINARY;
#endif

	/* Crea el fichero */
	fd = creat (nombre_fichero,PERMISOS_DISCO);

	if ( fd==-1 )
	{
	  switch (errno)
	  {
	    case EACCES:
	      Error_SF = ERR_PERMISOS;
	      return -1;
	    default:
	      Error_SF = ERR_DISCO;
	      return -1;
	  }
	}

#ifdef __MSDOS__
	err=chsize (fd,tam_total);
#else
	err=ftruncate (fd,tam_total);
#endif

	if (err==-1)
	{
	  Error_SF=ERR_DISCO;
	  return -1;
	}

	/* Escribe los datos del superbloque */
	strcpy(bl.magico,MAGICO);
	bl.tam_bloque_fisico = tam_bloque;
	bl.num_bloques_fisicos = num_bloques;
	write(fd,&bl,TAM_SUPERBLOQUE);


	close(fd);
	Error_SF = ERR_SINERROR;
	return 0;

}  /* FabricaDisco */



/* Monta un disco para su uso */
/* se ignora el parámetro nbloques_cache */


int MontaDisco ( const char* nombre_fichero, int nbloques_cache )
{
	struct Superbloque bl;
	int ok;

	if (Disco!=-1) close(Disco);

	Disco = open(nombre_fichero,MODO);

	if ( Disco==-1 )
	{
	  switch (errno)
	  {
	    case EACCES:
	      Error_SF=ERR_PERMISOS;
	    case ENOENT:
	      Error_SF=ERR_NOEXISTE;
	    default:
	      Error_SF=ERR_DISCO;
	  }
	  return -1;
	}

	/* Lee información del bloque cero */
	ok=LeeDatosDisco (&TamBloqueFisico,&NumBloquesFisicos);
	if (ok==-1) return -1;

	Error_SF=ERR_SINERROR;
	return 0;

}  /* MontaDisco */





int DesmontaDisco (void)
{
	int ok;
	if (Disco==-1)
	{
	  Error_SF=ERR_INACTIVO;
	  return -1;
	}
	ok = close(Disco);
	if (ok==-1)
	 { Error_SF=ERR_DISCO; return -1; }
	else
	 { Error_SF=ERR_SINERROR; return 0; }
}




/* Acceso a un bloque físico (lectura o escritura) */

enum { LECTURA, ESCRITURA };


int AccedeBloqueFisico ( DWORD nbloque, void* bufer,enum ModoAccesoFisico modo )
{
	Error_SF=ERR_SINERROR;
#ifdef DEBUG
	printf("[%s:%d] ",modo==ACCESO_FISICO_LECTURA?"R":"W",nbloque);
#endif
	if ( Disco==-1 )
	{ Error_SF=ERR_INACTIVO; return -1; }
	if ( nbloque>=NumBloquesFisicos )
	{ Error_SF=ERR_RANGO; return -1; }

	lseek (Disco,nbloque*TamBloqueFisico,SEEK_SET);
	switch (modo)
	{
	   case ACCESO_FISICO_ESCRITURA:
	     return write ( Disco,bufer,TamBloqueFisico ) ==TamBloqueFisico;
	   case ACCESO_FISICO_LECTURA:
	     return read ( Disco,bufer,TamBloqueFisico ) ==TamBloqueFisico;
	   default:
	     Error_SF=ERR_CORRUPTO;
	     return -1;
	}
}


int LeeBloqueFisico ( DWORD nbloque, void* bufer )
{ return AccedeBloqueFisico(nbloque,bufer,LECTURA); }


int EscribeBloqueFisico ( DWORD nbloque, const void* bufer )
{ return AccedeBloqueFisico(nbloque,(void*)bufer,ESCRITURA); }



/* Acceso al superbloque (datos del disco) */


int LeeDatosDisco ( WORD* tam_bloque, DWORD* num_bloques )
{
   struct Superbloque sb;

   if (Disco==-1)
    { Error_SF=ERR_INACTIVO; return -1; }

   /* Recoge el superbloque */
   lseek(Disco,0,SEEK_SET);
   read(Disco,&sb,TAM_SUPERBLOQUE);

   /* Verifica que el disco tiene la "palabra mágica" */
   if ( strcmp(sb.magico,MAGICO) )
   {
     Error_SF=ERR_FORMATO;
     return -1;
   }

   /* Verifica que el tamaño de bloque físico es correcto */
   if (    sb.tam_bloque_fisico<MIN_TAMBLOQUE
	|| sb.tam_bloque_fisico>MAX_TAMBLOQUE
	|| sb.num_bloques_fisicos==0
      )
   {
     Error_SF = ERR_FORMATO;
     return -1;
   }

   *tam_bloque = sb.tam_bloque_fisico;
   *num_bloques = sb.num_bloques_fisicos;
   Error_SF = ERR_SINERROR;
   return 0;
}