#define SEQUENCER_MIN_BPM  50
#define SEQUENCER_MAX_BPM  177

// Ui config
#define LOCK_POT_SENSTIVITY 3

// hardware setup to fit different kinda of setups and arduino models
#define OCTAVE_POT_PIN            A3
#define NOTE_POT_PIN              A2
#define STEP_LENGTH_POT_PIN       A1
#define TEMPO_POT_PIN             A0

#define PREVIOUS_STEP_BUTTON_PIN  2
#define NEXT_STEP_BUTTON_PIN      3
#define REST_BUTTON_PIN           4
#define GLIDE_BUTTON_PIN          5
#define ACCENT_BUTTON_PIN         6
#define PLAY_STOP_BUTTON_PIN      7

#define PREVIOUS_STEP_LED_PIN     8
#define NEXT_STEP_LED_PIN         9
#define REST_LED_PIN              10
#define GLIDE_LED_PIN             11
#define ACCENT_LED_PIN            12
#define PLAY_STOP_LED_PIN         13

// User Interface data
uint16_t _step_edit = 0;
uint8_t _last_octave = 3;
uint8_t _last_note = 0;

uint8_t _bpm_blink_timer = 1;

void configureInterface()
{
  // Buttons config
  // use internal pullup for buttons
  pinMode(PREVIOUS_STEP_BUTTON_PIN, INPUT_PULLUP);
  pinMode(NEXT_STEP_BUTTON_PIN, INPUT_PULLUP);
  pinMode(REST_BUTTON_PIN, INPUT_PULLUP);
  pinMode(GLIDE_BUTTON_PIN, INPUT_PULLUP);
  pinMode(ACCENT_BUTTON_PIN, INPUT_PULLUP);
  pinMode(PLAY_STOP_BUTTON_PIN, INPUT_PULLUP);

  // Leds config
  pinMode(PREVIOUS_STEP_LED_PIN, OUTPUT);
  pinMode(NEXT_STEP_LED_PIN, OUTPUT);
  pinMode(REST_LED_PIN, OUTPUT);
  pinMode(GLIDE_LED_PIN, OUTPUT);
  pinMode(ACCENT_LED_PIN, OUTPUT);
  pinMode(PLAY_STOP_LED_PIN, OUTPUT);

  digitalWrite(PREVIOUS_STEP_LED_PIN, LOW);
  digitalWrite(NEXT_STEP_LED_PIN, LOW);
  digitalWrite(REST_LED_PIN, LOW);
  digitalWrite(GLIDE_LED_PIN, LOW);
  digitalWrite(ACCENT_LED_PIN, LOW);
  digitalWrite(PLAY_STOP_LED_PIN, LOW);  

  // getting first value state
  pressed(PREVIOUS_STEP_BUTTON_PIN);
  pressed(NEXT_STEP_BUTTON_PIN);
  pressed(REST_BUTTON_PIN);
  pressed(GLIDE_BUTTON_PIN);
  pressed(ACCENT_BUTTON_PIN);
  pressed(PLAY_STOP_BUTTON_PIN);

  // getting first values
  getPotChanges(OCTAVE_POT_PIN, 0, 10);
  getPotChanges(NOTE_POT_PIN, 0, 11);
  getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE);
  getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM);
  
  lockPotsState(true);

  //acidRandomize();
}

void processInterface()
{
  processButtons();
  processLeds();
  processPots();  
}

void tempoInterface(uint32_t * tick) 
{
  // BPM led indicator
  if ( !(*tick % (96)) || (*tick == 0) ) {  // first compass step will flash longer
    _bpm_blink_timer = 8;
    digitalWrite(PLAY_STOP_LED_PIN , HIGH);
  } else if ( !(*tick % (24)) ) {   // each quarter led on
    digitalWrite(PLAY_STOP_LED_PIN , HIGH);
  } else if ( !(*tick % _bpm_blink_timer) ) { // get led off
    digitalWrite(PLAY_STOP_LED_PIN , LOW);
    _bpm_blink_timer = 1;
  }
}

void sendPreviewNote(uint16_t step)
{
  unsigned long milliTime, preMilliTime;
  
  sendMidiMessage(NOTE_ON, _sequencer[step].note, _sequencer[step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY);

  // avoid delay() call because of uClock timmer1 usage
  //delay(200);
  preMilliTime = millis();
  while ( true ) {
    milliTime = millis();
    if (abs(milliTime - preMilliTime) >= 200) {
      break;
    }
  }
  
  sendMidiMessage(NOTE_OFF, _sequencer[step].note, 0);
}

void processPots()
{
  static int8_t octave, note, step_note;
  static int16_t tempo, step_length;

  octave = getPotChanges(OCTAVE_POT_PIN, 0, 10);
  if ( octave != -1 ) {  
    _last_octave = octave;
  }

  note = getPotChanges(NOTE_POT_PIN, 0, 11);
  if ( note != -1 ) { 
    _last_note = note;
  }

  // changes on octave or note pot?
  if ( octave != -1 || note != -1 ) {
    ATOMIC(_sequencer[_step_edit].note = (_last_octave * 8) + _last_note);
    if ( _playing == false && _sequencer[_step_edit].rest == false ) {
      sendPreviewNote(_step_edit);
    }
  }

  step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE);
  if ( step_length != -1 ) {  
    ATOMIC(_step_length = step_length);
    if ( _step_edit >= _step_length ) {
      _step_edit = _step_length-1;
    }
  }

  tempo = getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM);
  if ( tempo != -1 ) {   
    //uClock.setTempo(tempo);
  }
}
  
void processButtons()
{
  // play/stop
  if ( pressed(PLAY_STOP_BUTTON_PIN) ) {
    if ( _playing == false ) {
      // Starts the clock, tick-tac-tick-tac...
      uClock.start();
    } else {
      // stop the clock
      uClock.stop();
    }
  }

  // ramdom test
  //if ( pressed(PREVIOUS_STEP_BUTTON_PIN) && pressed(NEXT_STEP_BUTTON_PIN) ) {
    //acidRandomize();
    //return;
  //}

  // previous step edit
  if ( pressed(PREVIOUS_STEP_BUTTON_PIN) ) {
    if ( _step_edit != 0 ) {
      // add a lock here for octave and note to not mess with edit mode when moving steps around 
      lockPotsState(true);   
      --_step_edit;
    } else { // TODO: just for tests.. take this guy off here and put it on second page
      acidRandomize();
    }
    if ( _playing == false && _sequencer[_step_edit].rest == false ) {
      sendPreviewNote(_step_edit);
    }
  }

  // next step edit
  if ( pressed(NEXT_STEP_BUTTON_PIN) ) {
    if ( _step_edit < _step_length-1 ) {
      // add a lock here for octave and note to not mess with edit mode when moving steps around
      lockPotsState(true);     
      ++_step_edit;
    }
    if ( _playing == false && _sequencer[_step_edit].rest == false ) {
      sendPreviewNote(_step_edit);
    }    
  }

  // step rest
  if ( pressed(REST_BUTTON_PIN) ) {
    ATOMIC(_sequencer[_step_edit].rest = !_sequencer[_step_edit].rest);
    if ( _playing == false && _sequencer[_step_edit].rest == false ) {
      sendPreviewNote(_step_edit);
    }
  }

  // step glide
  if ( pressed(GLIDE_BUTTON_PIN) ) {
    ATOMIC(_sequencer[_step_edit].glide = !_sequencer[_step_edit].glide);
  }

  // step accent
  if ( pressed(ACCENT_BUTTON_PIN) ) {
    ATOMIC(_sequencer[_step_edit].accent = !_sequencer[_step_edit].accent);
    if ( _playing == false && _sequencer[_step_edit].rest == false ) {
      sendPreviewNote(_step_edit);
    }       
  }     
}
  
void processLeds()
{   
  // Editing First Step? 
  if ( _step_edit == 0 ) {
    digitalWrite(PREVIOUS_STEP_LED_PIN , HIGH);
  } else {
    digitalWrite(PREVIOUS_STEP_LED_PIN , LOW);
  }  

  // Editing Last Step? 
  if ( _step_edit == _step_length-1 ) {
    digitalWrite(NEXT_STEP_LED_PIN , HIGH);
  } else {
    digitalWrite(NEXT_STEP_LED_PIN , LOW);
  }  
  
  // Rest 
  if ( _sequencer[_step_edit].rest == true ) {
    digitalWrite(REST_LED_PIN , HIGH);
  } else {
    digitalWrite(REST_LED_PIN , LOW);
  }

  // Glide 
  if ( _sequencer[_step_edit].glide == true ) {
    digitalWrite(GLIDE_LED_PIN , HIGH);
  } else {
    digitalWrite(GLIDE_LED_PIN , LOW);
  }  

  // Accent 
  if ( _sequencer[_step_edit].accent == true ) {
    digitalWrite(ACCENT_LED_PIN , HIGH);
  } else {
    digitalWrite(ACCENT_LED_PIN , LOW);
  } 

  // shut down play led if we are stoped
  if ( _playing == false ) {
    digitalWrite(PLAY_STOP_LED_PIN , LOW);
  }
}

void acidRandomize() 
{
  // ramdom it all
  for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) {
    ATOMIC(_sequencer[i].note = random(36, 70)); // octave 2 to 4. octave 3 to 5 (40 - 83)
    ATOMIC(_sequencer[i].accent = random(0, 2));
    ATOMIC(_sequencer[i].glide = random(0, 2));
    ATOMIC(_sequencer[i].rest = random(0, 1));
  }
}