#include <stdlib.h>
#include <stdio.h>

#ifndef TARGET_OSX
#include <malloc.h>
#endif

#include "pcscdefines.h"
#include "AdmHndlr.h"
#include "ctapi.h"
#include "ifdhandler.h"

#ifndef TARGET_OSX
#include "config.h"
#endif

static unsigned short currentCardTerminal = 0;

char CT_init (	unsigned short Ctn,
		unsigned short Pn ) {                                   

  ULONG rv;
  static int acsinit = 0;

  rv = OK;

  currentCardTerminal = Ctn;

  if ( acsinit == 0 ) {
   acsinit = 1;
   
   rv = Adm_Initialize("usb", Ctn, 0);
  }


  if ( rv == STATUS_SUCCESS ) {
    return OK;
  } else {
    return ERR_CT;
  }

}


char CT_data (	unsigned short	Ctn,
		unsigned char	*Dad,
		unsigned char	*Sad,
		unsigned short	Lc,
		unsigned char	*Cmd,
		unsigned short	*Lr, 
		unsigned char	*Rsp) {


  ULONG rv;
  ULONG AtrLength;
  int i;

  if ( (*Dad) == 1 ) {
    /* Reader command */

    *Dad = *Sad;
    *Sad = 1;

    /* Removed per customer request
    if ( Cmd[0] != 0x20 ) {
      return ERR_INVALID;
    }
    */

    switch(Cmd[1]) {
    case 0x11:
	/* I don't see a RESET CT command in the ACS docs
           Passing this one as a RESET ICC instead */
    case 0x12:

      /* Request ICC should wait until card is inserted */

      if (Adm_IsICCPresent(Ctn) != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_CT_ERR_REMOVED / 256;
	Rsp[1] = CTBCS_CT_ERR_REMOVED % 256;
	
	*Lr = 2;
	return OK;
      }

	rv = Adm_SetOption(Ctn, ADM_OPTION_EMV_OFF | ADM_OPTION_MEMCARD_ON);
    if (rv != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_ICC_ERR_RESET / 256;
	Rsp[1] = CTBCS_ICC_ERR_RESET % 256;
	*Lr = 2;
	return OK;
    }

    rv = Adm_Select442818( Ctn, Rsp, &AtrLength );
    if (rv != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_ICC_ERR_RESET / 256;
	Rsp[1] = CTBCS_ICC_ERR_RESET % 256;
	*Lr = 2;
	return OK;
      }

      Rsp[AtrLength]   = CTBCS_ICCS_OK_RESET / 256;
      Rsp[AtrLength+1] = CTBCS_ICCS_OK_RESET % 256;
      
      *Lr = AtrLength + 2;

      return OK;
      break;

    case 0x15:
      /* Eject */
      rv = Adm_UnPowerICC(Ctn, 0);

      /* error handling needed */
      Rsp[0] = CTBCS_ICC_SUCCESS / 256;
      Rsp[1] = CTBCS_ICC_SUCCESS % 256;

      *Lr = 2;

      return OK;
      break;  

    case 0x13:
      /* Get Status */
      if (Adm_IsICCPresent(Ctn) == STATUS_SUCCESS) {
	Rsp[0] = 0x01;  /* Card inserted */
      } else {
	Rsp[0] = 0x00;  /* No card */
      }

      /* error handling needed */

      Rsp[1] = CTBCS_ICC_SUCCESS / 256;
      Rsp[2] = CTBCS_ICC_SUCCESS % 256;

      *Lr = 3;

      return OK;
      break;


    default:
      Rsp[0] = CTBCS_CT_ERR_INS / 256;
      Rsp[1] = CTBCS_CT_ERR_INS % 256;

      *Lr = 2;
      return OK;      
      break;

    }



  } else {
    /* Card command */
    *Dad = *Sad;
    *Sad = 0;

    switch(Cmd[1]) {
    case 0xA4:
      Rsp[0] = CTBCS_ICC_SUCCESS / 256;
      Rsp[1] = CTBCS_ICC_SUCCESS % 256;

      *Lr = 2;
      return OK;
      break;

    case 0xB0:
      /* Read Binary - part 7 */

      if (Adm_IsICCPresent(Ctn) != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_CT_ERR_REMOVED / 256;
	Rsp[1] = CTBCS_CT_ERR_REMOVED % 256;
	
	*Lr = 2;
	return OK;
      }

      if ( Cmd[4] == 0 ) {
	/* Read everything */

	if ( (Cmd[2] != 0) || (Cmd[3] != 0) ) {
	  /* Can't have offset */
	  Rsp[0] = CTBCS_ICC_ERR_MEM / 256;
	  Rsp[1] = CTBCS_ICC_ERR_MEM % 256;
	  *Lr = 2;
	  return OK;
	}	


	for ( i=0; i < 8; i++ ) {
	  rv = Adm_ReadData( Ctn, (i*128), &Rsp[i*128], 128);
	  if (rv != STATUS_SUCCESS) {
	    Rsp[0] = CTBCS_ICC_ERR_MEM / 256;
	    Rsp[1] = CTBCS_ICC_ERR_MEM % 256;
	    *Lr = 2;
	    return OK;
	  }	
	}

	Rsp[1024] = CTBCS_ICC_SUCCESS / 256;
	Rsp[1025] = CTBCS_ICC_SUCCESS % 256;
	*Lr = 1026;
	return OK;
      }



      rv = Adm_ReadData( Ctn, (Cmd[2]*256) + Cmd[3], Rsp, Cmd[4]);
      if (rv != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_ICC_ERR_MEM / 256;
	Rsp[1] = CTBCS_ICC_ERR_MEM % 256;
	*Lr = 2;
	return OK;
      }	

      Rsp[Cmd[4]]   = CTBCS_ICC_SUCCESS / 256;
      Rsp[Cmd[4]+1] = CTBCS_ICC_SUCCESS % 256;

      *Lr = Cmd[4] + 2;
      return OK;
      break;

    case 0xD6:
      /* Update Binary - part 7 */

      if (Adm_IsICCPresent(Ctn) != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_CT_ERR_REMOVED / 256;
	Rsp[1] = CTBCS_CT_ERR_REMOVED % 256;
	
	*Lr = 2;
	return OK;
      }

      rv = Adm_WriteData( Ctn, (Cmd[2]*256) + Cmd[3], &Cmd[5], Cmd[4] );
      if (rv != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_ICC_ERR_MEM / 256;
	Rsp[1] = CTBCS_ICC_ERR_MEM % 256;
      }	

      Rsp[0] = CTBCS_ICC_SUCCESS / 256;
      Rsp[1] = CTBCS_ICC_SUCCESS % 256;

      *Lr = 2;
      return OK;
      break;

    case 0x20:
      /* Verify - part 7 */

      if (Adm_IsICCPresent(Ctn) != STATUS_SUCCESS) {
	Rsp[0] = CTBCS_CT_ERR_REMOVED / 256;
	Rsp[1] = CTBCS_CT_ERR_REMOVED % 256;
	
	*Lr = 2;
	return OK;
      }

      rv = Adm_PresentCode( Ctn, &Cmd[5] );

      Rsp[0] = CTBCS_ICC_SUCCESS / 256;
      Rsp[1] = CTBCS_ICC_SUCCESS % 256;

      *Lr = 2;
      return OK;
      break;

    default:
      Rsp[0] = CTBCS_CT_ERR_INS / 256;
      Rsp[1] = CTBCS_CT_ERR_INS % 256;

      *Lr = 2;
      return OK;      
      break;

    }

  }

}

char CT_close ( unsigned short Ctn ) {

  ULONG rv;

  rv = Adm_UnInitialize(Ctn);

  if ( rv == STATUS_SUCCESS ) {
    return OK;   
  } else {
    return ERR_TRANS;
  }

}


