// Include Dateien

#include <stdlib.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <math.h>


// Port zum übernehmen der neuen Daten (RCLK vom SR) und zur Ebenenansteuerung
#define PORT_STORE PORTA
#define DDR_STORE DDRA
#define SR_STORE PA4 // SR Store Data (RCLK)
#define LAYER1 PA0   // Layer 1 (unten) enable
#define LAYER2 PA1   // Layer 2 enable
#define LAYER3 PA2   // Layer 3 enable
#define LAYER4 PA3   // Layer 4 (oben) enable


// Port zum anschalten des Tiefsetzstellers 3,3V
#define PORT_33V PORTB
#define DDR_33V  DDRB
#define P_33V    PB0

#define KEY1     (~PIND) & (1<<PD4) // linker Taster
#define KEY2     (~PIND) & (1<<PD3) // mittlerer Taster
#define KEY3     (~PIND) & (1<<PD2) // rechter Taster


typedef struct {
  uint8_t b;
  uint8_t g;
  uint8_t r;
} T_RGB;

typedef union {
  uint8_t all[192];
  T_RGB single[4][4][4]; //z y x
} T_LEDS;

// array mit zufallszahlen (19 benutzt, 2 backup > 0)
uint8_t zufall123[21] = {2, 3, 1, 3, 2, 1, 3, 1, 2, 1, 1, 3, 2, 3, 3, 1, 2, 3, 1, 1, 1};

// array mit zufallszahlen (64 stück, 16 bit)
uint16_t zufall16[] = {49004, 39963, 9686, 22674, 53709, 61579, 40895, 52825, 1704, 19871, 30531, 41747, 4895, 30952, 48331, 64324,
    21209, 16643, 30905, 2000, 64450, 64138, 39436, 64645, 49177, 46266, 52418, 4701, 5536, 17984, 45536, 4439,
    61554, 31806, 61487, 20570, 33403, 48775, 58597, 23398, 19279, 55900, 22118, 26442, 11849, 8322, 11986, 17858,
    7130, 17670, 24909, 38866, 6551, 41204, 17887, 57832, 41163, 16280, 34214, 16570, 45664, 30181, 21377, 18737};

// Das eigentliche LED-Array
#define byteCount 24
#define ledCount  byteCount*8
T_LEDS ledZustand;

// Wie viele dimmstufen soll es geben?
#define dimLevels 64


// das wird hochgezählt und mit den leds verglichen
uint8_t Step = 0;

// wie viele programme gibt es und mit was wird gestartet?
uint8_t program = 5;
#define PROGRAM_COUNT 8

// merken ob cube an oder aus ist
volatile uint8_t sleep = 0;



//==============
// PROTOTYPEN FÜR FUNKTIONEN
void SR_SPI_Init(void);
void SR_SPI_Dimmer(void);
void MainStep1(void);
void MainStep2(void);
void MainStep3(void);
void MainStep4(void);
void MainStep5(void);
//void MainStep6(void); =MainStep5
void MainStep7(void);
void MainStep8(void);
void debounce(void);
void clean(void);
void turn_off(void);
void turn_on(void);
void abdunkeln(T_RGB*, uint8_t);
void regenbogen(T_RGB*, uint8_t);
void delay200ms(void);





//===============
// function declarations



void SR_SPI_Init()
{
  DDRB |= (1<<PB4); // set this before enabling SPI (SS may not be low!)
  
  DDRB |= (1<<PB7); // SCK
  DDRB |= (1<<PB5); // MOSI
  
  DDRA |= (1<<PA4); // RCLK of Shift Registers
  
  SPCR = (1<<SPE) | (1<<MSTR);// | (1<<CPOL);
	SPSR |= (1<<SPI2X);
}

void SR_SPI_Dimmer()
{
  Step = 0;
	while (Step < dimLevels)
	{
    uint8_t* pPtr = (uint8_t*)&(ledZustand);	
  				
  	
    for (uint8_t l=3; l<7; l++)
    {
      uint8_t ByteCounter = byteCount/4;
    	
    	uint8_t levels = 0x0F & (~(1<<(l%4)));
    	
    	// hier muss genug getan werden, bis alle bits raus sind! zur not NOP!
    	PORTA =  (1<<PA4) | levels;
    	PORTA &= ~(1<<PA4);
    	
    	while(ByteCounter)
    	{
    		uint8_t bits=0;
    	
    	  uint8_t d = 8;
    			
    		while(d--)
    		{
    			uint8_t v = *pPtr; pPtr++;
    				
    			bits <<= 1;
    	
    			if(v <= Step)
    				bits |= 1;
    		}
    		
    		// write to shift registers!
    		SPDR = bits;
    		
    		ByteCounter--;
    	}
    }
  	Step++;
  }
}


//=====================
// DEBOUNCE
//
//
void debounce()
{
  static uint8_t debounce[3] = {0,0,0};
  
  if (KEY1) // Taster LINKS (S3)
  {
    if(debounce[0] < 255)
      debounce[0]++;
  }
  else
  {
    if (debounce[0] >= 10) // Links kurz
    {
      if (program > 1)
        program--;
      clean();
    }
    debounce[0]=0;
  }
  
  if (KEY2) // Taster MITTE (S2)  -  Das ist der einzige Taster, der wirklich bestückt werden muss... die anderen beiden haben nicht so richtig ihre Berechtigung.
  {
    if (debounce[1] >= 200) // Mitte lang (Cube ausschalten)
    {
      turn_off();
    }
    else
      debounce[1]++;
  }
  else
  {
    if (debounce[1] >= 10 && debounce[1] < 200) // Mitte kurz (nächstes Programm)
    {
      //program = 0;
      if (!sleep) {
        program++;
        clean();
        if (program == PROGRAM_COUNT)
          program = 1;
      }
      else
        turn_on();
    }
    debounce[1]=0;
  }
  
  if (KEY3) // Taster RECHTS (S1)
  {
    if(debounce[2] < 255)
      debounce[2]++;
  }
  else
  {
    if (debounce[2] >= 10) // Rechts kurz
    {
      if (program < PROGRAM_COUNT)
        program++;
      clean();
    }
    debounce[2]=0;
  }
}


void clean()
{
  for (uint8_t i=0; i<ledCount; i++)
    ledZustand.all[i] = 0;
  SR_SPI_Dimmer(); // apply data
}


void turn_on()
{
  sleep = 0;
  PORT_33V |= (1<<P_33V); // 3,3V an
}


void turn_off()
{
  sleep = 1;
  clean();
  
  PORT_33V &= ~(1<<P_33V); // 3,3V aus
  PORTA |= (1<<LAYER1) | (1<<LAYER2) | (1<<LAYER3) | (1<<LAYER4); // alle ebenen aus
  
  //set_sleep_mode(SLEEP_MODE_IDLE);
  //delay200ms();
  //delay200ms();
  //sleep_mode();
}

ISR(INT1_vect)
{
  /*if (sleep)
  {
    turn_on();
    delay200ms();
    delay200ms();
    delay200ms();
  }*/
}


//===================
// MAIN FUNCTION
//
//
int main( void )
{
	SR_SPI_Init();
	
	PORTD |= (1<<PD2) | (1<<PD3) | (1<<PD4); // pull-up for buttons
	
	DDRA |= (1<<LAYER1) | (1<<LAYER2) | (1<<LAYER3) | (1<<LAYER4); // ebenentreiber
	PORTA |= (1<<LAYER1) | (1<<LAYER2) | (1<<LAYER3) | (1<<LAYER4); // alle ebenen aus
	
	DDR_33V |= (1<<P_33V);
	PORT_33V |= (1<<P_33V); // 3.3V anschalten
	
	//GICR |= (1<<INT1); // int 1 aktivieren (mittlerer Button)
	MCUCR |= (1<<ISC11); // falling edge on INT1
	
	sei(); // ints einschalten für aufwachen aus sleep
	
	Step = 0;
	while(1)
	{
	  if (!sleep)
	  {
  	  switch(program)
  	  {
  	  case 1:
  	    MainStep1();
  	    break;
  	  case 2:
  	    MainStep2();
  	    break;
  	  case 3:
  	    MainStep3();
  	    break;
  	  case 4:
        MainStep4();
        break;
  	  case 5:
      case 6:
        MainStep5();
        break;
      case 7:
        MainStep7();
        break;
      case 8:
        MainStep8();
        break;
      default:
    	  {
    	    
    	  }
  	  }
	  }
  	
	  SR_SPI_Dimmer();
	  	    
		debounce();
	}
}









//=======================
// Effekte
//
void MainStep8() // wildes blinken
{
  static uint8_t arrayindex = 0;
  static uint8_t layer = 0;
  
  static uint8_t counter = 0;
  counter++;
  
  
  if ((counter%256) > 5)
  {
    uint16_t r = zufall16[arrayindex++];
    arrayindex %= 64;
    uint16_t g = zufall16[arrayindex++];
    arrayindex %= 64;
    uint16_t b = zufall16[arrayindex++];
    arrayindex %= 64;
    
    for (uint8_t i=0; i<16; i++)
    {
      ledZustand.all[layer*48 + i*3   ] = 64 * (r & 1);
      ledZustand.all[layer*48 + i*3 +1] = 64 * (g & 1);
      ledZustand.all[layer*48 + i*3 +2] = 64 * (b & 1);
      r = r>>1;
      g = g>>1;
      b = b>>1;
    }
    
    layer++;
    layer %= 4;
  }
  else // = 0
  {
    for (uint8_t i=0; i<192; i++)
      ledZustand.all[i] = 64;
    arrayindex++;
  }
}




void MainStep7() // sternenhimmel
{
  static uint16_t counter = 0;
  static uint8_t colorshift = 0;
  counter++;
  if (counter == 4096)
    counter = 0;
  
  if (!(counter % 7)) // leds verändern während fade ihre farbe (7=prim gegen wiederholung)
  {
    colorshift++;
    colorshift %= 192; // regenbogen geht nur 0-191
  }
  
  for (uint8_t x0=0; x0<=3; x0++)
  for (uint8_t y0=0; y0<=3; y0++)  
  for (uint8_t z0=0; z0<=3; z0++)
  {
    uint8_t index = (x0<<4) | (y0<<2) | z0; // leds 0-63 adressieren
    uint16_t zufall = zufall16[index] & 0x0FFF; // zufallszahlen auf 4096 beschränken
    uint16_t distance;
    
    if (zufall > (4096-256) && counter < 256)
      distance = counter + 4096 - zufall;
    else if (zufall < 256 && counter > (4096-256)) // sanfter übergang von 4095 auf 0
      distance = zufall - (counter - 4096);
    else
      distance = abs(zufall - counter);
    
    if (distance >= (4096-256))
      distance = 4096 - distance;
    
    if (distance > 255)
      distance = 255;
    
    //T_RGB color = {64, 64, 64};
    T_RGB color;
    regenbogen(&color, (zufall + colorshift) % 192);
    
    abdunkeln(&color, 255 - (uint8_t)distance);
    
    
    ledZustand.single[z0][y0][x0].b = color.b;
    ledZustand.single[z0][y0][x0].g = color.g;
    ledZustand.single[z0][y0][x0].r = color.r;
  }
}




void MainStep5() // bezugspunkt
{
  static uint8_t prescaler = 0;
    prescaler++;
        
  static int8_t x = 0;
  static int8_t y = 0;
  static int8_t z = 0;
  
  
  
  // pong-effekt
  static uint8_t zindex = 0;
  if (zindex >= 19)
    zindex = 0;
  
  static int8_t vx = 7;
  static int8_t vy = 5;
  static int8_t vz = 4;
  
  if (!(prescaler%8))
  {
    x += vx;
    y += vy;
    z += vz;
  
  
    if (x < 0) {
      x = 0;
      vx = zufall123[zindex++];
    }
    else if (x > 48) {
      x = 48;
      vx = -zufall123[zindex++];
    }
    
    if (y < 0) {
      y = 0;
      vy = zufall123[zindex++];
    }
    else if (y > 48) {
      y = 48;
      vy = -zufall123[zindex++];
    }
    
    if (z < 0) {
      z = 0;
      vz = zufall123[zindex++];
    }
    if (z > 48) {
      z = 48;
      vz = -zufall123[zindex++];
    }
    
    
    // grundfarbe für punkt
    static uint8_t basecolor = 0;
    basecolor++;
    if (basecolor >= 192)
      basecolor = 0;
    
  
  
    for (uint8_t x0=0; x0<=48; x0+=16)
    for (uint8_t y0=0; y0<=48; y0+=16)  
    for (uint8_t z0=0; z0<=48; z0+=16)
    {
      // abstände der LED zum Bezugspunkt
      uint8_t dx = abs(x-x0);
      uint8_t dy = abs(y-y0);
      uint8_t dz = abs(z-z0);
      uint8_t distance = dx+dy+dz;
      
      // echte positionen der LEDs (0-3)
      uint8_t ix = (x0+8)/16;
      uint8_t iy = (y0+8)/16;
      uint8_t iz = (z0+8)/16;
      
      T_RGB color = {0, 0, 0};
      
      // farbiger punkt
      if (program == 5)
      {
        regenbogen (&color, basecolor);
        
        int16_t i = 255-distance*8;
        if (i < 0)
          i = 0;
        abdunkeln(&color, (uint8_t)i);
      }
      // regenbogen
      else
      {
        regenbogen(&color, 144-distance);
      }
      
      
      ledZustand.single[iz][iy][ix].b = color.b;
      ledZustand.single[iz][iy][ix].g = color.g;
      ledZustand.single[iz][iy][ix].r = color.r;
    }
  }
}

void MainStep4() // Farbrad 2 Test
{
  static uint8_t r=1, b=2, g=4;
  static uint8_t rot=0, gruen=0, blau=0;
  
  static uint8_t prescaler = 0;
        prescaler++;
      
  if (!(prescaler%16))
  {
    if (rot>=61) r=-2;
    if (rot<=1) r=3;
    if (gruen>=63) g=-3;
    if (gruen<=2) g=1;
    if (blau>=61) b=-1;
    if(blau<=4) b=3;
    
    rot = rot + r;
    gruen = gruen + g;
    blau = blau + b;
    
    for (uint8_t leda=2; leda<ledCount; leda+=3)
      ledZustand.all[leda] = rot;
        
    for (uint8_t ledb=1; ledb<ledCount; ledb+=3)
      ledZustand.all[ledb] = gruen;
        
    for (uint8_t ledc=0; ledc<ledCount; ledc+=3)
      ledZustand.all[ledc] = blau;
  }
}


void MainStep3() // lauflicht
{
  static uint8_t prescaler = 0;
      prescaler++;
    
  if (!(prescaler%64))
  {
    static uint8_t count=0;
    count++;
    if (count == ledCount)
      count=0;
    
    for (uint8_t led=0; led < ledCount; led++)
    {
      if (led == count)
        ledZustand.all[led] = 255;
      else
        ledZustand.all[led] = 0;
    }
  }
}


void MainStep2() // farbrad
{
  static uint8_t prescaler = 0;
    prescaler++;
  
  if (!(prescaler%16))
  {
    static uint8_t count = 0;
    count++;
    if (count == 192)
      count=0;
    
    
    uint8_t rot;
    uint8_t gruen;
    uint8_t blau;
    
    
    if (count < 64)
    {
      rot = count;
      blau = 0;
      gruen = 64 -count;
    }
    else if ((count >= 64) && (count < 128))
    {
      rot = 128-count;
      blau = count - 64;
      gruen = 0;
    }
    else
    {
      rot = 0;
      blau = 192-count;
      gruen = count-128;
    }
    
    
    for (uint8_t leda=2; leda<ledCount; leda+=3)
      ledZustand.all[leda] = rot;
    
    for (uint8_t ledb=1; ledb<ledCount; ledb+=3)
        ledZustand.all[ledb] = gruen;
    
    for (uint8_t ledc=0; ledc<ledCount; ledc+=3)
      ledZustand.all[ledc] = blau;
  }
}



void MainStep1() // fader
{
  static uint8_t common = 0;
  static uint8_t commondir = 0;
  static uint8_t prescaler = 0;
  prescaler++;
  static uint8_t activeled;
  activeled = 0;
  
  if (prescaler%8 == 0)
  {
    if (commondir == 0)
    {
      if (common < dimLevels-1)
        common++;
      else
        commondir = 1;
    }
    else
    {
      if (common > 0)
        common--;
      else
        commondir = 0;
    }
  
  
    while (activeled<ledCount)
    {
      ledZustand.all[activeled] = common;
      activeled++;
    }
  }
}



// ========================
// HILFSFUNKTIONEN
//
void abdunkeln(T_RGB* color, uint8_t helligkeit)
// teilt farbwert für niedrigere helligkeit
// helligkeit 0-255
{
  (*color).r = (uint16_t)(helligkeit*(*color).r)/255;
  (*color).g = (uint16_t)(helligkeit*(*color).g)/255;
  (*color).b = (uint16_t)(helligkeit*(*color).b)/255;
}


void regenbogen(T_RGB *color, uint8_t pos)
// erzeugt regenbogenfarbe für pos 0-191 (blau->türkis->grün->gelb->rot->rosa->blau)
{
  if (pos < 64)
  {
    (*color).g = pos;
    (*color).r = 0;
    (*color).b = 64 -pos;
  }
  else if ((pos >= 64) && (pos < 128))
  {
    (*color).g = 128-pos;
    (*color).r = pos - 64;
    (*color).b = 0;
  }
  else
  {
    (*color).g = 0;
    (*color).r = 192-pos;
    (*color).b = pos-128;
  }
}


void delay200ms()
{
  for (uint16_t k=0; k<3200; k++)
    for (uint8_t i=0; i<255; i++)
      asm ("nop");
}


