/**************************************************************************************************
*
*  StringSerialProtocol
*
*   Version:      1.1.3 - Mai 2009
*   Author:       Christoph Wartmann / chair for caad - ETH Zürich  /  wartmann[at]arch.ethz.ch
*                 Etienne Ribeiro    / tutorial assistant caad      /  eribeiro[at]ethz.ch
*
*   Desc:         A Protocoll to communicate through the Serial Interface. All data will be
*                 converted to Ascii-Code. This means an int number (2 bytes) now needs up to
*                 6 bytes and must be coded and decoded. On the other hand this protocol is very
*                 reliable even if you reset the board during communication.
*
*   Protocoll:    bits 0 to 8: command
*                 bits 8 to ...: message
*                 last bits: CR, LF or CR LF or LF CR
*
*   Methodes:     SerialProtocolLibrary (PApplet parent, String comPort)
*                   Constructor
*                   parent = use this when calling
*                   comPort = COM Port to connect to ("" means last comPort in list will be selected)
*                 SerialProtocolLibrary (PApplet parent, int iPort) {
*                   Constructor
*                   parent = use this when calling
*                   iPort = ID from COM Port to connect to (-1 means last comPort in list will be selected)
*                 void Serial_setID (char ID)
*                   If this function is called, the specified ID will be sent wenn calling Serial_sendMessage
*                   ID: a value from 0 to 254
*                   Note: ID = 0 means you send and receive commands to/from all nodes
*                   Note: NEVER USE 10 or 13 as NODE_ID!
*                 void send (char cmd, String message)
*                   Send an long number through serial.
*                   cmd = a char to seperate different commands. Don't use char with ascii 10 and 13
*                   message = long number to send (-2147483648 to 2147483647)
*                 void send (char cmd, int message)
*                   Send an int number through serial.
*                   cmd = a char to seperate different commands. Don't use char with ascii 10 and 13
*                   message = int number to send (-32768 to 32767)
*                 void sendToAllNodes (char cmd, String message)
*                   Sends a String to all nodes (Serial_setID() must be called in setup())
*                 void sendToAllNodes (char cmd, int message)
*                   Sends an int to all nodes (Serial_setID() must be called in setup())
*                 void sendToNode (int NodeID, char cmd, String message)
*                   Sends a String to specified Node (Serial_setID() must be called in setup())
*                   Note: you can use function send(..) if your sending messages to node ID specified in Serial_setID()
*                   Note: NEVER USE 10 or 13 as NODE_ID!
*                 void sendToNode (int NodeID, char cmd, int message)
*                   Sends an int to specified Node (Serial_setID() must be called in setup())
*                   Note: you can use function send(..) if your sending messages to node ID specified in Serial_setID()
*                   Note: NEVER USE 10 or 13 as NODE_ID!
*                 int parseInteger (String val)
*                   Parse string value to int. (-2147483648 to 2147483647)
*
*   Not implemented:
*      - Sending float values
*      - unsigned values
*
***************************************************************************************************/

import processing.serial.*;
import java.lang.reflect.*;



class SerialProtocolLibrary implements Runnable {



        // const

        final int MAX_COMMAND_LENGTH = 100;
        final int LF = 10;
        final int CR = 13;


        // var

        Serial port;
        char buffer[] = new char [MAX_COMMAND_LENGTH];
        int cBuffer = 0;
        int current_bytescount = 0; // length of current Command
        char current_command;
        // Callbacj
        Method SerialEventMethod1;
        Method SerialEventMethod2;
        // Parent
        PApplet parent;
        // Threading:
        Thread thread;
        int intervall = 25;
        //
        boolean useID = false;
        int myID = 0;



        // ------------------
        // Functions



        // Constructor

        SerialProtocolLibrary (PApplet parent, int iPort) {

                String comPort = "";
                if (iPort != -1)
                comPort = Serial.list()[iPort];

                ini (parent, comPort);

        }
        SerialProtocolLibrary (PApplet parent, String comPort) {

                ini (parent, comPort);

        }
        private void ini (PApplet parent, String comPort) {

                //
                this.parent = parent;


                // Connect to COM-Port
                println (Serial.list());
                if (comPort == null || comPort == "")
                comPort = Serial.list()[Serial.list().length - 1];
                try { port = new Serial(parent, comPort, 9600); } catch (Exception ex) { println("Problems connecting to Wiring-Board"); return; }
                println ("Connected to " + comPort);


                // Callback
                try {
                        String tmpstr = "";
                        SerialEventMethod1 = parent.getClass().getMethod("SerialEvent", new Class[] {tmpstr.getClass(), tmpstr.getClass()});
                } catch (Exception e) { }
                try {
                        String tmpstr = "";
                        SerialEventMethod2 = parent.getClass().getMethod("SerialEvent", new Class[] {tmpstr.getClass(), tmpstr.getClass(), tmpstr.getClass()});
                } catch (Exception e) { }


                
                thread = new Thread(this);
                thread.start();

        }
        // function to call check() each 'intervall' milliseconds
        public void run() {
                while (Thread.currentThread() == thread) {

                        try {
                                check();
                        } catch (Exception e) {
                                e.printStackTrace();
                                thread = null;
                        }
                        try {
                                Thread.sleep(intervall);
                        } catch (InterruptedException ex) { }

                }
        }





        // Set Id
        //   ID: a value from 0 to 254
        //   Note: ID = 0 means you send and receive commands to/from all nodes
        void setID (int ID) {

                useID = true;
                myID = ID;

        }


        // Send Message

        void send (char cmd, String message) { if (useID) port.write (myID); port.write (cmd); port.write (message); port.write(10); } // Strings
        void send (char cmd, int message) { if (useID) port.write (myID); port.write (cmd); port.write (str(message)); port.write(10); }
        void sendToAllNodes (char cmd, String message) { if (useID) port.write (0); port.write (cmd); port.write (message); port.write(10); } // Strings
        void sendToAllNodes (char cmd, int message) { if (useID) port.write (0); port.write (cmd); port.write (str(message)); port.write(10); }
        void sendToNode (int NodeID, char cmd) { if (useID) port.write (NodeID); port.write (cmd); port.write(10); } // Strings
        void sendToNode (int NodeID, char cmd, String message) { if (useID) port.write (NodeID); port.write (cmd); if (message != "") port.write (message); port.write(10); } // Strings
        void sendToNode (int NodeID, char cmd, int message) { if (useID) port.write (NodeID); port.write (cmd); if (str(message) != "") port.write (str(message)); port.write(10); }




        // Check if something is comming in. Must be called in loop.

        private void check () {

                while (port.available() > 0) {

                        // Fill buffer
                        int val = port.read();
                        buffer [cBuffer] = (char) val;


                        // Message complete
                        if (val == LF || val == CR) {

                                if (useID == false || (useID == true && myID == 0) || (useID == true && buffer[0] == myID)) { // if ID is not used or first byte = ID

                                        // First Byte
                                        int firstByte = 0;
                                        if (useID)
                                        firstByte = 1;


                                        // Callback
                                        if (SerialEventMethod1 != null && cBuffer > 0) {// if cBuffer == 0, no data sent (if you send LF CR or ...)

                                                try {
                                                        SerialEventMethod1.invoke(parent, new String[] {str(buffer[firstByte]), new String(buffer).substring(firstByte + 1, cBuffer) });
                                                } catch (Exception e) {}

                                        }
                                        if (SerialEventMethod2 != null && cBuffer > 0) {// if cBuffer == 0, no data sent (if you send LF CR or ...)

                                                try {
                                                        SerialEventMethod2.invoke(parent, new String[] {str(int(buffer[0])), str(buffer[1]), new String(buffer).substring(firstByte + 1, cBuffer) });
                                                } catch (Exception e) {}

                                        }

                                }


                                //
                                cBuffer = 0;
                                return;

                        }


                        // ++
                        cBuffer += 1;

                }

        }





        // *****************
        // Parse



        // parseInteger

        int parseInteger (String val) {

                int ival = 0;
                int sign = 1;

                for (int i=0;i<val.length();i++) {

                        if (val.charAt(i) == '-')
                        sign = -1;
                        if (val.charAt(i) >= 48 && val.charAt(i) <= 57)
                        ival +=  (val.charAt(i)-48) * pow(10,(val.length()-i-1));

                }


                // return
                return sign * ival;

        }




}