#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Multiplexer4067.h>
// #include <Bounce.h>

const int multiplexAPin = 0;   // S0 pin
const int multiplexBPin = 1;   // S1 pin
const int multiplexCPin = 2;   // S2 pin
const int multiplexAnalogPin = 39; // Analog input pin connected to the multiplexer

int keyboardState[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int keyboardNotes[8] = { 48, 50, 51, 53, 55, 56, 58, 60 };
int currentKeyPressed = -1;

//GUItool: begin automatically generated code
AudioSynthWaveform waveform1;   //xy=93,311
AudioEffectEnvelope envelope1;  //xy=298,237
AudioOutputI2S i2s1;            //xy=246,219
AudioMixer4 mixer1;             //xy=253,323
AudioOutputAnalogStereo dacs1;
// AudioConnection patchCord1(waveform1, 0, i2s1, 0);
// AudioConnection patchCord2(waveform1, 0, dacs1, 0);
AudioConnection patchCord1(waveform1, envelope1);
AudioConnection patchCord2(envelope1, 0, i2s1, 0);
AudioConnection patchCord3(envelope1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1;  //xy=504,172
// // GUItool: end automatically generated code

int tempo = 1000;
int tempoPotPin = A17;
//int synthNotes[8] = { 60, 62, 64, 65, 67, 69, 71, 72 };
//int drumNotes[8] = { 36, 39, 36, 39, 36, 39, 36, 39 };

//keep track of note for synth -- better to put this in a struct
int currentNote = 69;//a440

//Convert any MIDI note number to it's frequency
float freqFromMidiNote(int note){
  return 440.0f * powf(2, (note - 69.0f) / 12.0f);
}

//melody sequence 
int melodyNotes[8] = { 60, 62, 63, 72, 70, 65, 67, 70 };
//Drum sequence arrays

int kickNotes[8] = { 1, 0, 0, 0, 1, 0, 0, 0 };
int clapNotes[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
int openhihatNotes[8] = { 0, 0, 1, 0, 0, 1, 0, 0 };
int closedhihatNotes[8] = { 0, 0, 0, 1, 0, 0, 0, 1 };
int snareNotes[8] = { 0, 0, 0, 0, 1, 0, 0, 0 };
int cowbellNotes[8] = { 1, 0, 0, 0, 0, 1, 0, 0 };
int cymbalNotes[8] = { 0, 0, 0, 1, 0, 0, 0, 1 };
int tomNotes[8] = { 0, 0, 1, 0, 0, 1, 0, 0 };

int instrumentchangeflag = 0;

int channelselectbutton;
int lastchannelselectbutton = LOW;

int adsrButton;
int lastadsrButton;

int attack = 500;
int decay = 500; 
float sustain = 0.5;
int release = 2000;

int velocity = 100;

int channel = 0;

// int channel = 2;

unsigned long lastStepTime = 0;
int currentStep = 0;
int totalSteps = 8;

int ledPins[8] = { 5, 6, 38, 8, 9, 10, 11, 12 };
int buttonStates[8] = { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW };
int lastButtonStates[8] = { LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW };

int lastButtonState = LOW;  // state of the button last time you checked
//#include <MIDI.h>
//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

int current_waveform = WAVEFORM_SINE;

void setup() {
  //MIDI.begin(MIDI_CHANNEL_OMNI); // Initialize MIDI with Omni channel (responds to all MIDI channels)
  //usbMIDI.sendNoteOn(note, velocity, channel);  // Replace 'note', 'velocity', and 'channel' with your values

  // Initialize serial communication
  Serial.begin(9600);
  AudioMemory(25);

  // always need this for synth
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);

  //waveform1.begin(0.8, 261, WAVEFORM_SINE);

  envelope1.noteOff();
  envelope1.attack(attack);
  envelope1.decay(decay);
  envelope1.sustain(sustain);
  envelope1.release(release);

  //setting the mixer levels
  mixer1.gain(0, 1);
  mixer1.gain(1, 1);
  mixer1.gain(2, 1);
  mixer1.gain(3, 1);

  waveform1.frequency(440);
  waveform1.amplitude(1.0);

  waveform1.begin(1.0, 440, WAVEFORM_SINE);
  //current_waveform = WAVEFORM_TRIANGLE;
  //waveform1.begin(current_waveform);

  //make pin 2 an input:
  pinMode(25, INPUT);
  pinMode(26, INPUT);
  pinMode(27, INPUT);
  pinMode(28, INPUT);
  pinMode(29, INPUT);
  pinMode(30, INPUT);
  pinMode(31, INPUT);
  pinMode(32, INPUT);

  pinMode(37, INPUT);

  pinMode(40, INPUT);  // ON AND OFF BUTTON FOR INSTRUMENT SELECT

  for (int i = 0; i < totalSteps; i++) {
    pinMode(ledPins[i], OUTPUT);
  }

    // Set the control pins (S0, S1, S2) as outputs
  pinMode(multiplexAPin, OUTPUT);
  pinMode(multiplexBPin, OUTPUT);
  pinMode(multiplexCPin, OUTPUT);
  
  pinMode(multiplexAnalogPin, INPUT);

  // Initialize the control pins
  digitalWrite(multiplexAPin, LOW);
  digitalWrite(multiplexBPin, LOW);
  digitalWrite(multiplexCPin, LOW);

}

void loop() {
  updateTempo();
  updateSequencer();
  updateLeds();
  updatebuttonChannelSelect();
  updateAdsrButton();
  updateEnvelope();
  updateKeyboard();
  // AudioNoInterrupts();
  // waveform1.frequency(4000);
  // waveform1.phase(360);
  // AudioInterrupts();

}

void updateKeyboard(){

  static int sequenceCounter = 0;

  static int counter = 0;
  for (int channel = 0; channel < 8; channel++) {
    // Select the channel using the control pins
    digitalWrite(multiplexAPin, (channel & 0x01) ? HIGH : LOW);
    digitalWrite(multiplexBPin, (channel & 0x02) ? HIGH : LOW);
    digitalWrite(multiplexCPin, (channel & 0x04) ? HIGH : LOW);
    // int channel = 0;
    // digitalWrite(multiplexAPin, LOW);
    // digitalWrite(multiplexBPin, LOW);
    // digitalWrite(multiplexCPin, LOW);
    
    // Read the analog value from the selected channel
    int sensorValue = analogRead(multiplexAnalogPin);

    int previousValue = keyboardState[channel];
    int newValue = (int)(sensorValue > 500);

    
    if(previousValue != newValue){
      if(newValue == 0){
        triggerNoteOff(currentNote);
      }
      if(newValue == 1){
        currentKeyPressed = channel;
        currentNote = keyboardNotes[currentKeyPressed];
        triggerNoteOn(currentNote);
        melodyNotes[sequenceCounter] = currentNote;
        sequenceCounter++;
        sequenceCounter %= 8;
      }
  
    }
    keyboardState[channel] = newValue;
    
    if(counter % 200 == 0){
      // Print the channel and its value to the serial monitor
      Serial.print("Channel ");
      Serial.print(channel);
      Serial.print(": ");
      Serial.println(sensorValue);

    }
    
    // Delay for a short time (optional)
    delay(1);
  }
  counter++;
}

void updateEnvelope() {

  static int counter_print = 0;

  attack = map(1024 - analogRead(A9), 0, 1023, 10, 1000);  // first number is min input, second max input, third min output, fourth max output its always 0 fist then 1023
  decay = map(1024 - analogRead(A8), 0, 1023, 10, 1000);
  sustain = (float)(1024 - analogRead(A7)) / 1023.0;
  release = map(1024 - analogRead(A6), 0, 1023, 10, 1000);

if(counter_print % 200 == 0){
  Serial.print("envelope ");
  Serial.print(attack);
  Serial.print(" ");
  Serial.print(decay);
  Serial.print(" ");
  Serial.print(sustain);
  Serial.print(" ");
  Serial.print(release);
  Serial.println(" ");
  counter_print = 0;
}
counter_print++;

  envelope1.attack(attack);
  envelope1.hold(0);
  envelope1.decay(decay);
  envelope1.sustain(sustain);
  envelope1.release(release);
}

void triggerNoteOn(int note){
  waveform1.frequency(freqFromMidiNote(note));
  envelope1.noteOn();

}
void triggerNoteOff(int note){
  envelope1.noteOff();
}

  void updateAdsrButton() {

    

    adsrButton = digitalRead(37);
    if (adsrButton != lastadsrButton) {
      if (adsrButton == HIGH) {

        current_waveform++;
        current_waveform %= 13;
        waveform1.begin(current_waveform);

        // if(currentNote > 80){
        //   currentNote = 60;
        // }
        // currentNote+=2;
        
        // triggerNoteOn(currentNote);

        // Serial.println("envelope start");
      }
      if (adsrButton == LOW) {
        //Serial.println("envelope off");
        //triggerNoteOff(currentNote);

      }
    }
    lastadsrButton = adsrButton;
  }

  // void onNoteOn(int buttonNum) {
  //   Serial.println("button pressed");
  //   //envelope1.noteOn();
  // }

  // void onNoteOff(int buttonNum) {
  //   Serial.println("button released");
  //   //envelope1.noteOff();
  //}

  void updateTempo() {
    tempo = map(analogRead(A17), 0, 1023, 50, 1000);
  }

  void updatebuttonChannelSelect() {
    channelselectbutton = digitalRead(40);
    if (channelselectbutton != lastchannelselectbutton) {
      if (channelselectbutton == LOW)

      {
        instrumentchangeflag = 1;

        Serial.println("instrument select");
      }

      lastchannelselectbutton = channelselectbutton;
    }
  }

  // clap

  void onButtonChangeclapNote(int pin, int step) {
    buttonStates[step] = digitalRead(pin);

    //   check if the current button state is different than the last state:
    if (buttonStates[step] != lastButtonStates[step]) {
      // do stuff if it is different here
      if (buttonStates[step] == LOW) {
        //      Serial.println(step);

        //Serial.println(drumNotes[2]);
        Serial.println("Button was just pressed.");
        //      if (drumNotes[step] == 0) {
        //        drumNotes[step] = 36;
        //      } else if (drumNotes[step] == 36) {
        //        drumNotes[step] = 39;
        //      } else if (drumNotes[step] == 39) {
        //        drumNotes[step] = 0;
        //      }

        //      clapNotes[step] = 1;
        if (clapNotes[step] == 1) {
          clapNotes[step] = 0;
        } else if (clapNotes[step] == 0) {
          clapNotes[step] = 1;
        }
        Serial.print(clapNotes[0]);
        Serial.print(clapNotes[1]);
        Serial.print(clapNotes[2]);
        Serial.print(clapNotes[3]);
        Serial.print(clapNotes[4]);
        Serial.print(clapNotes[5]);
        Serial.print(clapNotes[6]);
        Serial.println(clapNotes[7]);
      }
      // save button state for next comparison:
      lastButtonStates[step] = buttonStates[step];
    }
  }

  // KICK PART FOR PATTERN
  void onButtonChangekickNote(int pin, int step) {
    buttonStates[step] = digitalRead(pin);

    //   check if the current button state is different than the last state:
    if (buttonStates[step] != lastButtonStates[step]) {
      // do stuff if it is different here
      if (buttonStates[step] == LOW) {
        //      Serial.println(step);

        //Serial.println(drumNotes[2]);
        Serial.println("Button was just pressed.");
        //      if (drumNotes[step] == 0) {
        //        drumNotes[step] = 36;
        //      } else if (drumNotes[step] == 36) {
        //        drumNotes[step] = 39;
        //      } else if (drumNotes[step] == 39) {
        //        drumNotes[step] = 0;
        //      }

        //      clapNotes[step] = 1;
        if (kickNotes[step] == 1) {
          kickNotes[step] = 0;
        } else if (kickNotes[step] == 0) {
          kickNotes[step] = 1;
        }
        Serial.print(kickNotes[0]);
        Serial.print(kickNotes[1]);
        Serial.print(kickNotes[2]);
        Serial.print(kickNotes[3]);
        Serial.print(kickNotes[4]);
        Serial.print(kickNotes[5]);
        Serial.print(kickNotes[6]);
        Serial.println(kickNotes[7]);
      }
      // save button state for next comparison:
      lastButtonStates[step] = buttonStates[step];
    }
  }

  //
  // openhihatNotes PART FOR PATTERN

  void onButtonChangeopenhihatNote(int pin, int step) {
    buttonStates[step] = digitalRead(pin);

    //   check if the current button state is different than the last state:
    if (buttonStates[step] != lastButtonStates[step]) {
      // do stuff if it is different here
      if (buttonStates[step] == LOW) {

        Serial.println("Button was just pressed.");

        if (openhihatNotes[step] == 1) {
          openhihatNotes[step] = 0;
        } else if (openhihatNotes[step] == 0) {
          openhihatNotes[step] = 1;
        }
        Serial.print(openhihatNotes[0]);
        Serial.print(openhihatNotes[1]);
        Serial.print(openhihatNotes[2]);
        Serial.print(openhihatNotes[3]);
        Serial.print(openhihatNotes[4]);
        Serial.print(openhihatNotes[5]);
        Serial.print(openhihatNotes[6]);
        Serial.println(openhihatNotes[7]);
      }
      // save button state for next comparison:
      lastButtonStates[step] = buttonStates[step];
    }
    // delay(2);
  }

  // closedhihats
  void onButtonChangeclosedhihatNote(int pin, int step) {

    buttonStates[step] = digitalRead(pin);

    //   check if the current button state is different than the last state:
    if (buttonStates[step] != lastButtonStates[step]) {
      // do stuff if it is different here
      if (buttonStates[step] == LOW) {

        Serial.println("Button was just pressed.");

        if (closedhihatNotes[step] == 1) {
          closedhihatNotes[step] = 0;
        } else if (closedhihatNotes[step] == 0) {
          closedhihatNotes[step] = 1;
        }
        Serial.print(closedhihatNotes[0]);
        Serial.print(closedhihatNotes[1]);
        Serial.print(closedhihatNotes[2]);
        Serial.print(closedhihatNotes[3]);
        Serial.print(closedhihatNotes[4]);
        Serial.print(closedhihatNotes[5]);
        Serial.print(closedhihatNotes[6]);
        Serial.println(closedhihatNotes[7]);
      }
      // save button state for next comparison:
      lastButtonStates[step] = buttonStates[step];
    }
  }

  // snare

  void onButtonChangeNote(int pin, int step) {

    int* notesArray = kickNotes;

    // assign array based on channel
    if (channel == 1) {
      notesArray = clapNotes;
    }
    if (channel == 2) {
      notesArray = openhihatNotes;
    }
    if (channel == 3) {
      notesArray = closedhihatNotes;
    }
    if (channel == 4) {
      notesArray = snareNotes;
    }
    if (channel == 5) {
      notesArray = cowbellNotes;
    }
    if (channel == 6) {
      notesArray = cymbalNotes;
    }
    if (channel == 7) {
      notesArray = tomNotes;
    }

    buttonStates[step] = digitalRead(pin);

    //   check if the current button state is different than the last state:
    if (buttonStates[step] != lastButtonStates[step]) {
      // do stuff if it is different here
      if (buttonStates[step] == LOW) {
        if (instrumentchangeflag == 1) {
          channel = step;
          Serial.println(channel);
          instrumentchangeflag = 0;
        } else if (instrumentchangeflag == 0) {

          if (notesArray[step] == 1) {
            notesArray[step] = 0;
          } else if (notesArray[step] == 0) {
            notesArray[step] = 1;
          }
          Serial.print(notesArray[0]);
          Serial.print(notesArray[1]);
          Serial.print(notesArray[2]);
          Serial.print(notesArray[3]);
          Serial.print(notesArray[4]);
          Serial.print(notesArray[5]);
          Serial.print(notesArray[6]);
          Serial.println(notesArray[7]);
        }

        Serial.println("Button was just pressed.");
      }
      // save button state for next comparison:
      lastButtonStates[step] = buttonStates[step];
    }
  }

  void updateSequencer() {

    if (millis() > lastStepTime + tempo) {

      lastStepTime = millis();

      currentStep++;
      if (currentStep >= totalSteps) {
        currentStep = 0;
      }

      //Playing back melody sequence 
      // if (melodyNotes[currentStep] > 0) {
      //   triggerNoteOff(melodyNotes[currentStep]);// going to eventually replace whole thing to playSDwave.play(filename)
      //   triggerNoteOn(melodyNotes[currentStep]);// going to eventually replace whole thing to playSDwave.play(filename)
      // }

      int midiChannel = 1;
      if (kickNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(36, velocity, midiChannel); // going to eventually replace whole thing to playSDwave.play(filename)
      }
      if (clapNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(39, velocity, midiChannel);
      }
      if (openhihatNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(46, velocity, midiChannel);
      }
      if (closedhihatNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(42, velocity, midiChannel);
      }
      if (snareNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(38, velocity, midiChannel);
      }
      if (cowbellNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(50, velocity, midiChannel);
      }
      if (cymbalNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(49, velocity, midiChannel);
      }
      if (tomNotes[currentStep] > 0) {
        usbMIDI.sendNoteOn(47, velocity, midiChannel);
      }
    }

    // channel for different instruments
    // kick

    // should handle everything
    // TODO: can we make this a loop?
    onButtonChangeNote(25, 0);
    onButtonChangeNote(26, 1);
    onButtonChangeNote(27, 2);
    onButtonChangeNote(28, 3);
    onButtonChangeNote(29, 4);
    onButtonChangeNote(30, 5);
    onButtonChangeNote(31, 6);
    onButtonChangeNote(32, 7);

    //  if (channel == 0 ) {
    //    onButtonChangekickNote (25, 0);
    //    onButtonChangekickNote (26, 1);
    //    onButtonChangekickNote (27, 2);
    //    onButtonChangekickNote (28, 3);
    //    onButtonChangekickNote (29, 4);
    //    onButtonChangekickNote (30, 5);
    //    onButtonChangekickNote (31, 6);
    //    onButtonChangekickNote (32, 7);
    //  }
    //  // clap
    //  else if (channel == 1) {
    //    onButtonChangeclapNote (25, 0);
    //    onButtonChangeclapNote (26, 1);
    //    onButtonChangeclapNote (27, 2);
    //    onButtonChangeclapNote (28, 3);
    //    onButtonChangeclapNote (29, 4);
    //    onButtonChangeclapNote (30, 5);
    //    onButtonChangeclapNote (31, 6);
    //    onButtonChangeclapNote (32, 7);
    //  }
    //  //openhihat
    //  else if (channel == 2) {
    //    onButtonChangeopenhihatNote (25, 0);
    //    onButtonChangeopenhihatNote (26, 1);
    //    onButtonChangeopenhihatNote (27, 2);
    //    onButtonChangeopenhihatNote (28, 3);
    //    onButtonChangeopenhihatNote (29, 4);
    //    onButtonChangeopenhihatNote (30, 5);
    //    onButtonChangeopenhihatNote (31, 6);
    //    onButtonChangeopenhihatNote (32, 7);
    //  }
    //  //closedhihat
    //  else if (channel == 3) {
    //    onButtonChangeclosedhihatNote (25, 0);
    //    onButtonChangeclosedhihatNote (26, 1);
    //    onButtonChangeclosedhihatNote (27, 2);
    //    onButtonChangeclosedhihatNote (28, 3);
    //    onButtonChangeclosedhihatNote (29, 4);
    //    onButtonChangeclosedhihatNote (30, 5);
    //    onButtonChangeclosedhihatNote (31, 6);
    //    onButtonChangeclosedhihatNote (32, 7);
    //  }

    //  else {
    //    //channel = 0;
    //  }

    //else if (channel ==2){
    //  onButtonChangeclapNote (25, 0);
    //  onButtonChangeclapNote (26, 1);
    //  onButtonChangeclapNote (27, 2);
    //  onButtonChangeclapNote (28, 3);
    //  onButtonChangeclapNote (29, 4);
    //  onButtonChangeclapNote (30, 5);
    //  onButtonChangeclapNote (31, 6);
    //  onButtonChangeclapNote (32, 7);
    //}

    delay(10);
  }

  void updateLeds() {
    for (int i = 0; i < totalSteps; i++) {
      if (currentStep == i) {
        digitalWrite(ledPins[i], HIGH);
      } else {
        digitalWrite(ledPins[i], LOW);
      }
      // usbMIDI.sendNoteOn(note, velocity, channel);
    }
  }