/*
 * Evil Copyright (c) 2015 Kevin Nygaard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Pinout for Stentura 200 (NOT FOR FLASH)
 * Ribbon
 * ||||||
 * ||||||
 * ||||||
 * xxxxxx (contacts facing up)
 *  \\\\\\_   Power +5V
 *   \\\\\_ 2 Test
 *    \\\\_ 3 Shift load
 *     \\\_ 4 Serial out
 *      \\_ 5 Clock
 *       \_   Ground
 */

/* IO Definitions STENTURA 200*/
/*
#define CLK      5
#define DATA_IN  4
#define LATCH_EN 3
#define TEST_EN  2
#define BAUD     115200
*/





/*
 * Pinout for ProCAT Flash
 * IO Board Pads 
 * (NOTE: THIS IS NOT THE RIBBON CABLE, that is different)
 * 
 * Pads are in two columns, 1,2,3 and 4,5,6. 1 being the square pad (visible from top).
 * 
 * 1 ??? (Must be test. Connects to Resistors)
 * 2 PWR
 * 3 GND
 * 
 * 4 Serial Out (Pin 9 of first IC)
 * 5 CLK (Clk rail)
 * 6 SH/LD (Shift Load rail)
 * 
 */


/* IO Definitions ProCAT Flash*/
#define CLK      5
#define DATA_IN  4
#define LATCH_EN 6
#define TEST_EN  2 //Can't put TEST on 1, which would be UART TX
#define BAUD     9600

#define DEBUG 0 //set to 1 when using Arduino to debug

#define DEBOUNCE_PERIOD 10



#define ARRAY_SIZE(array) \
    (sizeof(array) / sizeof(*array))

/*
 * Setup IO and serial connections
 */
void setup()
{
    // Initialize serial
    Serial.begin(BAUD);
    if(DEBUG){
      Serial.println("Hax Steno Begin");
    }
    // Setup IOs
    pinMode(CLK,      OUTPUT);
    pinMode(LATCH_EN, OUTPUT);
    pinMode(DATA_IN,  INPUT);
    //pinMode(TEST_EN,  OUTPUT); //Not needed

    // Drive known values to outputs
    digitalWrite(CLK,      LOW);
    digitalWrite(LATCH_EN, LOW);
    //digitalWrite(TEST_EN,  LOW); //Not needed
}

/*
 * Convert raw Stentura byte data into packed Gemini PR format
 *
 * Data that is seemingly skipped is rendundant, and as far as I can tell,
 * unnecessary
 *
 * Raw matrix mapping of Stentura 200 SRT from serial shift registers
 *  0: S-
 *  1: T-
 *  2: K-
 *  3: P-
 *  4: W-
 *  5: H-
 *  6: R-
 *  7: A-
 *  8: O-
 *  9: *
 * 10: -E
 * 11: -U
 * 12: -F
 * 13: -R
 * 14: -P
 * 15: -B
 * 16: -L
 * 17: -G
 * 18: -T
 * 19: -S
 * 20: -D
 * 21: -Z
 * 22: #
 * 23: N/A
 *
 * Gemini PR protocol (from plover source ./plover/machine/geminipr.py)
 *
 * In the Gemini PR protocol, each packet consists of exactly six bytes and the
 * most significant bit (MSB) of every byte is used exclusively to indicate
 * whether that byte is the first byte of the packet (MSB=1) or one of the
 * remaining five bytes of the packet (MSB=0). As such, there are really only
 * seven bits of steno data in each packet byte. This is why the STENO_KEY_CHART
 * below is visually presented as six rows of seven elements instead of six rows
 * of eight elements.
 * STENO_KEY_CHART = ("Fn" , "#" , "#" , "#" , "#" , "#"  , "#"  ,
 *                    "S-" , "S-", "T-", "K-", "P-", "W-" , "H-" ,
 *                    "R-" , "A-", "O-", "*" , "*" , "res", "res",
 *                    "pwr", "*" , "*" , "-E", "-U", "-F" , "-R" ,
 *                    "-P" , "-B", "-L", "-G", "-T", "-S" , "-D" ,
 *                    "#"  , "#" , "#" , "#" , "#" , "#"  , "-Z")
 *
 */

 //void construct_data(char raw_data[], char packed_data[])
//{
 //   packed_data[0] = 0x80;
   // packed_data[1] = (raw_data[ 0] << 6)  /* S- */
    //               | (raw_data[ 1] << 1)  /* T- */
    //               | (raw_data[ 2] << 3)  /* K- */
    //               | (raw_data[ 3] << 2)  /* P- */
    //               | (raw_data[ 4] << 0)  /* W- */
    //               | (raw_data[ 5] << 0); /* H- */

    //packed_data[2] = (raw_data[ 6] << 6)  /* R- */
    //               | (raw_data[ 7] << 5)  /* A- */
     //              | (raw_data[ 8] << 4)  /* O- */
     //              | (raw_data[ 9] << 3); /* *  */

    //packed_data[3] = (raw_data[10] << 3)  /* -E */
     //              | (raw_data[11] << 2)  /* -U */
      //             | (raw_data[12] << 1)  /* -F */
       //            | (raw_data[13] << 0); /* -R */

    //packed_data[4] = (raw_data[14] << 6)  /* -P */
     //              | (raw_data[15] << 5)  /* -B */
      //             | (raw_data[16] << 4)  /* -L */
       //            | (raw_data[17] << 3)  /* -G */
        //           | (raw_data[18] << 2)  /* -T */
         //          | (raw_data[19] << 1)  /* -S */
          //         | (raw_data[20] << 0); /* -D */

    //packed_data[5] = (raw_data[22] << 6)  /* #  */
     //              | (raw_data[21] << 0); /* -Z */
//}



 /* ProCAT Flash Mapping
  *  
  *  The ProCAT Flash is somewhat different. The above Stentura 200 will not work. Instead we have:
  *  
  *   * Raw matrix mapping of ProCAT Flash from serial shift registers
  *   * You can somewhat infer this from the PCB (cheat) by reading letter order, but not all are logical
 *  0: H- //
 *  1: W- //
 *  2: P- //
 *  3: K- //
 *  4: T- //changes marked with comments
 *  5: S- //
 *  6: #  //      //old was R- 
 *  
 *  7: A- 
 *  8: -R //
 *  9: F //
 * 10: -U //
 * 11: -E //
 * 12: * //
 * 13: -R
 * 
 * 14: -P
 * 15: R  //was -B
 * 16: -Z //
 * 17: -D //
 * 18: -S //
 * 19: -T //
 * 20: -G //
 * 
 * 21: -L ??
 * 22: #
 * 23: ????
 * 
 * need -B
  */
void construct_data(char raw_data[], char packed_data[])
{
    packed_data[0] = 0x80;
    packed_data[1] = (raw_data[ 5] << 6)  /* S- */ //Have to move arrays entries around here, if you are going to 
                   | (raw_data[ 4] << 4)  /* T- */ //change values. Need all 24 bits.
                   | (raw_data[ 3] << 3)  /* K- */
                   | (raw_data[ 2] << 2)  /* P- */
                   | (raw_data[ 1] << 1)  /* W- */
                   | (raw_data[ 0] << 0); /* H- */

    packed_data[2] = (raw_data[ 15] << 6)  /* R- */
                   | (raw_data[ 14] << 5)  /* A- */
                   | (raw_data[ 13] << 4)  /* O- */
                   | (raw_data[ 12] << 3); /* *  */

    packed_data[3] = (raw_data[11] << 3)  /* -E */
                   | (raw_data[10] << 2)  /* -U */
                   | (raw_data[9] << 1)  /* -F */
                   | (raw_data[8] << 0); /* -R */

    packed_data[4] = (raw_data[23] << 6)  /* -P */ // was 14
                   | (raw_data[22] << 5)  /* -B */  // was dupe 0
                   | (raw_data[21] << 4)  /* -L */  
                   | (raw_data[20] << 3)  /* -G */
                   | (raw_data[19] << 2)  /* -T */
                   | (raw_data[18] << 1)  /* -S */
                   | (raw_data[17] << 0); /* -D */

    packed_data[5] = (raw_data[6] << 6)  /* #  */ //
                   | (raw_data[16] << 0); /* -Z */
}               

/*
 * Read keystrokes and return them to the host
 */
void loop(){
    // Byte array of depressed keys, following mapping above
    // 0 means not pressed, 1 means pressed
    static char pressed_keys[24];

    // Keys packed in Gemini PR format, ready for transmission over serial
    static char packed_keys[6];

    // Chord has been started, but is not complete (all keys released)
    static char in_progress;

    // Send data to host over serial
    static char send_data;

     // Debounce counter
    static int debounce_count;



    int i;
    char pressed;
    char keys_down;


    //If keys have already been recorded into pressed_keys array
    if (send_data) {
        construct_data(pressed_keys, packed_keys);
        Serial.write(packed_keys, ARRAY_SIZE(packed_keys));

        /*
         * Data returned to host, reset stateful variables
         * Note that we don't need to do this in the setup, because they are all
         * static and guaranteed to be 0 by spec
         */
        memset(pressed_keys, 0, sizeof(pressed_keys));
        send_data   = 0;
        in_progress = 0;


    // Else, record keys into pressed_keys array
    } else {
        // Latch current state of all keys
        digitalWrite(LATCH_EN, HIGH);

        // Read all latched data
        keys_down = 0;
        for (i = 0; i < ARRAY_SIZE(pressed_keys); i++){
             
          
            /*
             * All inputs are pulled up. Pressing a key shorts the circuit to
             * ground.
             *
             * We invert the logic here to convert to more conventional positive
             * logic.
             */
            pressed          = !digitalRead(DATA_IN);

            //At this point, you are reading each individual bit in as a 1 if pressed.

            
            //debug
            if(pressed && DEBUG){
              Serial.print(pressed, BIN);
              Serial.print("Pressed was high for i = ");
              Serial.println(i);
            }
            
            // Once a key is pressed, it stays pressed until the chord is over
            // OR result to array
            pressed_keys[i] |= pressed;

            
            if (pressed) {
                keys_down   = 1;
                in_progress = 1;
            }

            /*
             * Toggle clock. Max frequency of shift register MM74HC165 is 30
             * MHz, so it can be switched without any delay by Arduino.
             */
            digitalWrite(CLK, HIGH);
            digitalWrite(CLK, LOW);
          
        }

        // Make latch transparent again to capture next set of keystrokes
        digitalWrite(LATCH_EN, LOW);

         if (keys_down) {
            debounce_count = 0;
        }

        // Return data to host when all keys have been released
        if (in_progress && !keys_down  && (++debounce_count >= DEBOUNCE_PERIOD)) {
            send_data = 1;
        }
    }
}
