/**
 * Title:        Amulet 1/4 VGA LCD and Touchscreen Demo
 * Description:  Simple JStamp program which interracts with the Amulet serial 1/4 VGA LCD and touchscreen controller
 * Copyright:    Copyright (c) 2001
 * Company:      Systronix
 * @author BBoyes
 * @version
 */

import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;

import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.CommPortIdentifier;

import com.ajile.jcx.*;

/** Example class that uses javax.comm and serial events
  * This class implements SerialPortEventListener in
  * order to receive the DataAvailable event.
  */
public class vga_1_4 implements javax.comm.SerialPortEventListener {
  /** reference to the serial input stream */
  protected InputStream inStream;
  /** reference to the serial output stream */
  protected OutputStream outStream;

  /**
   * Number of bytes total in a message from the LCD. For example, a get byte
   * has the message type and two ascii chars of the message ID, total of 3 bytes.
   * Use a value of 0 to mean we are not currently building up a packet.
   * As soon as we receive the start byte we set msgSize to a nonzero value
   */
  private byte msgSize=0;

  /**
   * This is the value of the message if it is a byte, such as LCD doing a get byte
   * or sending an RPC 03, in this case the value is 0x03.
   */
  private byte msgValue=0;

  /**
   *
   */
  private byte msgIndex=0;

  /**
   * The incoming data buffer, will hold request from the LCD
   */
  static byte[] readBuffer = new byte[10];

  final Motor mElbow = new Motor(Motor.MOTOR_PORT_0);
  final Motor mWrist = new Motor(Motor.MOTOR_PORT_1);

  /** Main entry point for the program.<br>
    * Initializes the serial port, sends a message, then blocks.
    *
    * Serial data will be echoed via the SerialThread calling
    * SerialEvent() method with DataAvailable messages.
    *
    * @params args not used
    */
  public  static void main (String [] args) {

    vga_1_4 vga_1_4 = new vga_1_4();
    vga_1_4.initializeSerialPort();
    vga_1_4.sendMessage("1/4 VGA LCD UI & JCX Lego Example bab 1.1");
    System.out.println("1/4 VGA LCD UI & JCX Legos Example bab 1.1");

    // block this thread
    // the serial events handler will be called
    // from the serialThread
    vga_1_4.blockThread();

  } // end main

  /**
    * method to block a thread
    */
  public synchronized void blockThread () {
    try {
      wait();
    } catch (InterruptedException ie) {
    }
  }

  /**
    * Sends a string out the serial port.
    *
    * @param msgString is the string to be send out the serial port
    */
  public void sendMessage(String msgString) {
    try {
      outStream.write(msgString.getBytes());
    } catch (IOException ioe) {
      System.out.println("IOException caught in app.sendMessage");
    }
  }

  /** Method that reads the input stream and echo's the data out the uart.<br>
    * If no data is available it will block waiting for incomming data
    *
    * What we want here is to look for a variable length packet from the LCD,
    * build up an array until the packet is complete, then process it and start
    * all over. Packets come at most every 100 msec or so. The Client Start of
    * Message values 0x11-0x15 cannot occur in the message body and indicate
    * an error if they do.
    *
    * Communication is half duplex. The LCD is considered the master.
    * LCD Message type bytes are 0x11-0x15. These are followed by two or more
    * ASCII bytes which should only have the values 0x20-0x7F.
    * So we can start a packet receipt by looking for 0x11-0x15.
    *
    * 0x10 is the acknowledge message and has no other data following.
    * The reply starts with the message type but with the 0x11 changed to 0x21,
    * then followed by appropriate data.
    *
    * 0x14 is the RPC message followed by the RPC Hi and Lo nibbles.
    * Response to the RPC should be 0x24 followed by the RPC Hi and Lo nibbles.
    * An action button such as "lower arm" could use this RPC.
    *
    * @params inStream a handle to the input stream from the serial port
    * @params outStream a handle to the output stream from the serial port
    */
  public void echo (InputStream inStream,OutputStream outStream) {
    try {
      byte rcvVal = (byte) inStream.read(); // blocking call
      /*
        switch (rcvVal)
        {
        case 0x11:
        case 0x12:
        case 0x13:
        case 0x14:
        case 0x15:
          msgIndex=0;
          break;
        default:
          break;
        } // end first switch-case
        */
      switch (rcvVal) {
      case 0x11:
        // get byte, LCD wants a byte value returned
        msgSize = 3;
        msgIndex=0;
        readBuffer[msgIndex++] = rcvVal;
        break;
      case 0x12:
        // get string, LCD wants a null-terminated string.
        break;
      case 0x13:
        // set byte, LCD sends a byte value to controller
        break;
      case 0x14:
        // Remote Procedure Call, LCD sends the RPC value
        msgSize = 3;
        msgIndex=0;
        readBuffer[msgIndex++] = rcvVal;
        //System.out.println("RPC start");
        break;
      case 0x15:
        // LCD get word, wants a 2-byte value returned H:L
        break;
      default:
        // must be part of the data payload of a message in progress
        //
        if (msgIndex <= readBuffer.length)
          {
          readBuffer[msgIndex++] = rcvVal;
          // do we have a complete packet?
          if (msgIndex == msgSize)
            {
            // we have a complete packet
            System.out.println("Complete packet found");
            processMsg();
            }
          }
        else
          {
          // we have way more chars than we should -- error, start over
          System.out.println("Bad packet - too long");
          msgIndex = 0;
          }
        break;
      } // end switch-case
      //outStream.write(rcvVal);
    } catch (IOException e) {
      System.out.println("IOException caught in echo");
    } // end catch
  } // end echo

  /**
   * Process the message, which exists in readBuffer
   */
  public void processMsg () {
    msgIndex=0;
    switch (readBuffer[msgIndex++])
    {
      case 0x11:
      case 0x12:
        break;
      case 0x14:
        /** RPC
         *  convert ASCII RPC number to byte
         */
        //msgValue =  ((readBuffer[1] - 0x30) << 4) + (readBuffer[2] - 0x30);
        msgValue = ascHex (readBuffer, (byte) 1);
        // send acknowledgement
        readBuffer[0] = (byte) 0x24;
        try {
          outStream.write(readBuffer, 0, msgSize);
          System.out.println("Processed RPC " + msgValue);
          } catch (IOException e) {
            System.out.println("IOException caught in RPC process");
            }
          // use the RPC value to cause some action
          switch (msgValue)
          {
          case 0x00:
            System.out.println("Elbow Hold");
            mElbow.setState(Motor.MOTOR_BRAKE);
            break;
          case 0x01:
            System.out.println("Elbow Up");
            if (Motor.MOTOR_REVERSE == mElbow.getState()) {
              mElbow.setState(Motor.MOTOR_BRAKE);
              }
            else {
              mElbow.setState(Motor.MOTOR_REVERSE);
              }
            break;
          case 0x02:
            System.out.println("Elbow Down");
            if (Motor.MOTOR_FORWARD == mElbow.getState()) {
              mElbow.setState(Motor.MOTOR_BRAKE);
              }
            else {
              mElbow.setState(Motor.MOTOR_FORWARD);
              }
            break;
          case 0x10:
            System.out.println("Wrist Hold");
            mWrist.setState(Motor.MOTOR_BRAKE);
            break;
          case 0x11:
            System.out.println("Wrist Open");
            if (Motor.MOTOR_REVERSE == mWrist.getState()) {
              mWrist.setState(Motor.MOTOR_BRAKE);
              }
            else {
              mWrist.setState(Motor.MOTOR_REVERSE);
              }
            break;
          case 0x12:
            System.out.println("Wrist Close");
            if (Motor.MOTOR_FORWARD == mWrist.getState()) {
              mWrist.setState(Motor.MOTOR_BRAKE);
              }
            else {
              mWrist.setState(Motor.MOTOR_FORWARD);
              }
            break;
          case 0x20:
            System.out.println("All STOP");
            mWrist.setState(Motor.MOTOR_BRAKE);
            mElbow.setState(Motor.MOTOR_BRAKE);
            break;
          default:
            System.out.println("Unknown RPC " + msgValue);
            mWrist.setState(Motor.MOTOR_BRAKE);
            mElbow.setState(Motor.MOTOR_BRAKE);
            break;
          }
        break;
      case 0x15:
        break;
      default:
        // error, should only have a good packet here
        break;
    } //end switch

  } // end processMsg

  private byte ascHex (byte [] barray, byte offset)
  {
  byte result;

  result = (byte) ((barray[offset] - 0x30) << 4);
  result += (byte) (barray[offset+1] - 0x30);
  return result;
  }

  /** Javax.comm will call this method whenever a Data Available event occurs
    *
    * @params ev this is the serial event it tells us what kind of
    *   event occurred
    */
  public void serialEvent(SerialPortEvent ev) {
    try {

      // only event supported on the aj100
      if (ev.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

        try {
          while (inStream.available() > 0) {
            //int numBytes = inStream.read(readBuffer);
            echo(inStream,outStream);
            }
        } catch (IOException e) {
          System.out.println("IOException caught in serialEvent");
          }
        //  echo(inStream,outStream);
       }

    } catch (Exception e) {
      System.out.println("Exception caught outside serialEvent");
    }

  } // end serialEvent


  /** Method to initialize the serial port (9600N81)and set up the
    * input and output streams. If it is unable
    * to aquire the serial port, the program will exit.
    *
    * It demostrates how to get a hold of the serial input
    * and output streams using javax.comm. It also registers this class as
    * a listener for the data available events with Javax.comm.
    */
  public void initializeSerialPort() {
    SerialPort serialPort;
    CommPortIdentifier commPortId;

    try {
      commPortId = CommPortIdentifier.getPortIdentifier("com1");
      if (!commPortId.isCurrentlyOwned()) {
        if (commPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
          try {
            // Open the serial port. The name is unimportant,
            // The second (timeout) parameter is unimplemented
            serialPort = (SerialPort) commPortId.open("serialExample2App",0);

            // Amulet best at 115200, single baud rate for use or programming
            serialPort.setSerialPortParams(115200,
                                           javax.comm.SerialPort.DATABITS_8,
                                           javax.comm.SerialPort.STOPBITS_1,
                                           javax.comm.SerialPort.PARITY_NONE);

            // Register for notification of data changes
            try {serialPort.addEventListener(this); } catch (Exception tml) {}
            serialPort.notifyOnDataAvailable(true); // currently only DataAvailable is supported

            // Get output and input streams
            outStream = serialPort.getOutputStream();
            inStream = serialPort.getInputStream();

          } catch (javax.comm.UnsupportedCommOperationException ucoe) {
              System.out.println("Error: UnsupportedCommOperationException");
              System.exit(1);
          } catch (javax.comm.PortInUseException piue) {
              System.out.println("Error: PortInUseException");
              System.exit(1);
          } catch (java.io.IOException ioe) {
              System.out.println("Error: IOException");
              System.exit(1);
          } // end catch block
        } else {
          System.out.println("Error: comm port is not a serial port");
          System.exit(1);
        } // end if port is serial
      } //  end if port is not owned
    } catch (javax.comm.NoSuchPortException nsp) {
        System.out.println("NoSuchPortException");
        System.exit(1);
    }

  } // end initializeSerialPort

} // end vga_1_4