#include <stdint.h>
#include <msp430.h>

/*
    Commands:

    0x0D  mute
    0x2A  
    0x2B  "Full"
    0x2C  PC
    0x2D  side-by-side?
    0x2F  "i"
    0x38  AV
*/

#define UART_BIT_TIME 104
#define UART_HALF_BIT_TIME 52

#define IR_PORT P1OUT
const uint8_t IR_BIT = 0x40;

const uint8_t RX_BIT = 0x04;

uint8_t rxbyte;
uint16_t rxbuf; // byte currently being received
uint8_t rxbit; // current bit
uint8_t rxdone; // done receiving byte

uint8_t state = 0;
uint8_t buf[20];
uint8_t start = 0;

void delay_ms(uint16_t ms) {
    while (ms--)
        __delay_cycles(1000);
}

void wait_for_button() {
    while (!(P1IN & 0x08)) ;
    delay_ms(20);
    while (P1IN & 0x08) ;
    delay_ms(20);
}

void philips_rc5_send(uint16_t data) {
    const uint16_t bit_cycles = 889 - 6;

    // Configure timer
    P1SEL |= IR_BIT;
    P1SEL2 |= IR_BIT;
    P1IFG &= ~IR_BIT;
    
    // Configure timer (36kHz, count up)
    TACTL = TASSEL1 | TACLR;
    TACCTL0 = 0;
    TACCTL2 = 0;
    TACCR0 = 27; // MHZ / 36000 - 1; // ~36kHz (round(1000000/36000) - 1)
    TACCR2 = 0;

    __delay_cycles(bit_cycles * 2);
    
    // Start timer
    TACTL |= MC_1;
    
    // Send bits
    uint8_t bit = 14;
    while (bit--) {
        if (data & (1 << 13)) {
            TACCTL2 = 0;
            __delay_cycles(bit_cycles);

            TACCTL2 = OUTMOD_4;
            __delay_cycles(bit_cycles);
        } else {
            TACCTL2 = OUTMOD_4;
            __delay_cycles(bit_cycles);

            TACCTL2 = 0;
            __delay_cycles(bit_cycles);
        }

        data <<= 1;
    }

    P1OUT &= ~IR_BIT;
    TACCTL2 = 0;
}

int16_t inc;
uint16_t bittime;
uint8_t fail;

int main() {
    bittime = UART_BIT_TIME;
    // Disable watchdog
    WDTCTL = WDTPW | WDTHOLD;

    // 1MHz internal timer
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    // Configure output port (timer output, minus SEL so interrupts work)
    P1SEL = 0;
    P1SEL2 = 0;
    P1OUT &= ~IR_BIT;
    P1DIR |= IR_BIT;
    
    // Configure RX port (software UART)

    P1IES |= RX_BIT;
    P1IFG &= ~RX_BIT;
    P1IE |= RX_BIT;

    P1DIR |= 0x01;
    P1OUT &= ~0x01;

    __bis_status_register(GIE);

    while (1) {
        // Wait for interrupt

        inc++;

        // ...in low power mode

        if (rxdone) {
            rxdone = 0;

            if (state == 0) {
                if (rxbyte == 0xFF)
                    state++;
            } else if (state == 1) {
                buf[0] = rxbyte;

                state++;
            } else {
                buf[1] = rxbyte;

                __disable_interrupt();
                philips_rc5_send((((uint16_t)buf[0]) << 8) | buf[1]);
                
                __enable_interrupt();

                state = 0;

                delay_ms(100);
                P1OUT |= 0x01;
                delay_ms(100);
                P1OUT &= ~0x01;
            }
        }

        if (fail)
            P1OUT |= 0x01;

        if (!rxdone)
            __bis_status_register(CPUOFF | GIE);
    }

    return 0;
}

void __attribute__((interrupt (PORT1_VECTOR))) p1_isr() {
    // Disable port interrupt
    P1IE &= ~RX_BIT;
    P1IFG &= ~RX_BIT;

    // Clear buffers
    rxbuf = 0;
    rxbit = 9;

    // Enable timer, start at half bit time
    TACTL = TASSEL_2 | MC_2;
    TACCR0 = TAR + UART_HALF_BIT_TIME;
    TACCTL0 = OUTMOD1 | CCIE;
}

void __attribute__((interrupt (TIMER0_A0_VECTOR))) ta0_isr() {
    TACCR0 += UART_BIT_TIME;

    if (rxbit == 0) {
        TACTL = TASSEL_2;
        TACCTL0 &= ~CCIE;
        
        P1IFG &= ~RX_BIT;
        P1IE |= RX_BIT;

        // start bit = 0, stop bit = 1
        if ((rxbuf & 0x201) == 0x200) {
            rxbyte = (rxbuf >> 1) & 0xFF;
            rxdone = 1;
        }

        __bic_status_register_on_exit(CPUOFF);
    } else {
        if (P1IN & RX_BIT)
            rxbuf |= 0x400;

        rxbuf >>= 1;

        rxbit--;
    }
}
