// Matt Edwards
// RGB LED color test (for MDE RGB board v1)
// Based on hello.array.44.c, "Charlieplex LED array hello-world", by Neil Gershenfeld

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input 
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set

#define led_port PORTA
#define input_port PORTA
#define input_pins PINA
#define led_direction DDRA
#define input_direction DDRA

#define input_pin1 (1 << PA6)
#define input_pin2 (1 << PA5)
#define input_pin3 (1 << PA4)

#define A (1 << PA0) // rail 1
#define B (1 << PA1) // rail 2
#define C (1 << PA2) // rail 3
#define D (1 << PA3) // rail 4

void rgbflash(uint8_t from, uint8_t to1, uint8_t to2, uint8_t to3, 
	      uint8_t Rpct, uint8_t Gpct, uint8_t Bpct, uint8_t cycles) {
  // source from, sink to, flash
  set(led_port,from);
  clear(led_port,to1);
  clear(led_port,to2);
  clear(led_port,to3);
  output(led_direction,from);

  for (uint8_t j = 0; j < cycles; ++j) {
    output(led_direction,to1);
    output(led_direction,to2);
    output(led_direction,to3);

    for (uint8_t i = 0; i < 100; ++i) {
      if (i >= Rpct) {
	clear(led_direction,to1);
      }
      if (i >= Gpct) {
	clear(led_direction,to2);
      }
      if (i >= Bpct) {
	clear(led_direction,to3);
      }
      _delay_us(1);
    }
  }

  input(led_direction,from);
  input(led_direction,to1);
  input(led_direction,to2);
  input(led_direction,to3);
}
int main(void) {
  // set clock divider to /1
  CLKPR = (1 << CLKPCE);
  CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

  set(input_port, input_pin1);
  set(input_port, input_pin2);
  set(input_port, input_pin3);

  input(input_direction, input_pin1);
  input(input_direction, input_pin2);
  input(input_direction, input_pin3);

  const uint8_t lights = 4;
  uint8_t redA[lights], greenA[lights], blueA[lights];

  for (uint8_t i = 0; i < lights-1; ++i) {
    redA[i] = 0;
    greenA[i] = 0;
    blueA[i] = 0;
  }

  redA[lights-1] = (rand() % 80);
  greenA[lights-1] = (rand() % 80);
  blueA[lights-1] = (rand() % 80);

  uint8_t red = 0, green = 0, blue = 0;
  uint8_t switch1 = 0, switch2 = 0, switch3 = 0;
  uint8_t targ = 0, all = 0, crazy = 0, temp = 0, MODE = 1, countdown = 0;

  while (1) {
    if (MODE) { // random color rotation
      switch1 = !pin_test(input_pins, input_pin1);
      switch2 = !pin_test(input_pins, input_pin2);
      switch3 = !pin_test(input_pins, input_pin3);
      
      if (switch1 || switch2 || switch3) {
	MODE = !MODE;
	countdown = 75;
      }

      for (uint8_t i = 0; i < lights-1; ++i) {
	redA[i] = redA[i+1];
	greenA[i] = greenA[i+1];
	blueA[i] = blueA[i+1];
      }

      redA[lights-1] += ((rand() % 31) - 5);
      greenA[lights-1] += ((rand() % 31) - 5);
      blueA[lights-1] += ((rand() % 31) - 5);
      
      redA[lights-1] %= 80;
      greenA[lights-1] %= 80;
      blueA[lights-1] %= 80;
      
      for (uint8_t rep = 0; rep <= 120; ++rep) {
	rgbflash(A, B, C, D, redA[3], greenA[3], blueA[3], 1);
	rgbflash(D, C, B, A, redA[2], greenA[2], blueA[2], 1);
	rgbflash(B, A, D, C, redA[1], greenA[1], blueA[1], 1);
	rgbflash(C, D, A, B, redA[0], greenA[0], blueA[0], 1);
      }
    } else { // interactive mode
      switch1 = !pin_test(input_pins, input_pin1);
      switch2 = !pin_test(input_pins, input_pin2);
      switch3 = !pin_test(input_pins, input_pin3);

      if (switch1 || switch2 || switch3) {
	countdown = 75;
      }
      if (switch1 && switch2 && switch3){
	crazy = !crazy;
      } else if (switch1 && switch3) {
	all = !all;
      } else if (switch1 && switch2){
	targ++;
      } else if (switch2 && switch3) {
	red = 0;
	green = 0;
	blue = 0;
      } else if (switch1) {
	red += 5;
      } else if (switch2) {
	green += 5;
      } else if (switch3) {
	blue += 5;
      }

      red = red % 90;
      green = green % 90;
      blue = blue % 90;
      
      targ = targ % 4;

      for (uint8_t rep = 0; rep <= 200; ++rep) {
	if (all) {
	  rgbflash(A, B, C, D, red, green, blue, 1);
	  if (crazy) {temp = red; red = green; green = blue; blue = temp;}
	  rgbflash(D, C, B, A, red, green, blue, 1);
	  if (crazy) {temp = red; red = green; green = blue; blue = temp;}
	  rgbflash(B, A, D, C, red, green, blue, 1);
	  if (crazy) {temp = red; red = green; green = blue; blue = temp;}
	  rgbflash(C, D, A, B, red, green, blue, 1);
	} else {
	  switch (targ) {
	  case 0: rgbflash(A, B, C, D, red, green, blue, 1); break;
	  case 1: rgbflash(D, C, B, A, red, green, blue, 1); break;
	  case 2: rgbflash(B, A, D, C, red, green, blue, 1); break;
	  case 3: rgbflash(C, D, A, B, red, green, blue, 1); break;
	  }
	}
      }
      countdown--;
      if (countdown <= 0) {
	MODE = !MODE;
      }
    }
  }
}
