////////////////////////////////////////////// 
//        RemoteXY include library          // 
////////////////////////////////////////////// 

// RemoteXY select connection mode and include library  
#define REMOTEXY_MODE__SOFTSERIAL
#include <SoftwareSerial.h> 

#include <RemoteXY.h> 

// RemoteXY connection settings  
#define REMOTEXY_SERIAL_RX 2 
#define REMOTEXY_SERIAL_TX 3 
#define REMOTEXY_SERIAL_SPEED 9600 


// RemoteXY configurate   
#pragma pack(push, 1) 
uint8_t RemoteXY_CONF[] = 
  { 1,0,51,0,6,5,0,3,135,2
  ,2,96,13,2,129,0,4,18,102,8
  ,9,111,102,102,32,32,32,111,110,32
  ,32,111,98,111,32,32,108,117,32,32
  ,32,108,100,32,32,32,32,108,98,32
  ,32,32,99,97,0 }; 
   
// this structure defines all the variables of your control interface  
struct { 

    // input variable
  uint8_t select_1; // =0 if select position A, =1 if position B, =2 if position C, ... 

    // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0 

} RemoteXY; 
#pragma pack(pop) 

///////////////////////////////////////////// 
//           END RemoteXY include          // 
///////////////////////////////////////////// 


// pins:
char layers[3] = {A0, A1, A2};
const int STARTING_PIN = 4;
const int END_PIN = 12;

// fields:
bool nextOrderRecieved;
int curOrder = 0;

void setup() {
  RemoteXY_Init (); 

  Serial.begin(9600);

  for (int i = 4; i <= 12; i++) {
      pinMode(i,OUTPUT);  // PINS4-12 are set as output
  }  

  for (int i = 0; i < sizeof(layers); i++) {
    char curLayer = layers[i];
    pinMode(curLayer,OUTPUT);   // set PIN curLayer as Output
    digitalWrite(curLayer,HIGH);  // pull up curLayer pin
  }

  TurnOffAll();
}

void loop() { 
  RemoteXY_Handler ();

  if(curOrder != RemoteXY.select_1) {
    curOrder = RemoteXY.select_1;
    SwitchLoop();
  } 
}

void SwitchLoop() {
  switch(RemoteXY.select_1) {
    case 0:
      TurnOffAll();
      break;
    case 1:
      TurnOnAll();
      break;
    case 2:
      OneByOne();
      break;
    case 3:
      LayerByLayerUp();
      break;
    case 4:
      LayerByLayerDown();
      break;
    case 5: 
      UpAndDownLayer();
      break;
    case 6:
      CircleAround(2, true);
      break;
    default: 
      TurnOffAll();
  }  
}

void TurnOnAll() {
  for (int i = 0 ; i < sizeof(layers); i++) {
    char curLayer = layers[i];
    digitalWrite(curLayer,LOW); //curLayer is grounded
    
    for (int i = STARTING_PIN; i <= END_PIN; i++) {
      digitalWrite(i,HIGH); //turn ON all LEDs in curLayer
    }
  }
}

void TurnOffAll() {
  for (int i = 0; i < sizeof(layers); i++) {
    char curLayer = layers[i];
    digitalWrite(curLayer,HIGH); //curLayer is pulled up
    
    for (int i = STARTING_PIN; i <= END_PIN; i++) {
      digitalWrite(i,LOW); //turn OFF all LEDs in curLayer
    }
  }
}


void OneByOne() { 
  int delayTime = 200;

  for (int i = 0; i < sizeof(layers); i++) {
    char curLayer = layers[i];
    digitalWrite(curLayer,LOW); //curLayer is grounded
    
    for (int i = STARTING_PIN; i <= END_PIN; i++) {
      digitalWrite(i,HIGH); // Turn ON LED
      delay(delayTime);
      digitalWrite(i,LOW); // Turn OFF LED
    }
    digitalWrite(curLayer,HIGH); //curLayer is pulled up
  }
}

void CircleAround(int amount, bool showMid) {
  int midLED = 8;
  int outerLEDs[8] = {4, 5, 6, 9, 12, 11, 10, 7};
  int outerLEDsCount = (sizeof(outerLEDs) / sizeof(int));
  int delayTime = 400;

  for(int i = 0; i < amount; i++) {
    for (int i = 0; i < sizeof(layers); i++) {
      char curLayer = layers[i];
      digitalWrite(curLayer, LOW);
      if(showMid)
        digitalWrite(midLED, HIGH);    
    }
  
    // Outer LEDs:
    for(int i = 0; i < outerLEDsCount; i++) {  
      digitalWrite(outerLEDs[i], HIGH);
      delay(delayTime);
      digitalWrite(outerLEDs[i], LOW);
    }
  }
}

void LayerByLayerDown() { 
  int delayTime = 400;

  for (int i = 0; i < sizeof(layers); i++) {
    char curLayer = layers[i];
    
    digitalWrite(curLayer,LOW); //curLayer is grounded
    for (int i = STARTING_PIN; i <= END_PIN; i++) {
      digitalWrite(i,HIGH); //turn ON all LEDs in curLayer
    }
    delay(delayTime);
    for (int i = STARTING_PIN; i <= END_PIN; i++) {
      digitalWrite(i,LOW); //turn OFF all LEDs in curLayer
    }
    digitalWrite(curLayer,HIGH); //curLayer is pulled up
  }
}

void LayerByLayerUp() { 
  int delayTime = 400;

  for (int i = sizeof(layers); i >= 0 ; i--) {
    char curLayer = layers[i];

    digitalWrite(curLayer,LOW); //curLayer is grounded
    for (int i = END_PIN; i >= STARTING_PIN; i--) {
      digitalWrite(i,HIGH); //turn ON all LEDs in curLayer
    }
    delay(delayTime);
    for (int i = END_PIN; i >= STARTING_PIN; i--) {
      digitalWrite(i,LOW); //turn OFF all LEDs in curLayer
    }
    digitalWrite(curLayer,HIGH); //curLayer is pulled up
  }
}

void LayerByLayerUpAndStay(int delayTime) {
   for (int i = sizeof(layers); i >= 0 ; i--) {
      char curLayer = layers[i];
  
      digitalWrite(curLayer,LOW); //curLayer is grounded
      for (int i = END_PIN; i >= STARTING_PIN; i--) {
        digitalWrite(i,HIGH); //turn ON all LEDs in curLayer
      }
          
      if(i != 0) {
        delay(delayTime);
        for (int i = END_PIN; i >= STARTING_PIN; i--) {
          digitalWrite(i,LOW); //turn OFF all LEDs in curLayer
        }
        digitalWrite(curLayer,HIGH); //curLayer is pulled up
      }
   }
}

void LayerByLayerDownAndStay(int delayTime) {
  for (int i = 0; i < sizeof(layers); i++) {
      char curLayer = layers[i];
      
      digitalWrite(curLayer,LOW); //curLayer is grounded
      for (int i = STARTING_PIN; i <= END_PIN; i++) {
        digitalWrite(i,HIGH); //turn ON all LEDs in curLayer
      }
  
      if(i != sizeof(layers) - 1) {
        delay(delayTime);
        for (int i = STARTING_PIN; i <= END_PIN; i++) {
          digitalWrite(i,LOW); //turn OFF all LEDs in curLayer
        }
        digitalWrite(curLayer,HIGH); //curLayer is pulled up
      }
   }
}

void UpAndDownLayer() {
 int delayTime = 400;
 int amount = 3;

  for(int i = 0; i < amount; i++) {
    LayerByLayerUpAndStay(delayTime);
    delay(delayTime/2);
    LayerByLayerDownAndStay(delayTime);
    delay(delayTime/2);
  }
}

