package terminal;

import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import javax.swing.*;

import opencard.core.event.*;
import opencard.core.service.*;
import opencard.core.terminal.*;
import opencard.opt.terminal.*;
import opencard.opt.util.*;

public class TableTerminal extends JPanel
{
   static final String TITLE = "Table terminal";
   static final byte[] TABLE_APPLET_AID = { 0x39, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01 };
   static final byte CLA_ISO = (byte)0x00;
   static final byte CLA_TABLE = (byte)0x39;

   JTextField setTextField, nextTextField;
   JButton setButton, nextButton;
   JTextArea statusTextArea;

   CardTerminal terminal;
   PassThruCardService service;

   public TableTerminal(JFrame parent) {
      buildGUI(parent);
      (new CardThread()).start();
   }

   void buildGUI(JFrame parent) {
      setLayout(new BorderLayout());
      statusTextArea = new JTextArea(20,30);
      add(new JScrollPane(statusTextArea),BorderLayout.CENTER);
      JPanel setPanel = new JPanel();
      setTextField = new JTextField(5);
      setButton = new JButton("Set");
      SetEventListener setEventListener = new SetEventListener();
      setTextField.addActionListener(setEventListener);
      setButton.addActionListener(setEventListener);
      setPanel.add(new JLabel("Set base to: "));
      setPanel.add(setTextField);
      setPanel.add(setButton);
      JPanel nextPanel = new JPanel();
      nextTextField = new JTextField(5);
      nextTextField.setEditable(false);
      nextButton = new JButton("Get");
      NextEventListener nextEventListener = new NextEventListener();
      nextTextField.addActionListener(nextEventListener);
      nextButton.addActionListener(nextEventListener);
      nextPanel.add(new JLabel("Get next value: "));
      nextPanel.add(nextTextField);
      nextPanel.add(nextButton);
      JPanel controlsPanel = new JPanel(new GridLayout(2,1));
      controlsPanel.add(setPanel);
      controlsPanel.add(nextPanel);
      add(controlsPanel,BorderLayout.SOUTH);
      parent.addWindowListener(new CloseEventListener());
   }

   class CardThread extends Thread
   {
      public void run() {
         try {
            SmartCard.start();
            CardTerminalRegistry terminalRegistry = CardTerminalRegistry.getRegistry();
            Enumeration terminals = terminalRegistry.getCardTerminals();
            terminal = (CardTerminal)terminals.nextElement();
            cardSession(CardRequest.ANYCARD);
            while (true) {
               cardSession(CardRequest.NEWCARD);
            }
         } catch (Exception e) {
            status(e.getMessage());
         }
      }

      void cardSession(int requestType) {
         try {
            CardRequest request =
               new CardRequest(requestType,terminal,PassThruCardService.class);
            SmartCard card = SmartCard.waitForCard(request);
            service =
               (PassThruCardService)card.getCardService(PassThruCardService.class,true);
            selectApplet(TABLE_APPLET_AID);
         } catch (Exception e) {
            status(e.getMessage());
         }
      }
   }

   class SetEventListener implements ActionListener
   {
      public void actionPerformed(ActionEvent ae) {
         try {
            byte base = (byte)(Integer.parseInt(setTextField.getText()));
            status("Attempting to set base to: " + (base & 0x000000FF) + "... ");
            if (service == null) {
               status("Could not send/receive APDU... ");
            } else {
               setBase(base);
            }
         } catch (NumberFormatException nfe) {
            status("NaN");
         } catch (Exception e) {
            status(e.getMessage());
         }
      }
   }

   class NextEventListener implements ActionListener
   {
      public void actionPerformed(ActionEvent ae) {
         try {
            status("Attempting to get next value... ");
            if (service == null) {
               status("Could not send/receive APDU... ");
            } else {
               int value = getNextValue();
               nextTextField.setText(Integer.toString(value));
            }
         } catch (Exception e) {
            status(e.getMessage());
         }
      }
   }

   class CloseEventListener extends WindowAdapter
   {
      public void windowClosing(WindowEvent we) {
         try {
            SmartCard.shutdown();
         } catch (Exception e) {
         } finally {
            System.exit(0);
         }
      }
   }

   void selectApplet(byte[] aid) throws CardTerminalException {
      byte cla = CLA_ISO;
      byte ins = (byte)0xA4;
      byte p1 = (byte)0x04;
      byte p2 = (byte)0x00;
      int le = 0; 
      byte[] data = aid;
      CommandAPDU capdu = new ISOCommandAPDU(cla,ins,p1,p2,data,le);
      ResponseAPDU rapdu = sendCommandAPDU(capdu);
   }

   void setBase(byte base) throws CardTerminalException {
      byte cla = CLA_TABLE;
      byte ins = (byte)0x05;
      byte p1 = (byte)0x00;
      byte p2 = (byte)0x00;
      int le = 0;
      byte[] data = { base };
      CommandAPDU capdu = new ISOCommandAPDU(cla,ins,p1,p2,data,le);
      ResponseAPDU rapdu = sendCommandAPDU(capdu);
   }

   int getNextValue() throws CardTerminalException, CardServiceException {
      byte cla = CLA_TABLE;
      byte ins = (byte)0x09;
      byte p1 = (byte)0x00;
      byte p2 = (byte)0x00;
      int le = 2;
      CommandAPDU capdu = new ISOCommandAPDU(cla,ins,p1,p2,le);
      ResponseAPDU rapdu = sendCommandAPDU(capdu);
      if (rapdu.sw() != 0x9000 || rapdu.data().length != 2) {
         throw new CardServiceException("Invalid response APDU from card... ");
      }
      byte[] data = rapdu.data();
      return ((data[0] & 0x000000FF) << 8) | (data[1] & 0x000000FF);
   }

   ResponseAPDU sendCommandAPDU(CommandAPDU capdu)
   throws CardTerminalException {
      status(capdu.toString());
      ResponseAPDU rapdu = service.sendCommandAPDU(capdu);
      status(rapdu.toString());
      return rapdu;
   }

   void status(String txt) {
      statusTextArea.append(txt + "\n");
   }

   public static void main(String[] arg) {
      JFrame frame = new JFrame(TITLE);
      Container c = frame.getContentPane();
      TableTerminal panel = new TableTerminal(frame);
      c.add(panel);
      frame.pack();
      frame.setVisible(true);
   }
}

