/**
 * \file firmware/modelsuntype5.c
 * \brief Hardware specific part for Sun Type 5 keyboard
 * \author Ronald Schaten <ronald@schatenseite.de>
 * \version $Id$
 *
 * License: GNU GPL v2 (see License.txt)
 */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>

#include "keycodes.h"
#include "tools.h"
#include "modelinterface.h"
#include "usbdrv.h"

/* ----------------------- hardware I/O abstraction ------------------------ */

#define PORTLEDS    PORTB  ///< port on which the LEDs are connected
#define PINLEDS     PINB   ///< port on which the LEDs are connected
#define DDRLEDS     DDRB   ///< port on which the LEDs are connected
#define LEDSCROLL   PINB7  ///< address of the scroll-lock LED
#define LEDNUM      PINB6  ///< address of the num-lock LED
#define LEDCOMP     PINB5  ///< address of the compose LED
#define LEDCAPS     PINB4  ///< address of the caps-lock LED

#define SRCLOCKON    PORTC |= (1 << PC5)
#define SRCLOCKOFF   PORTC &= ~(1 << PC5)
#define SRDATAON     PORTC |= (1 << PC6)
#define SRDATAOFF    PORTC &= ~(1 << PC6)
#define SRSTROBEON   PORTC |= (1 << PC7)
#define SRSTROBEOFF  PORTC &= ~(1 << PC7)

uint16_t curmatrix[22];  ///< contains current state of the keyboard
uint16_t oldmatrix[22];  ///< contains old state of the keyboard
uint16_t ghostmatrix[22]; ///< contains pressed keys that belong to a ghost-key situation

void hardwareInit(void) { 
    // configure ports
    DDRA = 0x00;
    PORTA = 0xff;
    DDRB = (1 << PB4) |(1 << PB5) | (1 << PB6) | (1 << PB7);
    PORTB = (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3);
    DDRC = (1 << PC5) | (1 << PC6) | (1 << PC7);
    PORTC = (1 << PC0) | (1 << PC1) | (1 << PC2) | (1 << PC3) | (1 << PC4);
    DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7));
    PORTD |= (1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7);

    DDRD |= (1 << PD0) | (1 << PD2); // needed for USB reset
    _delay_us(11);      // delay >10ms for USB reset
    DDRD &= ~((1 << PD0) | (1 << PD2)); // remove USB reset condition

    // configure timer 0 for a rate of 12M/(1024 * 256) = 45.78Hz (~22ms)
    TCCR0 = 5;          // timer 0 prescaler: 1024

    // blink, to indicate power-on
    PORTLEDS &= ~((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL) | (1 << LEDCOMP));
    _delay_ms(50);
    PORTLEDS |= ((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL) | (1 << LEDCOMP));

    // clean shift registers -- set all row lines to 1, except for KEYROW
    SRDATAON;
    SRSTROBEOFF;
    for (uint8_t i = 0; i < 21; i++) {
        SRCLOCKON; SRCLOCKOFF;
    }
    SRSTROBEON; SRSTROBEOFF;
}

/**
 * Print the current state of the keyboard in a readable form. This function
 * is used for debug-purposes only.
 */
void printMatrix(void) {
    for (uint8_t i = 0; i < 22; i++) {
        char buffer[10];
        sprintf(buffer, "%4x", ~(curmatrix[i] | 0xe000));
        for(int j= 0; j < strlen(buffer); j++) {
            if(buffer[j] == '0')
                buffer[j]= ' ';
        }
        sendString(buffer);
        if (i == 11) {
            sendString(":");
        } else {
            sendString(".");
        }
    }
    sendString("\n");
}

void toggle(void) {
    // not used in this model/version
}

void setSpeed(uint8_t speed) {
    // not used in this model/version
}

void setLeds(uint8_t LEDstate) {
    if (LEDstate & LED_NUM) { // light up num lock
        PORTLEDS &= ~(1 << LEDNUM);
    } else {
        PORTLEDS |= (1 << LEDNUM);
    }
    if (LEDstate & LED_CAPS) { // light up caps lock
        PORTLEDS &= ~(1 << LEDCAPS);
    } else {
        PORTLEDS |= (1 << LEDCAPS);
    }
    if (LEDstate & LED_SCROLL) { // light up scroll lock
        PORTLEDS &= ~(1 << LEDSCROLL);
    } else {
        PORTLEDS |= (1 << LEDSCROLL);
    }
    if (LEDstate & LED_COMPOSE) { // light up compose
        PORTLEDS &= ~(1 << LEDCOMP);
    } else {
        PORTLEDS |= (1 << LEDCOMP);
    }
}

/**
 * The keymatrix-array contains positions of keys in the matrix. Here you can
 * see which row is connected to which column when a key is pressed. This array
 * probably has to be modified if this firmware is ported to a different
 * keyboard.
 * \sa modmatrix
 */
const uint8_t PROGMEM keymatrix[22][13] = {
  // 0             1             2             3             4               5                            6               7                   8             9               10                  11             12
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_dot,                     KEY_F8,         KEY_F10,            KEY_Reserved, KEY_9,          KEY_minus,          KEY_P,         KEY_K},                 //  0
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_slash,                   KEY_equals,     KEY_F11,            KEY_Reserved, KEY_0,          KEY_lbracket,       KEY_semicolon, KEY_L},                 //  1
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_LeftArrow,  KEY_DownArrow,               KEY_grave,      KEY_F12,            KEY_Reserved, KEY_Euro,       KEY_DELETE,         KEY_rbracket,  KEY_apostroph},         //  2
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_RightArrow, KEY_UpArrow,                 KEY_Insert,     KEY_PrintScreen,    KEY_Reserved, KEY_NumLock,    KEY_DeleteForward,  KEY_Return,    KEY_KP4},               //  3
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KP2,        KEY_KP5,                     KEY_ScrollLock, KEY_Mute,           KEY_Reserved, KEY_KPslash,    KEY_Home,           KEY_End,       KEY_KP7},               //  4
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KP3,        KEY_KP6,                     KEY_Pause,      KEY_Volume_Down,    KEY_Reserved, KEY_KPasterisk, KEY_PageUp,         KEY_PageDown,  KEY_KP8},               //  5
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KPcomma,    KEY_KP0,                     KEY_KPminus,    KEY_Volume_Up,      KEY_Reserved, KEY_KP9,        KEY_KPplus,         KEY_KPenter,   KEY_KP1},               //  6
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  7
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Application /*compose*/, KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  8
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Power,    KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  9
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  10
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  11
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_capslock,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  12
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Find,       KEY_Select /*open*/,         KEY_ESCAPE,     KEY_Help,           KEY_Reserved, KEY_Stop,       KEY_Menu /*props*/, KEY_Tab,       KEY_Execute /*front*/}, //  13
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  14
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Cut,        KEY_Paste,                   KEY_1,          KEY_Cancel /*any*/, KEY_Reserved, KEY_Again,      KEY_Undo,           KEY_Q,         KEY_Copy},              //  15
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Z,                       KEY_2,          KEY_F1,             KEY_Reserved, KEY_3,          KEY_E,              KEY_W,         KEY_D},                 //  16
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_C,          KEY_X,                       KEY_4,          KEY_F2,             KEY_Reserved, KEY_5,          KEY_R,              KEY_F,         KEY_A},                 //  17
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_V,          KEY_N,                       KEY_F3,         KEY_F5,             KEY_Reserved, KEY_6,          KEY_T,              KEY_G,         KEY_S},                 //  18
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_B,          KEY_M,                       KEY_F4,         KEY_F6,             KEY_Reserved, KEY_7,          KEY_U,              KEY_Y,         KEY_H},                 //  19
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved,   KEY_Reserved,                KEY_Reserved,   KEY_Reserved,       KEY_Reserved, KEY_Reserved,   KEY_Reserved,       KEY_Reserved,  KEY_Reserved},          //  20
    {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Spacebar,   KEY_comma,                   KEY_F7,         KEY_F9,             KEY_Reserved, KEY_8,          KEY_O,              KEY_I,         KEY_J},                 //  21
};

/**
 * The modmatrix-array contains positions of the modifier-keys in the matrix.
 * It is built in the same way as the keymatrix-array.
 * \sa keymatrix
 */
const uint8_t PROGMEM modmatrix[22][13] = { // contains positions of modifiers in the matrix
  // 0                1              2             3               4             5              6         7         8         9         10        11        12
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  0
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  1
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  2
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  3
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  4
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  5
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  6
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_ALT_RIGHT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  7
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  8
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  9
    {MOD_NONE,        MOD_GUI_RIGHT, MOD_GUI_LEFT, MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  10
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_ALT_LEFT, MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  11
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  12
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  13
    {MOD_SHIFT_RIGHT, MOD_NONE,      MOD_NONE,     MOD_SHIFT_LEFT, MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  14
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  15
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  16
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  17
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  18
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  19
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_CONTROL_LEFT}, //  20
    {MOD_NONE,        MOD_NONE,      MOD_NONE,     MOD_NONE,       MOD_NONE,     MOD_NONE,      MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE},         //  21
};

/**
 * Checks if more than one bit in data is set.
 * \param data value to check
 * \return true if more than one bit is set
 */
static uint8_t bitcount2(uint16_t data) {
    data &= (data - 1);
    return data != 0;
}

/**
 * check if reportBuffer contains the key
 * \param buffer buffer to check
 * \param key key to search
 * \return 1 if buffer contains key, 0 otherwise
 */
static uint8_t bufferContains(uint8_t* buffer, uint8_t key) {
    for (uint8_t i = 2; i < sizeof(buffer); i++) {
        if (buffer[i] == key) {
            return 1;
        }
    }
    return 0;
}

/**
 * Scan and debounce keypresses. This is the main worker function for normal
 * keyboard operation, the code contains lot of comments. Basically, it first
 * scans the keyboard state. If a change is detected, it initializes a counter
 * that is decreased each time this function is called. If the counter reaches
 * 1, that means that the same scan result has been scanned ten times in a row,
 * so we can be pretty sure that the keys are in a certain state (as in: not
 * bouncing). Then, the codes for keys and modifiers are searched from the two
 * arrays, the USB-message to send the state is prepared. The return value of
 * this function indicates if the message has to be sent.
 * \return flag to indicate whether something has changed
 */
uint8_t scankeys(uint8_t* reportBuffer, uint8_t* oldReportBuffer, uint8_t sizeOfReportBuffer) {
    static uint8_t debounce = 5;
    uint8_t retval = 0;
    SRDATAOFF; // mark first bit as the active one
    SRCLOCKON; SRCLOCKOFF; // trigger clock to shift first bit into the register
    SRDATAON; // all further bits will be inactive
    for (uint8_t row = 0; row < 22; row++) {
        SRSTROBEON; SRSTROBEOFF; // copy current register values to output
        _delay_us(30);
        uint16_t data = ((PINC & 0x1f) << 8) | PINA; // we need the lower five bits of PINC and PINA
        if (data != curmatrix[row]) {
            // if a change was detected
            debounce = 10; // activate debounce counter
            curmatrix[row] = data; // and store the result
            ghostmatrix[row] = 0;
        }
        SRCLOCKON; SRCLOCKOFF; // trigger clock to shift register values
    }
    if (debounce) {
        // Count down, but avoid underflow
        debounce--;
    }
    if (debounce == 1) {
        memset(ghostmatrix, 0, sizeof(ghostmatrix));
        for (uint8_t i = 0; i < 21; i++) {
            uint16_t keys = (~curmatrix[i]) & 0x1fff;
            if (bitcount2(keys)) { // check if 2 or more keys are pressed
                for (uint8_t j = i + 1; j < 22; j++) {
                    uint16_t common_columns = keys & (~curmatrix[j]);
                    if (bitcount2(common_columns)) { // 2 or more columns in common => ghostkeys
                        ghostmatrix[i] |= common_columns;
                        ghostmatrix[j] |= common_columns;
                    }
                }
            }
        }
        // debounce counter expired, create report
        uint8_t reportIndex = 2; // reportBuffer[0] contains modifiers
        memset(reportBuffer, 0, sizeOfReportBuffer); // clear report buffer
        for (uint8_t row = 0; row < 22; row++) { // process all rows for key-codes
            uint16_t data = curmatrix[row]; // restore buffer
            if (data != 0x1fff) { // anything on this row? - optimization
                for (uint8_t col = 0; col < 13; col++) { // check every bit on this row
                    uint8_t key, modifier, isghostkey;
                    if (!(data & (1 << col))) {
                        key = pgm_read_byte(&keymatrix[row][col]);
                        modifier = pgm_read_byte(&modmatrix[row][col]);
                        isghostkey = (ghostmatrix[row] & (1 << col)) != 0;
                    } else {
                        key = KEY_Reserved;
                        modifier = MOD_NONE;
                        isghostkey = 0x00;
                    }
                    if (key != KEY_Reserved) { // keycode should be added to report
                        if (reportIndex >= sizeOfReportBuffer) { // too many keycodes
                            if (!retval & 0x02) { // Only fill buffer once
                                memset(reportBuffer+2, KEY_ErrorRollOver, sizeOfReportBuffer-2);
                                retval |= 0x02; // continue decoding to get modifiers
                            }
                        } else {
                            if (isghostkey) {
                                // we're in a ghost-key situation
                                if (bufferContains(oldReportBuffer, key)) {
                                    // this key has been pressed before, so we still send it
                                    reportBuffer[reportIndex] = key; // set next available entry
                                    reportIndex++;
                                }
                            } else {
                                reportBuffer[reportIndex] = key; // set next available entry
                                reportIndex++;
                            }
                        }
                    }
                    if (modifier != MOD_NONE) { // modifier should be added to report
                        reportBuffer[0] |= modifier;
                    }
                }
            }
        }
        retval |= 0x01; // must have been a change at some point, since debounce is done
    }
    return retval;
}

