//---------------------------------------------------------------------------

#include <vcl.h>
#include <mmsystem.h>
#include <inifiles.hpp>
#include "stdio.h"
#pragma hdrstop

#include "crd_ctrl.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TCardCtrl *CardCtrl;
//---------------------------------------------------------------------------
__fastcall TCardCtrl::TCardCtrl(TComponent* Owner)
        : TForm(Owner)
{
 LM_opened = false;
 char COMM[][10] = { "COM1", "COM2", "COM3", "COM4" };

 IDcard = 0;
 TbCards->Active = true;
 Height = 270;

        // Recuperamos la configuración
        TIniFile *ini;
        ini = new TIniFile( ExtractFileDir( Application->ExeName ) +  "\\CAP.INI"  );
        // General
        PuertoE = ini->ReadInteger( "CAP_w", "PuertoEscritor", 1 );
 AbrePuerto( COMM[PuertoE]);

        cb_PosP    = ini->ReadInteger( "CAP_w", "PosPrefijo", 0 );
        cb_LenP    = ini->ReadInteger( "CAP_w", "LenPrefijo", 2 );
        cb_PosC    = ini->ReadInteger( "CAP_w", "PosCodigo", 2 );
        cb_LenC    = ini->ReadInteger( "CAP_w", "LenCodigo", 8 );
        cb_PosR    = ini->ReadInteger( "CAP_w", "PosRevision", 10 );
        cb_LenR    = ini->ReadInteger( "CAP_w", "LenRevision",  2 );
        cb_PosFd   = ini->ReadInteger( "CAP_w", "PosFechaDesde", 12 );
        cb_PosFh   = ini->ReadInteger( "CAP_w", "PosFechaHasta", 18 );

        delete ini;

}
//---------------------------------------------------------------------------
int __fastcall TCardCtrl::LocateCard( int idc, int owner )
{
 int dev = idc; int version = 0;
  if ( !TbCards->Locate( "id", idc, TLocateOptions() << loCaseInsensitive << loPartialKey ) )
  {
    Height = 270;
    noCardFound->Visible = true;
    TbCards->Insert();
    TbCards->FieldByName("idowner")->AsInteger = owner;
    TbCards->FieldByName("idCard")->AsInteger = 0+((version&0x1F)<<1)+(owner<<6);
    TbCards->FieldByName("vCard")->AsInteger = version;
    TbCards->Post();
    dev = TbCards->FieldByName( "id" )->AsInteger;
  }
  return dev;
}
//---------------------------------------------------------------------------

void __fastcall TCardCtrl::BitBtn2Click(TObject *Sender)
{
  if ( TbCards->FieldByName("idCard")->AsInteger & 0x0001 )
  {
   ShowMessage( "La tarjeta pertenece a un ABONADO TEMPORAL, por lo que su version no puede ser incrementada");
  } else {
   int newVersion, idCard;
   if ( Application->MessageBox("Reasignación código tarjeta, inutilizará la tarjeta actual. ¿Realmente desea continuar?", "CRD: cambio de tarjetra", MB_OKCANCEL) != IDOK)
     return;

   newVersion = (TbCards->FieldByName("vCard")->AsInteger+1)%32;
   idCard = TbCards->FieldByName("idCard")->AsInteger;
   TbCards->Edit();
   TbCards->FieldByName("vCard")->AsInteger = newVersion;
   TbCards->FieldByName("idCard")->AsInteger = (idCard & 0xFFC1) + ((newVersion&0x1F)<<1);
   TbCards->Post();
  }
}
//---------------------------------------------------------------------------

//----------------------- ESCRITOR DE TARJETAS ------------------------------
int  __fastcall TCardCtrl::AbrePuerto(LPTSTR lpszPortName)
{
  DWORD dwError;
  DCB PortDCB;
  COMMTIMEOUTS CommTimeouts;

  if ( LM_opened ) return true;

  LM_opened = false;
	
  // Open the serial port.
  LM_handle = CreateFile (lpszPortName, // Pointer to the name of the port
                      GENERIC_READ | GENERIC_WRITE,
                                    // Access (read/write) mode
                      0,            // Share mode
                      NULL,         // Pointer to the security attribute
                      OPEN_EXISTING,// How to open the serial port
                      0,            // Port attributes
                      NULL);        // Handle to port with attribute
                                    // to copy

  // If it fails to open the port, return FALSE.
  if ( LM_handle == INVALID_HANDLE_VALUE ) 
  {
	dwError = GetLastError ();
	return false;
  } else {
	LM_opened = true;
  }
  
  PortDCB.DCBlength = sizeof (DCB);     

  // Get the default port setting information.
  GetCommState (LM_handle, &PortDCB);

  // Change the DCB structure settings.
  PortDCB.BaudRate = 9600;              // Current baud 
  PortDCB.fBinary = TRUE;               // Binary mode; no EOF check 
  PortDCB.fParity = TRUE;               // Enable parity checking. 
  PortDCB.fOutxCtsFlow = FALSE;         // No CTS output flow control 
  PortDCB.fOutxDsrFlow = FALSE;         // No DSR output flow control 
  PortDCB.fDtrControl = DTR_CONTROL_ENABLE; 
                                        // DTR flow control type 
  PortDCB.fDsrSensitivity = FALSE;      // DSR sensitivity 
  PortDCB.fTXContinueOnXoff = TRUE;     // XOFF continues Tx 
  PortDCB.fOutX = FALSE;                // No XON/XOFF out flow control
  PortDCB.fInX = FALSE;                 // No XON/XOFF in flow control 
  PortDCB.fErrorChar = FALSE;           // Disable error replacement. 
  PortDCB.fNull = FALSE;                // Disable null stripping. 
  PortDCB.fRtsControl = RTS_CONTROL_ENABLE; 
                                        // RTS flow control 
  PortDCB.fAbortOnError = FALSE;        // Do not abort reads/writes on 
                                        // error.
  PortDCB.ByteSize = 7;                 // Number of bits/bytes, 4-8 
  /*
  PortDCB.ByteSize = 8;                 // Number of bits/bytes, 4-8 
  */
  PortDCB.Parity = EVENPARITY;          // 0-4=no,odd,even,mark,space 
  PortDCB.StopBits = 0;        // 0,1,2 = 1, 1.5, 2 

  // Configure the port according to the specifications of the DCB 
  // structure.
  if (!SetCommState (LM_handle, &PortDCB))
  {
    // Could not configure the serial port.
///*-*/    printf( "Unable to configure the serial port\n%s\n" );
    dwError = GetLastError ();
    return FALSE;
  }


  // Retrieve the time-out parameters for all read and write operations
  // on the port. 
  GetCommTimeouts (LM_handle, &CommTimeouts);

  // Change the COMMTIMEOUTS structure settings.
  CommTimeouts.ReadIntervalTimeout = MAXDWORD;  
  
//  CommTimeouts.ReadTotalTimeoutMultiplier = 0;  
    CommTimeouts.ReadTotalTimeoutMultiplier = 1000;  

  CommTimeouts.ReadTotalTimeoutConstant = 1000;    
  CommTimeouts.WriteTotalTimeoutMultiplier = 10;  
  CommTimeouts.WriteTotalTimeoutConstant = 1000;    

  // Set the time-out parameters for all read and write operations
  // on the port. 
  if (!SetCommTimeouts (LM_handle, &CommTimeouts))
  {
    // Could not set the time-out parameters.
///*-*/    printf( "Unable to set the time-out parameters" );
    dwError = GetLastError ();
    return FALSE;
  }

  // Direct the port to perform extended functions SETDTR and SETRTS.
  // SETDTR: Sends the DTR (data-terminal-ready) signal.
  // SETRTS: Sends the RTS (request-to-send) signal. 
  EscapeCommFunction (LM_handle, SETDTR);
  EscapeCommFunction (LM_handle, SETRTS);

  return LM_opened;
};

void  __fastcall TCardCtrl::CierraPuerto(void)
{
 if ( LM_opened )
 {
   CloseHandle( LM_handle );
 }
};

// ObtenerRespuesta
//					Lee del puerto los datos pendientes del buffer
//					(solo lee lo que expulse el LM)
//
//
char * __fastcall TCardCtrl::ObtenerRespuesta( char *rcv )
{
 bool waitingResp = true;
 DWORD CHK, chkF = 0;
 int i = 0;
 DWORD dwCommModemStatus;

   if ( LM_opened )
   {
/*
		// Specify a set of events to be monitored for the port.
		SetCommMask (LM_handle, EV_RXCHAR | EV_CTS | EV_DSR | EV_RLSD | EV_RING);
		// Wait for an event to occur for the port.
		WaitCommEvent (LM_handle, &dwCommModemStatus, 0);
	    // Re-specify the set of events to be monitored for the port.
		SetCommMask (LM_handle, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING);
*/
		// Recogemos la respuesta del lector
		while( i < 75 ) 
		{
		   ReadFile( LM_handle, &(rcv[i]), 1, &CHK, NULL );
		   if ( CHK == 1 )
		   {
			 if ( waitingResp )
			 {
				 if ( rcv[i] != 0x06) waitingResp = false;
				 // ACK
				 // printf( "Esperando a la tarjeta\n" );
			 }

			 if ( rcv[i] == 13 ) break; else { 
				 if ( !waitingResp || i==0 ) i++;
			 }
		   } else 
		   if ( CHK == 0 )
		   {
			  chkF++;
		      if ( chkF == 20 )
			  {
				sprintf( rcv, "ERROR: Respuesta fuera de tiempo" );
				return rcv;
			  }
		   }
		} 
		rcv[i+1] = '\0';

   } else {
		sprintf( rcv, "ERROR: LM no abierto" );
	}

  #ifdef _verbose
///*-*/	printf( "Recibido: %s\n", rcv	);
  #endif

  return rcv;
};




// EnviarComando
//					Envia el comando CMD al lector ID, devolviendo la respuesta 'rcv'
//
//
//
char * __fastcall TCardCtrl::EnviaCMD1( char *CMD, char *rcv, char mlen )
{
 DWORD CHK;
 if ( LM_opened )
 {

   #ifdef _verbose
///*-*/ printf( "Enviado: %s\n", CMD );
   #endif
   WriteFile( LM_handle, CMD, strlen(CMD)+mlen, &CHK, NULL );

   ObtenerRespuesta( rcv );
 } else
  sprintf( rcv, "ERROR: LM no abierto" );

 return rcv;
}

char * __fastcall TCardCtrl::EnviaCMD( char *CMD, char *rcv )
{
 EnviaCMD1( CMD, rcv, 0 );
 return rcv;
}

#define ZT_ClearCommand "/CL0/"
// #define ZT_ReadCommand "/RD3/"
   #define ZT_ReadCommand "/RD1/"
   #define ZT_WriteCommand "/WR1/"
#define ACK 0x06
#define STX 0x02
#define NAK 0x15
#define ETX 0x03
#define EOT 0x04
#define CR  0x0D

char  __fastcall TCardCtrl::CalculaBCC( char *cadena )
{
  int i, dev=0;
  for ( i=0; cadena[i] != ETX; i++ )
  {
      dev ^= cadena[i];
  }
  dev ^= cadena[i];

  return dev;
}


char * __fastcall TCardCtrl::LeeTarjeta( char *rcv )
{
	// Leemos una tarjeta
	char buff[3], buff2[80];
	EnviaCMD( ZT_ReadCommand, rcv );
        if ( rcv[0] == ACK && rcv[1] == 0x0D )
        {
        	// Ahora si se ha recibido <ACK><CR> Leemos la tarjeta que pasemos
                sndPlaySound("snd\\inserta_card_leer.wav", SND_ASYNC | SND_FILENAME);
        	ObtenerRespuesta( rcv );
        	// Vemos si es la tarjeta o un error
        	if ( rcv[0] == STX && rcv[1] == '/' && rcv[2] == 'E' && rcv[3] == 'R' )
        	{
        		//es un error
        	  #ifdef _verbose
///*-*/        		printf( "Error: %d", rcv[1] );
        	  #endif
        	} else {
                        char len = 0;
                        while( rcv[len]!=CR )len++;

        		char BCC = CalculaBCC( &rcv[1] ),
        			 BCCr = rcv[ len-1 ];
        		if ( BCC != BCCr )
        		{
        			sprintf( rcv, "Error: BCC recibido es incorrecto BCC(%x) != R(%x)\n", BCC, BCCr );
        		}
        	}
              	buff[0] = ACK; buff[1] = '\0';
        	EnviaCMD( buff, buff2 );
        } else {
                sprintf( rcv, "ERROR: Commando no aceptado\n");
        }
 return rcv;
}

char * __fastcall TCardCtrl::EscribeTarjeta( char *datos, char *rcv )
{
	// Leemos una tarjeta
	char env[140], buff[140], BCC;
	EnviaCMD( ZT_WriteCommand, rcv );
        if ( rcv[0] == ACK && rcv[1] == CR )
        {
                sprintf( env, "%s%c", datos, ETX );
                BCC = CalculaBCC( env );
                sprintf( buff, "%c%s%c", STX, env, BCC );
                if ( BCC )
                        EnviaCMD( buff, rcv );
                else
                        EnviaCMD1( buff, rcv, 1 );

                if ( rcv[0] == ACK && rcv[1] == CR )
                {
                	// Ahora si se ha recibido <ACK><CR> Leemos la tarjeta que pasemos
                        sndPlaySound("snd\\inserta_card_grabar.wav", SND_ASYNC | SND_FILENAME);
                	ObtenerRespuesta( rcv );

                	// Vemos si es la tarjeta o un error
                	if ( !(rcv[0] == EOT && rcv[1] == CR) )
                	{
                		//es un error
                		sprintf( rcv, "Error: %s\n", rcv );
                	  #ifdef _verbose
///*-*/                		printf( "Error: %s\n", rcv );
                	  #endif
                	}
                      	buff[0] = ACK; buff[1] = CR; buff[2] = '\0';
                	EnviaCMD( buff, rcv );
                } else {
                 sprintf( rcv, "ERROR: No se recibio respuesta despues de los datos\n" );
                }
        } else {
                sprintf( rcv, "ERROR: Commando no aceptado\n");
        }
 return rcv;
}
void __fastcall TCardCtrl::GrabaTarjeraClick(TObject *Sender)
{
 char rcv[140];
 statusOK = false;
 if ( LM_opened )
 {
   // Inicializamos el ZT-2300
   EnviaCMD( ZT_ClearCommand, rcv );
   if ( rcv[0] == 'E' )
   {
     ShowMessage( "Error inicializando el GRABADOR" );
   } else {
        char rcv[80], card[140];
        ObtenCadenaTarjeta(card);
        EscribeTarjeta( card, rcv );

        statusOK = !( rcv[0] == 'E' && rcv[5] == ':' );
   }
 } else {
   ShowMessage("El Puerto no pudo ser ABIERTO" );
 }

}
//---------------------------------------------------------------------------
char * __fastcall TCardCtrl::ObtenCadenaTarjeta(char *dev)
{
 int Prefijo = 20, Rev = 0;
 //-----
 if ( IDcard != 0 )
         sprintf( dev, "%02d%08d%02d", Prefijo, IDcard, Rev );
 else
         sprintf( dev, "%02d%08d%02d", Prefijo, TbCards->FieldByName("idCard")->AsInteger, Rev );
 return dev;
}

void __fastcall TCardCtrl::VerificarTarjetaClick(TObject *Sender)
{
 char rcv[140];
 if ( LM_opened )
 {
   // Inicializamos el ZT-2300
   EnviaCMD( ZT_ClearCommand, rcv );
   if ( rcv[0] == 'E' )
   {
     ShowMessage( AnsiString( rcv ) + "Error inicializando el GRABADOR" );
   } else {
        char rcv[80], buff[80], card[140];
        ObtenCadenaTarjeta(card);
        LeeTarjeta( rcv );  int i;
        for( i=1; rcv[i] != ETX; i++ ) buff[i-1]=rcv[i];
        buff[i-1]='\0';
        
        if ( strcmp( buff, card ) != 0 )
        {
            sndPlaySound("snd\\card_verify_error.wav", SND_ASYNC | SND_FILENAME);
            ShowMessage("La tarjeta pasada, no se corresponde");
        } else {
            sndPlaySound("snd\\card_verify_ok.wav", SND_ASYNC | SND_FILENAME);
            ShowMessage("La tarjeta pasada, es correcta");
        }

   }
 } else {
   ShowMessage("El Puerto no pudo ser ABIERTO" );
 }
}
//---------------------------------------------------------------------------
void __fastcall TCardCtrl::FormClose(TObject *Sender, TCloseAction &Action)
{
 if ( LM_opened ) CierraPuerto();
}
//---------------------------------------------------------------------------

