#include "i2c.h"
#include "register_device.h"
#include <avr/interrupt.h>

// Define Registers that can be read via i2c, including callbacks
constexpr uint8_t nRegister = 5;
RegisterDevice<nRegister> registers;
using Callb = RegisterDevice<nRegister>::Callback<registers>;
i2c::Asynchronous_USI_Slave<Callb> slave(0x54);

// Define peripherals
using namespace peripherals;
using PIN_RED   = Pin<PORTA_t, DDRA_t, PINA_t, PA5>;
using PIN_GREEN = Pin<PORTA_t, DDRA_t, PINA_t, PA7>;
using PIN_BLUE  = Pin<PORTB_t, DDRB_t, PINB_t, PB2>;
using LED = RGBLED<PIN_RED, PIN_GREEN, PIN_BLUE>;

constexpr uint8_t n_colors = 0b111;
uint8_t color = 1;

// process i2c start signal
ISR(USI_START_vect) {
  slave.on_start();
}

// process single byte i2c transfer
ISR(USI_OVF_vect) {
  slave.on_transfer_complete();
}

// Change color on button press
bool triggered = false;
ISR(PCINT0_vect) {
  if(triggered) {
    triggered = false;
  } else {
    triggered = true;
    color++;
    if(color > n_colors) color = 1;
  }
}

int main(void) {
  // Setup Clock
  CLKPR = (1 << CLKPCE);
  CLKPR = 0;

  // Stop timer during configuration
  GTCCR |= (1 << TSM);

  // Fast PWM, Clear OCOA/OCOB on BOTTOM, set at match
  TCCR0A |= (1 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (0 << COM0B0) | (1 << WGM01) | (1 << WGM00);
  // Fast PWM, clock source is I/O clock/256
  TCCR0B |= (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00);
  
  // Always on
  OCR0A = 0xFF;
  OCR0B = 0xFF;

  // Same configuration as timer0
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (0 << WGM11) | (1 << WGM10);
  TCCR1B |= (0 << WGM13) | (1 << WGM11) | (0 << CS12) | (1 << CS11) | (0 << CS10);

  OCR1AL = 0xFF;
  OCR1BL = 0xFF;

  // start timer
  GTCCR &= ~(1 << TSM);

  registers[0] = 255;
  registers[1] = 0;
  registers[2] = 0;
  registers[3] = 3;
  registers[4] = 255;

  PORTA |= (1 << PA0);
  DDRA &= ~(1 << PA0);

  // Enable interrupts for port A
  GIMSK |= (1 << PCIE0);
  // Enable pin change interrupts for pin A7
  PCMSK0 |= (1 << PCINT0);

  slave.init();
  LED::init();

  sei();

  // Blink with LED in color defined by master
  uint8_t count = 1;
  uint8_t power = 255;
  for(;;) {
    count++;
    cli();
    switch(registers[3]) {
      case 0:
        power = count;
        break;
      case 1:
        if(count < 128) {
          power = 2 * count + 1;
        } else {
          power = 2 * (255 - count) + 1;
        }
        break;
      case 2:
        if(count < 128) {
          power = 255;
        } else {
          power = 0;
        }
        break;
      default:
        power = 255;
        break;
    }
    sei();
    //registers[0] = power * (color & 0x01);
    //registers[1] = power * ((color & 0x02) >> 1);
    //registers[2] = power * ((color & 0x04) >> 2);
    LED::set_color(registers[0], registers[1], registers[2]);
    if(registers[4]) {
      _delay_ms(3000/255);
    } else {
      _delay_ms(1000/255);
    }
  }
  return 0;
}