#include <SPI.h>
#include <Wire.h>
#include <MIDI.h>
#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>
#include <EEPROM.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/* OLED DISPLAY VARIABLES */
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT);

/* CHANGE PINS TO SUIT YOUR ARDUINO PINOUT */
const int sw1Pin = A0;
const int sw2Pin = A1;
const int sw3Pin = A2;
const int sw4Pin = A3;
const int sw5Pin = 10;
const int RELAY1 = 2; //set pin for RELAY 1 (CLEAN) D2
const int RELAY2 = 3; //set pin for RELAY 2 (GAIN1/GAIN2) D3
const int RELAY3 = 4; //set pin for RELAY 3 (BOOST) D4
const int RELAY4 = 5; //set pin for RELAY 4 (SAT) D5
const int RELAY5 = 6; //set pin for RELAY 5 (PLEXI) D6
const int RELAY6 = 7; //set pin for RELAY 6 (FX LOOP)
const int RELAY7 = 8; //set pin for RELAY 7 (SPARE)
const int RELAY8 = 9; //set pin for RELAY 7 (SPARE)

int swPush = 0;
boolean swState = LOW;
boolean swStateLast = LOW;

const unsigned char NaN [] PROGMEM = {
  0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
  0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x03,
  0x80, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x1f, 0xc0, 0x00,
  0x00, 0x00, 0x03, 0xf8, 0x7f, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x3f, 0xe0, 0x00, 0x00, 0x00,
  0x03, 0xf8, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xf8,
  0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
  0xe0, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x1f, 0xe0, 0x00,
  0x00, 0x07, 0xfc, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x03,
  0xfc, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x03, 0xfc, 0x00,
  0x00, 0x04, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xe0, 0x01, 0xfc, 0x00, 0x00, 0xff,
  0xff, 0xf0, 0x00, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x0c, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xfc,
  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x03, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0x83, 0xf0,
  0x00, 0x00, 0xff, 0xff, 0xff, 0xc7, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe3, 0x80, 0x00, 0x00,
  0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff,
  0xf7, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf7, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0,
  0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x8f, 0xf0, 0x00, 0x00,
  0x00, 0x1f, 0x80, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x03, 0x80, 0x00, 0x00
};

uint8_t midi_channel = 0;
MIDI_CREATE_DEFAULT_INSTANCE();

void handleError(int8_t err)
{
  digitalWrite(LED_BUILTIN, (err == 0) ? LOW : HIGH);
}

//MIDI CC FUNCTIONS
void midiCtrlChange(byte c, byte v) {
  MIDI.sendControlChange(c, v, 0xB0 | midi_channel);
  if (v > 64) {
    display.setCursor(0, 0);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print(" SIRIUS AMPLIFICATION ");
    display.setCursor(0, 55);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("CC:");
    display.setCursor(20, 55);
    display.print(c);
    display.setCursor(40, 55);
    display.print("V:");
    display.display();
    display.print(v);
    display.print("   ");
    display.display();
  }
  if (v <= 64) {
    display.setCursor(0, 0);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print(" SIRIUS AMPLIFICATION ");
    display.setCursor(0, 55);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("CC:");
    display.setCursor(20, 55);
    display.print(c);
    display.setCursor(40, 55);
    display.print("V:");
    display.display();
    display.print(v);
    display.print("   ");
    display.display();
  }
  if (c == 59 && v > 64) {
    //FX Loop Bypass CC MSG
    digitalWrite(RELAY6, HIGH);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("FX:");
    if (v == 127) display.print("ON");
    display.print("    ");
    display.display();
  }
  if (c == 59 && v <= 64) {
    //FX Loop Bypass CC MSG
    digitalWrite(RELAY6, LOW);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("FX:");
    if (v == 0) display.print("OFF");
    display.print("    ");
    display.display();
  }
  if (c == 85 && v > 64) {
    //Channel On CC MSG
    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, LOW);
    digitalWrite(RELAY3, LOW);
    digitalWrite(RELAY4, LOW);
    digitalWrite(RELAY5, LOW);
    display.setCursor(0, 20);
    display.setTextSize(2);
    display.setTextColor(WHITE, BLACK);
    display.print("CLEAN");
    display.print("  ");
    display.display();
  }
  if (c == 85 && v <= 64) {
    //Channel Off CC MSG
  }
  if (c == 86 && v > 64) {
    //Channel On CC MSG
    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY2, LOW);
    display.setCursor(0, 20);
    display.setTextSize(2);
    display.setTextColor(WHITE, BLACK);
    display.print("GAIN 1");
    display.print("    ");
    display.display();
  }
  if (c == 86 && v <= 64) {
    //Channel Off CC MSG
  }
  if (c == 87 && v > 64) {
    //Channel On CC MSG
    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY2, HIGH);
    display.setCursor(0, 20);
    display.setTextSize(2);
    display.setTextColor(WHITE, BLACK);
    display.print("GAIN 2");
    display.print("    ");
    display.display();
  }
  if (c == 87 && v <= 64) {
    //Channel Off CC MSG
    digitalWrite(RELAY2, LOW);
  }
  if (c == 88 && v > 64) {
    //Channel On CC MSG
    digitalWrite(RELAY3, HIGH);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("BOOST:");
    if (v == 127) display.print("ON");
    display.print("    ");
    display.display();
  }
  if (c == 88 && v <= 64) {
    //Channel Off CC MSG
    digitalWrite(RELAY3, LOW);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("BOOST:");
    if (v == 0) display.print("OFF");
    display.print("    ");
    display.display();
  }
  if (c == 89 && v > 64) {
    //Toggle On CC MSG
    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY5, HIGH);
    display.setCursor(0, 20);
    display.setTextSize(2);
    display.setTextColor(WHITE, BLACK);
    display.print("PLEXI");
    display.print("   ");
    display.display();
  }
  if (c == 89 && v <= 64) {
    //Toggle Off CC MSG
    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, LOW);
    digitalWrite(RELAY3, LOW);
    digitalWrite(RELAY4, LOW);
    digitalWrite(RELAY5, LOW);
    display.setCursor(0, 20);
    display.setTextSize(2);
    display.setTextColor(WHITE, BLACK);
    display.print("CLEAN");
    display.print("   ");
    display.display();
  }
  if (c == 90 && v > 64) {
    //Toggle On CC MSG
    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY4, HIGH);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("SAT:");
    if (v == 127) display.print("ON");
    display.print("    ");
    display.display();
  }
  if (c == 90 && v <= 64) {
    //Toggle Off CC MSG
    digitalWrite(RELAY4, LOW);
    display.setCursor(0, 45);
    display.setTextSize(1);
    display.setTextColor(WHITE, BLACK);
    display.print("SAT:");
    if (v == 0) display.print("OFF");
    display.print("    ");
    display.display();
  }
  if (c == 91 && v > 64) {
    //Reverb Volume On CC MSG
    //Use digitalWrite(OUTPUT,HIGH); for direct functions
  }
  if (c == 91 && v <= 64) {
    //Reverb Volume Off CC MSG
    //Use digitalWrite(OUTPUT,LOW); for direct functions
  }
  if (c == 92 && v > 64) {
    //Tremelo Volume On CC MSG
    //Use digitalWrite(OUTPUT,HIGH); for direct functions
  }
  if (c == 92 && v <= 64) {
    //Tremelo Volume Off CC MSG
    //Use digitalWrite(OUTPUT,LOW); for direct functions
  }
}

void setup() {
  //INITIATE INPUT & OUTPUTS
  pinMode(sw1Pin, INPUT_PULLUP);
  pinMode(sw2Pin, INPUT_PULLUP);
  pinMode(sw3Pin, INPUT_PULLUP);
  pinMode(sw4Pin, INPUT_PULLUP);
  pinMode(sw5Pin, INPUT_PULLUP);
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  pinMode(RELAY5, OUTPUT);
  pinMode(RELAY6, OUTPUT);
  pinMode(RELAY7, OUTPUT);
  pinMode(RELAY8, OUTPUT);

  // INITIATE CLEAN CHANNEL
  digitalWrite(RELAY1, HIGH);

  // INITIATE MIDI
  Serial.begin(9600);
  MIDI.setHandleError(handleError);
  MIDI.begin(MIDI_CHANNEL_OMNI);

  // INITIATE DISPLAY SPLASHSCREEN
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.drawBitmap(40, 5, NaN, 50, 50, WHITE);
  display.display();
  delay(3000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, 0);
  display.print(" SIRIUS AMPLIFICATION ");
  display.display();
  midiCtrlChange(85, 127);
}

void loop() {
  MIDI.read();
  //SWITCH 1 FUNCTIONS
  swState = digitalRead(sw1Pin);
  if (swState != swStateLast) 
  {
    if (swState == LOW) 
    {
      swPush++;
      if (swPush > 1) 
      {
        swPush = 0;
      }
      switch (swPush)
      {
        case 1:
          midiCtrlChange(86, 127);
          break;
        case 0:
          midiCtrlChange(87, 127);
          break;
      }
    }
    swStateLast = swState;
    delay(10);
    while (digitalRead(sw1Pin) == LOW) 
  {
    delay(5);
  }
  }
  //SWITCH 2 FUNCTIONS
  swState = digitalRead(sw2Pin);
  if (swState != swStateLast) 
  {
    if (swState == LOW)
    {
      swPush++;
      if (swPush > 1) 
      {
        swPush = 0;
      }
      switch (swPush)
      {
        case 1:
          midiCtrlChange(88, 127);
          break;
        case 0:
          midiCtrlChange(88, 0);
          break;
      }
    }
    swStateLast = swState;
    delay(10);
    while (digitalRead(sw2Pin) == LOW) 
  {
    delay(5);
  }
  }
  //SWITCH 3 FUNCTIONS
  swState = digitalRead(sw3Pin);
  if (swState != swStateLast) 
  {
    if (swState == LOW) 
    {
      swPush++;
      if (swPush > 1) 
      {
        swPush = 0;
      }
      switch (swPush)
      {
        case 1:
          midiCtrlChange(90, 127);
          break;
        case 0:
          midiCtrlChange(90, 0);
          break;
      }
    }
    swStateLast = swState;
    delay(10);
    while (digitalRead(sw3Pin) == LOW) 
  {
    delay(5);
  }
  }
  //SWITCH 4 FUNCTIONS
  swState = digitalRead(sw4Pin);
  if (swState != swStateLast) 
  {
    if (swState == LOW) 
    {
      swPush++;
      if (swPush > 1) 
      {
        swPush = 0;
      }
      switch (swPush)
      {
        case 1:
          midiCtrlChange(89, 127);
          break;
        case 0:
          midiCtrlChange(85, 127);
          break;
      }
    }
    swStateLast = swState;
    delay(10);
    while (digitalRead(sw4Pin) == LOW) 
  {
    delay(5);
  }
  }
  //SWITCH 5 FUNCTIONS
  swState = digitalRead(sw5Pin);
  if (swState != swStateLast) 
  {
    if (swState == LOW) 
    {
      swPush++;
      if (swPush > 1) 
      {
        swPush = 0;
      }
      switch (swPush)
      {
        case 1:
          midiCtrlChange(59, 127);
          break;
        case 0:
          midiCtrlChange(59, 0);
          break;
      }
    }
    swStateLast = swState;
    delay(10);
    while (digitalRead(sw5Pin) == LOW) {
        //loop until toggle button is released
        delay(5);
    }
  }
}