#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

#include <util/delay.h>

#define LED_PORT PORTB
#define LED_DIR DDRB
#define LED_PINS PINB
#define PIN_RED (1 << PA5)
#define PIN_GREEN (1 << PA7)
#define PIN_BLUE (1 << PB2)
#define PIN_BTN (1 << PA0)

struct Vec3 {
  //Vec3(uint8_t x, uint8_t y, uint8_t z) : x(x), y(y), z(z) {}
  uint8_t x, y, z;

  inline void set(uint8_t x, uint8_t y, uint8_t z) { this->x = x; this->y = y; this->z = z; }

  inline uint8_t r(){ return x < 0 ? -x : x; }
  inline uint8_t g(){ return y < 0 ? -y : y; }
  inline uint8_t b(){ return z < 0 ? -z : z; }
};

struct RGBLED {
  static inline void set(uint8_t r, uint8_t g, uint8_t b) {
    OCR0A = b;
    OCR0B = g;
    OCR1BL = r;
    OCR1BH = 0;
  }
};

volatile uint8_t state = 0;

ISR(PCINT0_vect) {
  state++;
  state %= 8;
}

int main(void){
  // 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);

  // Enable pull-up resistor after tristate (see datasheet)
  PORTB |= PIN_BLUE;
  PORTA |= (PIN_RED | PIN_GREEN);
  // configure as output
  DDRB |= PIN_BLUE;
  DDRA |= (PIN_RED | PIN_GREEN);

  // configure as input, enable internal pullup
  PORTA |= PIN_BTN;
  DDRA &= ~PIN_BTN;

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

  sei();

  static const uint8_t MAX = 255;
  static const uint8_t MIN = 1;

  Vec3 o{MAX, MAX, MAX};

  while(1){
    RGBLED::set(o.r(), o.g(), o.b());
    switch(state){
      case 0:
        if(o.x > MIN)
          o.x -= 5;
        else
          state++;
        // 0 255 255
        break;
      case 1:
        if(o.y > MIN)
          o.y -= 5;
        else
          state++;
        // 0 0 255
        break;
      case 2:
        if(o.x < MAX)
          o.x += 5;
        else
          state++;
        // 255 0 255
        break;
      case 3:
        if(o.z > MIN)
          o.z -= 5;
        else
          state++;
        // 255 0 0
        break;
      case 4:
        if(o.y < MAX)
          o.y += 5;
        else
          state++;
        // 255 255 0
        break;
      case 5:
        if(o.x > MIN)
          o.x -= 5;
        else
          state++;
        // 0 255 0
        break;
      case 6:
        if(o.z < MAX)
          o.z += 5;
        else
          state++;
        // 0 255 255
        break;
      case 7:
        if(o.x < MAX)
          o.x += 5;
        else
          state = 0;
        // 255 255 255
        break;
    }

    _delay_ms(20);
  }
}