package fr.cryptis.rmiclient;

import java.io.IOException;
import java.util.List;

import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import com.sun.javacard.clientlib.CardAccessor;


public class PCSCAccessor implements CardAccessor {
	CommandAPDU capdu;
	ResponseAPDU rapdu;
	Card card;
	CardChannel channel;
	
	public PCSCAccessor(){
		TerminalFactory factory = TerminalFactory.getDefault();
		try {
			List<CardTerminal> terminals = factory.terminals().list();
			System.out.println("Terminals : "+ terminals);

			// get the first terminal
			CardTerminal terminal = terminals.get(0);

			// establish a connection with the card
			card = terminal.connect("*");
			System.out.println("card: " + card);

			channel = card.getBasicChannel();
		}catch (CardException e) {
			System.out.println(e.getMessage());
		}catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}finally{
		}
	}
	
	public void closeCard() {
			try {
				card.disconnect(true);
			} catch (CardException e) {
				System.out.println(e.getMessage());
			}catch(Exception e){
			e.printStackTrace();
			}
	}
	
	
	public byte[] exchangeAPDU(byte[] arg0) throws IOException {
		try{
			byte[] rapdu = channel.transmit(new CommandAPDU(arg0)).getBytes();
			//extraction de SW a la fin de rapdu
			byte[] sw = new byte[2];
			sw[0] = rapdu [rapdu.length-2];
			sw[1] = rapdu [rapdu.length-1];
			//mise du SW en tete de retour et copie du reste de rapdu.
			byte []retour = new byte [rapdu.length];
			retour[0]= sw[0];
			retour[1]= sw[1];
			for (int i = 0; i<rapdu.length-2; i++){
				retour[i+2] = rapdu[i];
			}
			/*
			 * elements pour débugger le protocole :
			 * 
			 */
			System.out.print("=>");
			System.out.println(ByteArrayToHexString(arg0));
			String s = ByteArrayToHexString(retour);

			System.out.println("<="+s);
			s = ByteArrayToHexString(sw);
			System.out.println("SW="+s); 

			return retour;
			
		}catch(CardException e){
			System.out.println(e.getMessage());
			return null;
		}
	}
	
	public String toString(){
		return "card accessor PCSC made in France";
	}
	
	   /**
     * Utility method to convert a byte array to a hexadecimal string.
     *
     * @param bytes Bytes to convert
     * @return String, containing hexadecimal representation.
     */
    public static String ByteArrayToHexString(byte[] bytes) {
        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        char[] hexChars = new char[bytes.length * 2]; // Each byte has two hex characters (nibbles)
        int v;
        for (int j = 0; j < bytes.length; j++) {
            v = bytes[j] & 0xFF; // Cast bytes[j] to int, treating as unsigned value
            hexChars[j * 2] = hexArray[v >>> 4]; // Select hex character from upper nibble
            hexChars[j * 2 + 1] = hexArray[v & 0x0F]; // Select hex character from lower nibble
        }
        return new String(hexChars);
    }
    
    /**
     * Utility method to convert a hexadecimal string to a byte string.
     *
     * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
     *
     * @param s String containing hexadecimal characters to convert
     * @return Byte array generated from input
     * @throws java.lang.IllegalArgumentException if input length is incorrect
     */
    public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException {
        int len = s.length();
        if (len % 2 == 1) {
            throw new IllegalArgumentException("Hex string must have even number of characters");
        }
        byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters
        for (int i = 0; i < len; i += 2) {
            // Convert each character into a integer (base-16), then bit-shift into place
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }	

}