#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TimedBlink.h>
#include <MIDIcontroller.h>

/* ----------------- Pin Connection Notes ----------------
// All V+ is 3.3V unless stated
// OLED
  SDA --> GPIO 4
  SCL --> GPIO 5
// Switches (NO - connected to GND)
  Encoder Switch --> GPIO 6
  Footswitch A --> GPIO 7
  Footswitch B --> GPIO 8
// LEDs
  LED Switch A --> GPIO 9
  LED Switch B --> GPIO 10
// Encoder
  DT  --> GPIO 16
  CLK --> GPIO 17
// Pots
  Pot X   --> GPIO 22
  Pot Y   --> GPIO 21
  Pot Z   --> GPIO 20
  Pot Mix --> GPIO 19
  Pot Vol --> GPIO 18
--------------------- Pin Connection Notes ---------------- */ 

// OLED
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Pot Pins

#define potXPin A2 // pin34(GP28)
#define potYPin A1 // pin32(GP27)
#define potZPin A0 // pin31(GP26)

// Encoder Pins
const int encPinA = 16;
const int encPinB = 17;
const int encbtnPin = 6;

// Button Pins
#define swAPin 7
#define swBPin 8

// LED Pins
#define swALEDPin 9
#define swBLEDPin 10

TimedBlink monitor1(swALEDPin);
TimedBlink monitor2(swBLEDPin);

// MIDI
byte MIDIchannel = 1;
MIDIenc myEnc(encPinA, encPinB, 24);
MIDIbutton myButton(encbtnPin, 25, LATCH);



// presets 11 options: 
// (preset#),(presetName),(algo#),(potX),(potY),(potZ),(potMix),(potVol),(ledAMode),(ledBMode),(Bpm)
const int presetList[] = {1, 0, 1, 25, 45, 55, 60, 60, 1, 2, 85,
                          2, 1, 2, 51, 41, 0, 60, 60, 0, 2, 120,
                          3, 2, 3, 34, 60, 78, 60, 60, 0, 1, 51,
                          4, 3, 4, 27, 32, 22, 60, 60, 0, 1, 62,
                          5, 4, 5, 18, 95, 82, 60, 60, 0, 1, 70,
                          6, 5, 6, 100, 24, 45, 60, 60, 0, 1, 90,
                          7, 6, 7, 67, 78, 120, 60, 60, 0, 1, 155,
                          8, 7, 8, 21, 40, 25, 60, 60, 0, 1, 180
};

const char *presetNameList[] = {"CavrnSwell", "ModAngel", "Twilight", "Arise", "StndrdRoom", "Bleum", "Higher", "Treetops"};
const char *algoList[] = {"Room", "Hall", "Chamber", "Cavern", "Modulated", "Shimmer", "Pitch", "Duck"};

// Variable Pots in orders of 3 for each algorithm 
const char *potList[] = {"Time", "PreD", "Dmpn",
                    "Time", "PreD", "Dmpn",
                    "Time", "Bass", "Treb",
                    "Time", "Bass", "Treb",
                    "Time", "ModD", "ModR",
                    "Time", "Ptch", "Amnt",
                    "Time", "OctD", "OctU",
                    "Time", "Duck", "Sens"
                    };

int presetActiveNumber = 4; // Set preset to 1 >> move to last used in future!
int presetActiveAlgo = presetList[(presetActiveNumber*11)-9]; // Current Algorithm
int presetActiveTempo = presetList[(presetActiveNumber*11)-1]; //Current tempo
#define presetActiveName presetNameList[presetActiveNumber - 1] // Get name of Current Preset

// UI
#define potXName potList[(presetActiveAlgo * 3)-3]
#define potYName potList[(presetActiveAlgo * 3)-2]
#define potZName potList[(presetActiveAlgo * 3)-1]
#define potMixName "Mix"
#define potVolName "Vol"

// Pots - Values 0-120
int potXVal = 80;
int potXLastVal = 80;
int potXPresetVal = presetList[(presetActiveNumber*11)-8];
bool potXState = 0; // true if edited, false if saved state

int potYVal = 65;
int potYLastVal = 65;
int potYPresetVal = presetList[(presetActiveNumber*11)-7];
bool potYState = 0; // true if edited, false if saved state

int potZVal = 40;
int potZLastVal = 40;
int potZPresetVal = presetList[(presetActiveNumber*11)-6];
bool potZState = 0; // true if edited, false if saved state

int potMixVal = 60;
int potMixLastVal = 60;
int potMixPresetVal = presetList[(presetActiveNumber*11)-5];
bool potMixState = 0; // true if edited, false if saved state

int potVolVal = 60;
int potVolLastVal = 60;
int potVolPresetVal = presetList[(presetActiveNumber*11)-4];
bool potVolState = 0; // true if edited, false if saved state


int EncSwState = 0;
int EncSwLastState = 0;
int swAState = 0;
int swALastState = 0;
int swBState = 0;
int swBLastState = 0;

const int ledList[] = {swALEDPin, swBLEDPin}; // List all LEDs to iterate
int ledAMode = presetList[(presetActiveNumber*11)-3]; // 0 = off, 1 = on, 2 = Blink(to tempo), 3 = pattern blink(edited)
int ledBMode = presetList[(presetActiveNumber*11)-2];
int currentLED = ledList[0];
int ledDelay = 100; // how long led stays on when in pattern in ms

bool displayChanged = true;
bool buttonChanged = true;
bool potChanged = true;
bool encoderChanged = true;
bool testModeSwitch = 1;
bool ledATempo = false;
bool ledAEdited = false;
bool ledBTempo = false;
bool ledBEdited = false;

// --------- Splash screen for startup
const uint8_t splash_screen1[] = {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x67, 0x00, 0x00, 0xe7, 0x00, 0x00, 0xe7, 0x00, 0x01, 0xc3, 0x80, 0x01, 0xc3, 0x80, 0x01, 0xc3, 0x80, 0x03, 0x81, 0xc0, 0x03, 0x81, 0xc0, 0x03, 0x01, 0xc0, 0x07, 0x3c, 0xe0, 0x07, 0xfe, 0xe0, 0x07, 0xff, 0xf0, 0x0f, 0xc3, 0xf0, 0x0f, 0x80, 0xf0, 0x0e, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

void drawStartup() {
    display.clearDisplay();
    display.drawBitmap(64, 17, splash_screen1, 24, 24, 1);
    display.setTextColor(WHITE);
    display.setTextSize(1);
    display.setCursor(19, 27);
    display.println("GRACEFUL");
    display.setCursor(85, 27);
    display.println("UDIO");
    display.setCursor(20, 55);
    display.display();
    delay(1000);
    display.print("initializing");
    display.display();
    delay(100);
    /*
    display.print(".");
    display.display();
    delay(100);
    display.print(".");
    display.display();
    delay(100);
    display.print(".");
    display.display();
    delay(100);
    */
}
// --------- Splash screen for startup

void drawMain() {
  // OLED setup for Main operation
    display.clearDisplay();
    // ---------- Draw Title-Bar ----------
    display.setTextColor(WHITE);
    display.setTextSize(1);
    display.setFont(NULL); //necessary??
    display.setCursor(0, 0);
    display.println("PRESET");
    display.setCursor(38, 0);
    display.println(presetActiveNumber);
    display.setCursor(54, 0);
    display.println("|Alg");
    display.setCursor(80, 0);
    display.println(presetActiveAlgo);
    display.setCursor(86, 0);
    display.print("|");
    display.print(presetActiveTempo);
    display.println("Bpm");
    display.setCursor(1, 6);
    display.println("---------------------");

    // ---------- Preset Name ----------
    display.setCursor(3, 13);
    display.setTextSize(2);
    display.println(presetActiveName);

    // ---------- Pot Indicators ----------
    display.drawCircle(28, 42, 10, 1); // potX
    display.drawCircle(64, 42, 10, 1); // potY
    display.drawCircle(100, 42, 10, 1); // potZ
    //display.fillCircle(100, 42, 10, 1); // inverted circle color
    display.drawLine(28, 42, 36, 40, 1); // potX
    display.drawLine(64, 42, 65, 34, 1); // potY
    display.drawLine(100, 42, 94, 37, 1); // potZ
    //display.drawLine(100, 42, 94, 37, 0); // inverted line color

    // ---------- Pot Labels ----------
    display.setTextSize(1);
    display.setCursor(16, 55);
    display.println(potXName);
    display.setCursor(53, 55);
    display.println(potYName);
    display.setCursor(90, 55);
    //display.setTextColor(BLACK, WHITE); // inverted text color
    display.println(potZName);
    display.display();
}

void drawMenu() {
  // OLED setup for Menu's
}

// Relays for audio IO

void relayBypass() {
  // set relays to True Bypass
}

void relayActive() {
  // set relays to Active
}

// MIDI IO

// MIDI IN controller
// MIDI OUT controller
// MIDI Thru controller??


// User IO

// LED controller
void ledTest() {
  // set swALEDPin high
  digitalWrite(swALEDPin, HIGH);
  delay(500);
  digitalWrite(swBLEDPin, HIGH);
  delay(1000);
  digitalWrite(swALEDPin, LOW);
  digitalWrite(swBLEDPin, LOW);
  delay(1000);
}

void ledModeSet() {
// LED off 0 , LED On 1, LED tempo 2, LED Edited 3
  if (ledAMode == 0) {
    ledATempo = false;
    ledAEdited = false;
    digitalWrite(swALEDPin, LOW);
  } else if (ledAMode == 1) {
    ledATempo = false;
    ledAEdited = false;
    digitalWrite(swALEDPin, HIGH);
  } else if (ledAMode == 2) {
    ledATempo = true;
    ledAEdited = false;
  } else if (ledAMode == 3) {
    ledATempo = false;
    ledAEdited = true;
  }

  if (ledBMode == 0) {
    ledBTempo = false;
    ledBEdited = false;
    digitalWrite(swBLEDPin, LOW);
  } else if (ledBMode == 1) {
    ledBTempo = false;
    ledBEdited = false;
    digitalWrite(swBLEDPin, HIGH);
  } else if (ledBMode == 2) {
    ledBTempo = true;
    ledBEdited = false;
  } else if (ledBMode == 3) {
    ledBTempo = false;
    ledBEdited = true;
  }
}
void ledTempoPattern() {
  if (ledATempo == true) {
  monitor1.blink();
  }
  if (ledBTempo == true) {
  monitor2.blink();
  }
}


void checkIO() {
// Encoder controller


if (encoderChanged == true) {
 // read encoder and update 
}

// Button/switch Controller (include encoder and tap switches)
if (buttonChanged == true) {
 // read buttons and update
}

// Pot Controller
if (potChanged == true) {
 // read pots and update
}

}

void presetGet() {
  // already have preset number > pull rest of preset info
  presetActiveName = presetNameList[presetActiveNumber - 1]; // change later
  presetActiveAlgo = presetList[(presetActiveNumber*11)-9];
  potXPresetVal = presetList[(presetActiveNumber*11)-8];
  potYPresetVal = presetList[(presetActiveNumber*11)-7];
  potZPresetVal = presetList[(presetActiveNumber*11)-6];
  potMixPresetVal = presetList[(presetActiveNumber*11)-5];
  potVolPresetVal = presetList[(presetActiveNumber*11)-4];
  ledAMode = presetList[(presetActiveNumber*11)-3];
  ledBMode = presetList[(presetActiveNumber*11)-2];
  presetActiveTempo = presetList[(presetActiveNumber*11)-1];
  // update display and pots
  displayChanged = true;
  ledModeSet(); 
  // potChanged = true;
}

void checkDisplay() {
  if (displayChanged == true) {
    drawMain();
    displayChanged = false;
  }
}

/* void testMode() {
  if (testModeSwitch == 1) {
    if (presetActiveNumber > 8) {
      ++presetActiveNumber;
    } else if (presetActiveNumber == 8) {
      presetActiveNumber = 1;
    }
  }
} */

void setup() {
    Serial.begin(115200);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  //relayBypass(); // make sure relays are not active (should default to bypassed electrically)
  drawStartup();
  pinMode(swALEDPin, OUTPUT);
  pinMode(swBLEDPin, OUTPUT);
  ledTest();
  presetGet();
  monitor1.blink(ledDelay,(60000/presetActiveTempo)-ledDelay);
  monitor2.blink(ledDelay,(60000/presetActiveTempo)-ledDelay);
  //relayActive(); // unlatch relays - if setting 'Active on poweron' is set
}

void loop() {
  // Check IO
  checkIO(); // test Encoder, Pots, Buttons, MIDI for changes
  checkDisplay();
  ledTempoPattern();
  myEnc.send();
  myButton.send();
  
  while(usbMIDI.read()){}
  while(MIDI.read()){}
  // testMode();
  delay(1); // this speeds up the simulation
}

BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT