/***
 * File neve: TerminaltFogad.java
 * Szerző: Szatmáry Botond
 * Utolsó módosítás: 1998. jun. 15.
 * Leírás: Ez az osztály kommunikál a gyűrűn lévő alkalmazással. Ezen osztály
 * metódusain keresztül lehet gyűrűre adatokat másolni; onnan olvasni;
 * ellenörizni, hogy megfelelő alkalmazás van-e rátöltve; ellenőrizni, hogy
 * a kért művelet sikeresen végrehajtódott-e vagy sem.
 ***/

import java.util.*;
import javax.comm.*;
import java.smartcard.*;
import com.iButton.ifd.dallasIFD.DallasIFDDriver;

public class TerminaltFogad implements SmartCardListener
{
  // JiB Terminal applet CLA
  static final byte T_CLA = (byte) 0x81;
  
  // JiB Terminal applet utasitasok
  static final byte T_STORE = 1;
  static final byte T_READ  = 2;
  
  // Parameterek a T_STORE utasitashoz
  static final byte T_P1_SET_LENGTH  = 1;
  static final byte T_P1_NEXT_PACKET = 2;
  static final byte T_P1_LAST_PACKET = 3;
  
  // Konstansok iButton  applet selection
  static final int SELECT_CLA = 0;
  static final int SELECT_INS = 0xA4;
  static final int SELECT_P1  = 4;
  static final int SELECT_P2  = 0;
  
  static final int APDU_SEND_MAX = 110;
  
  // ISO 7816 SW for no error
  static final int SW_NO_ERROR = 0x9000;
  
  protected Slot    blueDot;
  protected Channel channel;
  protected DallasIFDDriver driver;
  protected boolean iButtonInserted, businessCardSelected;

  // Ezeket az adatokat tárolom majd a gyűrűn
  protected String name, group, road;
  
  public TerminaltFogad()
  {
    startPolling(null);
  }

  public TerminaltFogad(String deviceName)
  {
    startPolling(deviceName);
  }

  public boolean selectTerminal()
  {
    businessCardSelected = hasApplet("Terminal");
    return businessCardSelected;
  }

  public boolean isiButtonInserted()
  {
    return iButtonInserted;
  }

  public boolean isBusinessCardSelected()
  {
    return businessCardSelected;
  }
  
  public String getName()
  {
    return name;
  }

  public String getGroup()
  {
    return group;
  }

  public String getRoad()
  {
    return road;
  }

  public String getiButtonId()
  {
    String idString = new String("");
    int[] buttonId = driver.getiButtonId();
    
    if (buttonId != null)
      {
	for (int i = 0; i < buttonId.length; i++)
	  {
            if (buttonId[i] < 10)
	      idString += '0';
            idString += Integer.toHexString(buttonId[i]).toUpperCase();
	  }
      }
    
    return idString;
  }

  public String getAdapterId()
  {
    String idString = new String("");
    int[] adapterId = driver.getAdapterId();
    
    if (adapterId != null)
      {
	for (int i = 0; i < adapterId.length; i++)
	  {
            if (adapterId[i] < 10)
	      idString += '0';
            idString += Integer.toHexString(adapterId[i]).toUpperCase();
	  }
      }
    
    return idString;
  }

  public void setName(String name)
  {
    this.name = name;
  }
  
  public void setGroup(String group)
  {
    this.group = group;
  }
  
  public void setRoad(String road)
  {
    this.road = road;
  }
  
  public void cardInserted(SmartCardEvent event)
  {
    System.out.println("Java iButton inserted");
    blueDot = event.getSlot();
    try
      {
	channel = blueDot.openChannel();
	driver = (DallasIFDDriver) channel.getIFD();
	iButtonInserted = true;
      }
    catch  (IFDException e)
      {
	blueDot = null;
	e.printStackTrace();
      }
    catch (InvalidChannelException e)
      {
	blueDot = null;
	e.printStackTrace();
      }
    catch (SmartCardException e)
      {
	blueDot = null;
	e.printStackTrace();
      }
  }

  public void cardRemoved(SmartCardEvent event)
  {
    System.out.println("Java iButton removed");
    iButtonInserted = false;
    businessCardSelected = false;
    
    try
      {
	channel.close();
      }
    catch (IFDException e)
      {
	e.printStackTrace();
      }
    catch (InvalidChannelException e)
      {
	e.printStackTrace();
      }
    
    blueDot = null;
    driver = null;
  }
  
  public void unsolicitedAPDU(SmartCardEvent event)
  {
  }
  
  public void cardReset(SmartCardEvent event)
  {
  }
  
  public void extendedEvent(SmartCardEvent event)
  {
  }

  /**
   * Kiolvassa a Terminál tartalmát a Terminal applet-ről ami a Java iButton-on van
   */
  public void read() throws SmartCardException, TerminaltFogadException
  {
    byte[] buffer = new byte[0];
    CommandAPDU businessCardAPDU = new CommandAPDU((byte)T_CLA, (byte) T_READ, (byte)0, (byte)0, buffer, (byte)0);
    
    ResponseAPDU response = channel.sendAPDU(businessCardAPDU);
    validateResponseData(response);
    byteArrayToFields(response.getDataBytes());
  }

  /**
   * Kiírja az adatokat a Terminal applet-re (a Java iButton-ra)
   */
  public void store() throws SmartCardException, TerminaltFogadException
  {
    // Byte tömbbé alakítja az adatokat amit tárolni akar
    byte[] bcData = fieldsToByteArray();
    
    // Beallítja a hosszát
    byte[] buffer = new byte[2];
    buffer[0] = (byte) ((bcData.length & 0xFF00) >>> 8);
    buffer[1] = (byte) (bcData.length & 0xFF);
    CommandAPDU setLengthAPDU = new CommandAPDU((byte)T_CLA, (byte) T_STORE, (byte)T_P1_SET_LENGTH, (byte)0, buffer, (byte)0);
    
    validateResponseData(channel.sendAPDU(setLengthAPDU));
    
    int offset = 0;
    int bytesRemaining = bcData.length;
    int packetBytes;

    while (bytesRemaining > 0)
      {
	byte p1;
	if (bytesRemaining > APDU_SEND_MAX)
	  {
            packetBytes = APDU_SEND_MAX;
            p1 = T_P1_NEXT_PACKET;
	  }
	else
	  {
            packetBytes = bytesRemaining;
            p1 = T_P1_LAST_PACKET;
	  }
	
	byte[] packetData = new byte[packetBytes];
	for (int j = 0; j < packetBytes; j++)
	  packetData[j] = bcData[j+offset];
	offset += packetBytes;
	
	CommandAPDU storeAPDU = new CommandAPDU((byte)T_CLA, (byte) T_STORE, p1, (byte)0, packetData, (byte)0);
	
	validateResponseData(channel.sendAPDU(storeAPDU));
	bytesRemaining -= packetBytes;
      }
  }
  
  protected void validateResponseData(ResponseAPDU response) throws TerminaltFogadException
  {
    // Ellenőrzi a választ, hogy helyes-e?
    if (response.getSW() != SW_NO_ERROR)
      throw new TerminaltFogadException();
  }

  /**
   * Ha a bedugott iButton megfelelő appletet tartalmaz, akkor igazzal tér vissza,
   * különben hamis.
    */
  public boolean hasApplet(String appletName)
  {
    boolean selected = false;
    
    try
      {
	char[] appletNameChars = appletName.toCharArray();
	byte[] buffer = new byte[appletName.length()];

	for (int i = 0; i < buffer.length; i++)
	  buffer[i] = (byte) appletNameChars[i];
	
	CommandAPDU CoinPurseAPDU = new CommandAPDU((byte)SELECT_CLA,
						    (byte)SELECT_INS,
						    (byte)SELECT_P1,
						    (byte)SELECT_P2,
						    buffer,
						    (byte)0);
	
	ResponseAPDU response = channel.sendAPDU(CoinPurseAPDU);
	System.out.println(response.getSW());
	selected = (response.getSW() == SW_NO_ERROR) ? true : false;
      }
    catch  (SmartCardException  e)
      {
	System.out.println("APDU error");
	System.out.println("Exception: " + e.getMessage());
	e.printStackTrace();
      }
    
    return selected;
  }
  
  protected void startPolling(String nativeDevString)
  {
    addPort("LPT", "PORT_TYPE_LEGACY_PARALLEL", true);
    
    if (nativeDevString == null)
      {
	Enumeration allPorts = CommPortIdentifier.getPortIdentifiers();
	CommPortIdentifier portId;
	
	while (allPorts.hasMoreElements())
	  {
            portId = (CommPortIdentifier) allPorts.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
	      addPort(portId.getName(), "PORT_TYPE_SERIAL", false);
	  }
      }
    else
      {
	addPort(nativeDevString, "PORT_TYPE_LEGACY_SERIAL", false);
      }
  }
  
  protected void addPort(String portName, String portType, boolean firstPort)
  {
    IFDDriver dallasIFD = new DallasIFDDriver();
    Properties properties = new Properties();
    properties.put(dallasIFD.getClass().getName() + ".Port_Name", portName);
    properties.put(dallasIFD.getClass().getName() + ".Port_Type", portType);
    
    try
      {
	dallasIFD.setConfiguration(properties);
	IFDRegistry reg = IFDRegistry.getDefaultRegistry();
	reg.addIFD(dallasIFD);
	
	if (firstPort)
	  reg.addSmartCardListener(this);
      }
    catch  (IFDException e)
      {
	e.printStackTrace();
      }
  }
  
  protected void byteArrayToFields(byte[] ba)
  {
    if ((ba != null) && (ba.length > 1))
      {
	String bcString = new String(ba);
	
	int offset = 0;
	name = new String(ba, offset, bcString.indexOf(0, offset));
	offset += (name.length() + 1);
	group = new String(ba, offset, bcString.indexOf(0, offset) - offset);
	offset += (group.length() + 1);
	road = new String(ba, offset, bcString.length() - offset);
      }
  }
  
  protected byte[] fieldsToByteArray()
  {
    int totalLength = name.length() + group.length() + road.length();
    
    // 0-t használ elválasztójelnek
    totalLength += 6;
    
    byte[] bcData = new byte[totalLength];
    
    // Mindent beletesz egy byte tömbbe
    int i, index = 0;
    
    for (i = 0; i < name.length(); i++)
      bcData[i+index] = (byte) name.charAt(i);
    index += name.length();
    bcData[index++] = (byte) 0;
    
    for (i = 0; i < group.length(); i++)
      bcData[i+index] = (byte) group.charAt(i);
    index += group.length();
    bcData[index++] = (byte) 0;
    
    for (i = 0; i < road.length(); i++)
      bcData[i+index] = (byte) road.charAt(i);
    index += road.length();

    return bcData;
  }
}
