#include <ctype.h>
#include "CDC.h"
#include "helpers.h"

uint8_t debug[8];

/* AVR313: Interfacing the PC AT Keyboard */
unsigned char edge, bitcount;

ISR(INT0_vect) {
    debug[0]++;
    static unsigned char data;
    
    if (!edge) { /* falling edge */
	if (bitcount < 11 && bitcount > 2) {
	    data = (data >> 1);
	    if (PIND & _BV(PD1)) {
		data |= 0x80;
	    }
	}
        EICRA = _BV(ISC01) | _BV(ISC00); /* int0 on rising edge */
	edge = 1;
    } else {
	if (--bitcount == 0) {
            debug[1] = data;
	    bitcount = 11;
	}
        EICRA = _BV(ISC01); /* int0 on falling edge */
	edge = 0;
    }
}

static volatile uint16_t data;
static volatile uint8_t clocks = 0;
static volatile uint8_t communication_inhibited = 0;

static inline void update_data(uint8_t b) {
    if (b) {
	DDRD &= ~_BV(PD2); /* in */
	PORTD |= _BV(PD2); /* up */
    } else {
	DDRD |= _BV(PD2); /* out */
	PORTD &= ~_BV(PD2); /* 0 */
    }
}

static inline void update_clock(uint8_t b) {
    if (b) {
	DDRD &= ~_BV(PD3); /* in */
	PORTD |= _BV(PD3); /* up */
    } else {
	DDRD |= _BV(PD3); /* out */
	PORTD &= ~_BV(PD3); /* 0 */
    }
}

static inline void handle_timer(void) {
    TCNT0 = 128;
    if (clocks == 0) {
	return;
    }
    if ((clocks & 3) == 1) {
	if ((PIND & _BV(PD3)) == 0 && clocks > 1) {
	    data = 1;
	    clocks = 1;
	    communication_inhibited = 1;
	}
	update_data(data & 1);
	data = data >> 1;
    }
    if ((clocks & 1) == 0) {
	update_clock(clocks & 2);
    }
    clocks--;
}

ISR(TIMER0_OVF_vect) {
    handle_timer();
}

static void handle_cmd(uint8_t cmd) {
    // (echo s00011100001; sleep 0.1; echo s00000111111s00011100001) > /dev/ttyS1
    if (cmd == 's') {
        uint8_t i;
        
        /* assert(clocks == 0); */
        data = _BV(11) /*extra*/;
        for (i = 0; i < 11; i++) {
            if (get_byte() == '1') {
                data |= _BV(i);
            }
        }
        communication_inhibited = 0;
        clocks = 11 * 4 + 1;
        while (clocks) {
        }
        put_byte(communication_inhibited ? 'E' : 'O');
        return;
    }
    if (cmd == '\n' || cmd == '\r') {
        return;
    }
    put_byte(cmd);
    put_byte('=');
    switch (cmd) {
    case 'b':
        reboot();
        break;
    case 'd':
        cmd = get_byte();
        if (cmd >= '0' && cmd <= '9') {
            print_bits(debug[cmd - '0']);
        }
        break;
    case 't':
    {
        uint8_t i;
        
        data = _BV(11) /*extra*/;
        for (i = 0; i < 11; i++) {
            if (get_byte() == '1') {
                data |= _BV(i);
            }
        }
        print_bits(data >> 8);
        print_bits(data & 0xff);
        break;
    }
    default:
        put_byte('?');
        break;
    }
    put_byte('\r');
    put_byte('\n');
}

TASK(CDC_Task) {
    if (USB_IsConnected) {
        Endpoint_SelectEndpoint(CDC_RX_EPNUM);
        while (byte_readable()) {
            handle_cmd(get_byte());
            Endpoint_SelectEndpoint(CDC_RX_EPNUM);
        }
    }
}

void CDC_Task_Init(void) {
    DDRD &= ~_BV(PD1); /* kbd_data in */
    DDRD &= ~_BV(PD0); /* kbd_clock in */
    PORTD |= _BV(PD1); /* pull up kbd_data */
    PORTD |= _BV(PD0); /* pull up kbd_clock */
    EICRA = _BV(ISC01); /* int0 on falling edge */
    EIMSK = _BV(INT0); /* enable int0 */
    bitcount = 11;
    edge = 0;

    /* ((8000000/256)*2)/4 = 15625 Hz */
    TCCR0B = _BV(CS00); /* CLK/1 */
    TIMSK0 = _BV(TOIE0); /* OVF */
}

// hitting space caused 33 interrupts
// read as 00101001 = 41 = 0x29 and is correct


// PD0 - SCL -   J4 pin 10 --- nappis-CLK,  female PS/2 pin 5
// PD1 - SDA -   J4 pin 9  --- nappis-DATA, female PS/2 pin 1
// PD2 - RXD -   J4 pin 8  --- PC-DATA,     male PS2/2 pin 1
// PD3 - (TXD) - J4 pin 7  --- PC-CLK,      male PS/2 pin 5
// GND - GND -   J4 pin 2  --- nappis-GND,  female PS/2 pin 3
//                      ja --- PC-GND,      male PS/2 pin 3
