#include <LiquidCrystal.h>
#include <TimerOne.h>
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();



LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
/* PIN Definitions*/
#define startPin A3
#define stopPin A5
#define enterPin A4
#define dinClockPin  A1
#define dinGatePin  A0
#define encoderPin1 3
#define encoderPin2 2
#define encoderSwitchPin 4
#define led1Pin 5
#define led2Pin 6

/* Status Flags */
boolean enterState = 0;
boolean enterStatePrev = 0;
boolean startState = 0;
boolean startStatePrev = 0;
boolean stopState = 0;
boolean stopStatePrev = 0;
boolean encoderPin1State = 0;
boolean encoderPin1StatePrev = 0;
boolean encoderPin2State = 0;
boolean encoderPin2StatePrev = 0;
boolean encoderSwitchState = 0;
boolean encoderSwitchStatePrev = 0;

uint16_t clockState = 0;
uint8_t quarterCount = 0;
uint8_t barCount = 0;
uint8_t quarterLed;

/* The variable keyPressOrder describes which key
   was pressed or released in which order.
   Uppercase = pressed, lowercase = released
   Example:
    Ww: only Write was pressed and released
    WPpw: WRITE pressed PLAY pressed, PLAY released, WRITE released
    WPwp: WRITE pressed PLAY pressed, WRITE released, PLAY released
    WSsSsSsw: STOP hit three times, while WRITE was held
    ...
*/
String keyPressOrder = "";
String keyPressOrderLast = "";
//String keyPressOrderLastPrint = ""; //only needed for debugging
boolean keyPressOrderLastIsSet = 0;


//uint8_t maxValue;

uint8_t mode = 0; /* 0=select param; 1=enter value */
uint8_t playmode = 0;
uint8_t param = 0;

uint8_t paramValue[8];
uint8_t paramValueMax[8] = {250  ,  5        ,  5        ,  2        ,   5        ,  5        ,  1        ,  16};
uint8_t paramValueMin[8] = {20   ,  0        ,  0        ,  0        ,   0        ,  0        ,  0        ,  1};
String paramName[8] = {"TEMPO   ", "DIVIDER ", "SHUFFLE ", "RST-MODE",  "BARCOUNT", "DIN/MIDI", "CLK-GATE", "CHANNEL "};

uint8_t tempo;
long period;
boolean clockOn = 0;

uint8_t channel = 1;
uint8_t channelPrev = 1;
uint8_t ccNumber;


void setup()
{
  MIDI.begin();
  /* */
  lcd.begin(8, 2);

  pinMode(enterPin, INPUT);
  pinMode(startPin, INPUT);
  pinMode(stopPin, INPUT);
  pinMode(dinClockPin, OUTPUT);
  pinMode(dinGatePin, OUTPUT);
  pinMode(encoderPin1, INPUT_PULLUP);
  pinMode(encoderPin2, INPUT_PULLUP);
  pinMode(encoderSwitchPin, INPUT_PULLUP);
  /*preset the tempo */

  paramValue[0] = 120; // tempo
  paramValue[1] = 0;   // divider = 1/16
  paramValue[2] = 0;   // shuffle 0%
  paramValue[3] = 0;   // Reset after Stop
  paramValue[4] = 0;   // barcount = 1 x 4/4
  paramValue[5] = 1;   // DIN/MIDI = MIDI
  paramValue[6] = 0;   // CLOCK OUT = always
  paramValue[7] = 1;   // MIDI-Channel

  tempo = paramValue[0];
  channel = paramValue[7];

  /* */
  Timer1.initialize(100000);
  Timer1.attachInterrupt( timerIsr );
  period = (312500 / tempo);
  Timer1.setPeriod(period);
  //*/
  lcd.setCursor(0, 0);
  lcd.print(F("CLOCKMAN"));
  lcd.setCursor(0, 1);
  lcd.print(F("V0.4  "));
  delay(1000);
  lcd.setCursor(0, 0);
  lcd.print(F("        "));
  lcd.setCursor(0, 1);
  lcd.print(F("        "));

}


void timerIsr()
{
  ++clockState;
  if (clockState == 8) {
    clockState = 0;
    digitalWrite(dinClockPin, HIGH);
    using namespace midi;
    MIDI.sendRealTime(Clock);
  }

  /* THIS IS ONLY IF USED AS BREAKABLE CLOCK CHAIN
    if (barCount == 0 ) MIDI.sendNoteOff(127, 1, 1);
    //*/
  if (clockState == 0) {
    ++quarterCount;
    ++barCount;
    if (quarterCount == 24) quarterCount = 0;
    if (barCount == 96) barCount = 0;
  }

  if (clockState == 4) {
    digitalWrite(dinClockPin, LOW);
  }

  if (quarterCount == 0) {
    quarterLed = 1;
  }
  if (quarterCount == 12) quarterLed = 0;
}


int getEncoderValue(int value, int maxValue, int minValue, int param) {
  //value = inValue;
  if (encoderPin1State == 1 && encoderPin1StatePrev == 0 && encoderPin2State == 1 && value > minValue) {
    if (param > 0 )  value--;
    if (param == 0 && encoderSwitchState == 0) value--;
    if (param == 0 && encoderSwitchState == 1 && (value > (minValue + 11))) value = (value - 10);

  }
  if (encoderPin2State == 1 && encoderPin2StatePrev == 0 && encoderPin1State == 1 && value < maxValue) {
    if (param > 0 ) value++;
    if (param == 0 && encoderSwitchState == 0) value++;
    if (param == 0 && encoderSwitchState == 1 && (value < (maxValue - 11))) value = (value + 10);

  }
  encoderPin1StatePrev = encoderPin1State;
  encoderPin2StatePrev = encoderPin2State;
  return value;
}



void printParamValue() {
  if (param == 0) lcd.print(paramValue[param]);
  if (param == 1) {
    if (paramValue[param] == 0) lcd.print(F("1/16"));
    if (paramValue[param] == 1) lcd.print(F("1/8 "));
    if (paramValue[param] == 2) lcd.print(F("1/4 "));
    if (paramValue[param] == 3) lcd.print(F("1/12"));
    if (paramValue[param] == 4) lcd.print(F("1/24"));
    if (paramValue[param] == 5) lcd.print(F("1/48"));
  }
  if (param == 2) {
    if (paramValue[param] == 0) lcd.print(F(" 0%"));
    if (paramValue[param] == 1) lcd.print(F("10%"));
    if (paramValue[param] == 2) lcd.print(F("20%"));
    if (paramValue[param] == 3) lcd.print(F("30%"));
    if (paramValue[param] == 4) lcd.print(F("40%"));
    if (paramValue[param] == 5) lcd.print(F("50%"));
  }
  if (param == 3) {
    if (paramValue[param] == 0) lcd.print(F("A-154"));
    if (paramValue[param] == 1) lcd.print(F("A-155"));
    if (paramValue[param] == 2) lcd.print(F("MelDIC"));  }
  if (param == 4) {
    if (paramValue[param] == 0) lcd.print(F("1 bar "));
    if (paramValue[param] == 1) lcd.print(F("2 bars"));
    if (paramValue[param] == 2) lcd.print(F("4 bars"));
    if (paramValue[param] == 3) lcd.print(F("3/4"));
    if (paramValue[param] == 4) lcd.print(F("5/4"));
    if (paramValue[param] == 5) lcd.print(F("7/8"));
  }
  if (param == 5) {
    if (paramValue[param] == 0) lcd.print(F("DIN-1 "));
    if (paramValue[param] == 1) lcd.print(F("DIN-2 "));
    if (paramValue[param] == 2) lcd.print(F("DIN-3 "));
    if (paramValue[param] == 3) lcd.print(F("MIDI-1"));
    if (paramValue[param] == 4) lcd.print(F("MIDI-2"));
    if (paramValue[param] == 5) lcd.print(F("MIDI-3"));
  }
  if (param == 6) {
    if (paramValue[param] == 0) lcd.print(F("always"));
    if (paramValue[param] == 1) lcd.print(F("gated "));
  }
  if (param == 7) lcd.print(paramValue[param]);
}


void clearScreen() {
  lcd.setCursor(0, 0);
  lcd.print(F("        "));
  lcd.setCursor(0, 1);
  lcd.print(F("        "));
}


void loop()
{
  /*
    lcd.setCursor(0, 0);
    lcd.print(startState);
    lcd.print(stopState);
    lcd.print(enterState);
    lcd.print(encoderPin1State);
    lcd.print(encoderPin2State);
    lcd.println(encoderSwitchState);

    lcd.setCursor(15, 1);
    lcd.print(mode);
    lcd.print(F(" "));
    // */

  /* We read the buttons and the encoder */
  startState = !digitalRead(startPin);
  stopState = !digitalRead(stopPin);
  enterState = !digitalRead(enterPin);
  encoderPin1State = !digitalRead(encoderPin1);
  encoderPin2State = !digitalRead(encoderPin2);
  encoderSwitchState = !digitalRead(encoderSwitchPin);

  /* Now we read the order of the button presses */
  /* Prepare the variables */
  if ((startStatePrev == 0 && stopStatePrev == 0 && enterStatePrev == 0 && encoderSwitchStatePrev == 0) && (startState == 1 || stopState == 1 || enterState == 1 || encoderSwitchState == 1))  {
    keyPressOrder = "";
    if (keyPressOrderLastIsSet == 1) {
      keyPressOrderLastIsSet = 0;
    }
  }
  /* Read button presses and fill the variable */
  if (startStatePrev == 0 && startState == 1) {
    keyPressOrder = keyPressOrder + "P";
    startStatePrev = 1;
  }
  if (startStatePrev == 1 && startState == 0) {
    keyPressOrder = keyPressOrder + "p";
    startStatePrev = 0;
  }
  if (stopStatePrev == 0 && stopState == 1) {
    keyPressOrder = keyPressOrder + "S";
    stopStatePrev = 1;
  }
  if (stopStatePrev == 1 && stopState == 0) {
    keyPressOrder = keyPressOrder + "s";
    stopStatePrev = 0;
  }
  if (enterStatePrev == 0 && enterState == 1) {
    keyPressOrder = keyPressOrder + "E";
    enterStatePrev = 1;
  }
  if (enterStatePrev == 1 && enterState == 0) {
    keyPressOrder = keyPressOrder + "e";
    enterStatePrev = 0;
  }
  if (encoderSwitchStatePrev == 0 && encoderSwitchState == 1) {
    keyPressOrder = keyPressOrder + "C";
    encoderSwitchStatePrev = 1;
  }
  if (encoderSwitchStatePrev == 1 && encoderSwitchState == 0) {
    keyPressOrder = keyPressOrder + "c";
    encoderSwitchStatePrev = 0;
  }
  /* make the variable readable */
  if (startState == 0 && stopState == 0 && enterState == 0 && encoderSwitchState == 0 ) {
    if (keyPressOrderLastIsSet == 0) {
      keyPressOrderLast = keyPressOrder;
      //keyPressOrderLastPrint = keyPressOrderLast;
      keyPressOrderLastIsSet = 1;
    }
  }



  /* No matter in which State: Play and Stop Buttons alwas do what they say */
  if (keyPressOrderLast == "Pp") { //PLAY
    using namespace midi;
    MIDI.sendRealTime(Start);
    clockState = 0;
    barCount = 0;
    quarterCount = 0;
    playmode = 1;
    digitalWrite(dinGatePin, HIGH);
    //lcd.setCursor(0, 1);
    //lcd.print(F("PLAY "));

    keyPressOrderLast = "";
  }

  if (keyPressOrderLast == "Ss") {
    using namespace midi;
    MIDI.sendRealTime(Stop);
    playmode = 0;
    digitalWrite(dinGatePin, LOW);
    //lcd.setCursor(5, 1);
    //lcd.print(F("STOP "));
    keyPressOrderLast = "";
  }

  if (mode == 0) {
    if (keyPressOrderLast == "Ee") {
      mode = 1;
      keyPressOrderLast = "";
    }
    param = getEncoderValue(param, 7, 0, 10);
  }

  /* edit mode */
  if (mode == 1) {
    paramValue[param] = getEncoderValue(paramValue[param], paramValueMax[param], paramValueMin[param], param);
    /* parameter edit */
    if  (param > 0 && param < 7) {
      if (keyPressOrderLast == "Cc") { /* encoder buttone sends value while editing */
        ccNumber = 69 + param;
        MIDI.sendControlChange(ccNumber, paramValue[param], channel);
        keyPressOrderLast = "";
      }
    }
    if (param == 0) {
      tempo = paramValue[param];
      period = (312500 / tempo);
      if (enterState == 1) Timer1.setPeriod(period);
    }
    if (keyPressOrderLast == "Ee") {
      if (param == 0) {
        Timer1.setPeriod(period);
      }
      if  (param > 0 ) {
        ccNumber = 69 + param;
        MIDI.sendControlChange(ccNumber, paramValue[param], channel);
      }
      if (param == 7 ) {
        channel = paramValue[param];
      }
      mode = 0;
      keyPressOrderLast = "";
    }

  }





  /* */
  lcd.setCursor(0, 0);
  lcd.print(paramName[param]);
  //lcd.setCursor(13, 1);
  //lcd.print(param);

  if (mode == 0) {
    lcd.setCursor(0, 1);
    lcd.print(F(" "));
    printParamValue();
    lcd.print(F("      "));
  }

  if (mode == 1) {
    lcd.setCursor(0, 1);
    lcd.print(F("["));
    printParamValue();
    lcd.print(F("]      "));
  }

  //*/

  if (quarterLed == 1) digitalWrite(led1Pin, HIGH);
  if (quarterLed == 0) digitalWrite(led1Pin, LOW);
  if (playmode == 1) digitalWrite(led2Pin, HIGH);
  if (playmode == 0) digitalWrite(led2Pin, LOW);


  if (startState == 0 && startStatePrev == 1) startStatePrev = 0;
  if (stopState == 0 && stopStatePrev == 1) stopStatePrev = 0;
  if (enterState == 0 && enterStatePrev == 1) enterStatePrev = 0;
  if (encoderSwitchState == 0 && encoderSwitchStatePrev == 1) encoderSwitchStatePrev = 0;

}


/*

*/
