#include "Controllers.h"

// MIDI

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void noteOn(byte channel, byte pitch, byte velocity) {
  #ifndef DUMMY	
    midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOn);
  #endif
  #ifdef DEBUG
    Serial.print("Note on:");
    Serial.print(channel);
    Serial.print(",");
    Serial.print(pitch);
    Serial.print(",");
    Serial.println(velocity);
  #endif
}

void noteOff(byte channel, byte pitch, byte velocity) {
  #ifndef DUMMY	
    midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOff);
  #endif
  #ifdef DEBUG
    Serial.print("Note off:");
    Serial.print(channel);
    Serial.print(",");
    Serial.print(pitch);
    Serial.print(",");
    Serial.println(velocity);
  #endif
}

void controlChange(byte channel, byte control, byte value) {
  #ifndef DUMMY	
    midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
    MidiUSB.sendMIDI(event);
  #endif
  #ifdef DEBUG
    Serial.print("Control change:");
    Serial.print(channel);
    Serial.print(",");
    Serial.print(control);
    Serial.print(",");
    Serial.println(value);
  #endif
}

// Constructor and methods for analog controls and buttons (encoders use a different library)

AnalogControl::AnalogControl(int pin, byte effect, int middle) {
  this->pin = pin;
  this->effect = effect;
  this->middle = middle;
}

void AnalogControl::init() {
  pinMode(pin,INPUT);	
}

void AnalogControl::checkStatus() {
  value = analogRead(pin);
  if (value < 0+STICKYNESS){
      value = 0;
  } else if (value > 1023-STICKYNESS) {
      value = 1023;
  } else if (value > middle-STICKYNESS && value < middle+STICKYNESS) {
      value = middle;
  }
  if ( abs(value - prev_value) > DEAD_ZONE ){
    controlChange(1,effect, map(value,0,1023,0,127) );
    prev_value = value;
    #ifndef DUMMY
      MidiUSB.flush();
    #endif
  }
}

DigitalButton::DigitalButton(int pin, byte effect, byte effect_toggle, byte effect_long, byte effect_long_toggle) {
  this->effect = effect;
  this->effect_toggle = effect_toggle;
  this->effect_long = effect_long;
  this->effect_long_toggle = effect_long_toggle;
  this->pin = pin;
}

void DigitalButton::init() {
  pinMode(pin,INPUT_PULLUP);
  pressed = false;
  toggled = false;
  long_pressed = false;
  long_toggled = false;
}

void DigitalButton::checkStatus() {
   bool status = digitalRead(pin);
	
  if (status == LOW && pressed == false) {
      pressed = true;
      long_pressed = false;
      debounce = millis();
    }
    if (pressed && (millis() - debounce <= 10) && status == HIGH) {
      pressed = false;
      return;
    }
    if (pressed && (millis() - debounce > 10) && status == HIGH) {
        if (long_pressed == true) {
          long_pressed = false;
        } else if (effect_toggle == 0) {
          controlChange(1,effect,1);
        } else if (toggled) {
          controlChange(1,effect,1);
          toggled = false;
        } else {
          controlChange(1,effect_toggle,1);
          toggled = true;
        }
      #ifndef DUMMY
        MidiUSB.flush();        
      #endif
      pressed = false;
    }
    if (pressed && (millis() - debounce > LONG_INTERVAL) && (status == LOW) && (long_pressed == false)) {
      long_pressed = true;
      if (effect_long_toggle == 0) {
        controlChange(1,effect_long,1);
      } else if (long_toggled) {
        controlChange(1,effect_long,1);
        long_toggled = false;     
      } else {
        controlChange(1,effect_long_toggle,1);
        long_toggled = true;              
      }
      #ifndef DUMMY
	    MidiUSB.flush();
	  #endif
    }
}

MCP23017_Expander::MCP23017_Expander(int address, int effects[16], int effects_toggle[16], int effects_long[16], int effects_long_toggle[16]) {
	this->address = address;
	for (int i = 0; i < 16; i++) {
		this->effects[i] = effects[i];
		this->effects_toggle[i] = effects_toggle[i];
		this->effects_long[i] = effects_long[i];
		this->effects_long_toggle[i] = effects_long_toggle[i];
	}
}

void MCP23017_Expander::init() {
	Adafruit_MCP23X17 mcp;
	if (!mcp.begin_I2C(address)) {
		#ifdef DEBUG
			Serial.print("MCP23017 not found at address ");
			Serial.println(address,HEX);
		#endif
		while(true);
	}

	for (int i = 0; i < 16; i++) {
		mcp.pinMode(i,INPUT_PULLUP);
		pressed[i] = false;
		toggled[i] = false;
		long_pressed[i] = false;
		long_toggled[i] = false;
	}
	this->mcp = mcp;
}

void MCP23017_Expander::checkStatus() {
	for (int i = 0; i < 16; i++){
		if (effects[i] == 0 && effects_toggle[i] == 0 && effects_long[i] == 0 && effects_long_toggle[i] == 0) {
			return;
		}
		bool status = mcp.digitalRead(i);
		if (status == LOW && pressed[i] == false){
			Serial.println("button pressed");
			pressed[i] = true;
			long_pressed[i] = false;
			debounce[i] = millis();
		}
		if (pressed[i] && (millis() - debounce[i] <= 10) && status == HIGH) {
			pressed[i] = false;
			return;
		}
		if (pressed[i] && (millis() - debounce[i] > 10) && status == HIGH) {
			if (long_pressed[i] == true) {
				long_pressed[i] = false;
			} else if (effects_toggle[i] == 0) {
				controlChange(1,effects[i],1);
			} else if (toggled[i]) {
				controlChange(1,effects[i],1);
				toggled[i] = false;
			} else {
				controlChange(1,effects_toggle[i],1);
				toggled[i] = true;
			}
			#ifndef DUMMY
			  MidiUSB.flush();
			#endif
			pressed[i] = false;
		}
		if (pressed[i] && (millis() - debounce[i] > LONG_INTERVAL) && (status == LOW) && (long_pressed[i] == false)) {
			long_pressed[i] = true;
			if (effects_long_toggle == 0) {
				controlChange(1,effects_long[i],1);
			} else if (long_toggled[i]) {
				controlChange(1,effects_long[i],1);
				long_toggled[i] = false;
			} else {
				controlChange(1,effects_long_toggle[i],1);
				long_toggled[i] = true;
			}
			#ifndef DUMMY
			  MidiUSB.flush();
			#endif
		}
	}
}

Mux::Mux(int pinSIG, int effects[16], int mid_deadzone[16]) {
  this->pinSIG = pinSIG;
  for (int i = 0; i < 16; i++) {
    this->effects[i] = effects[i];
    this->mid_deadzone[i] = effects[i];
  }
}

void Mux::init() {
  pinMode(pinSIG,INPUT);
  pinMode(pinS0,OUTPUT);
  pinMode(pinS1,OUTPUT);
  pinMode(pinS2,OUTPUT);
  pinMode(pinS3,OUTPUT);	
}

void Mux::checkStatus(byte selector){
  if (effects[selector] == 0) {
	  return;
  }
  digitalWrite(pinS0, bitRead(selector,0));
  digitalWrite(pinS1, bitRead(selector,1));
  digitalWrite(pinS2, bitRead(selector,2));
  digitalWrite(pinS3, bitRead(selector,3));
  int value = analogRead(pinSIG);
  if (value < STICKYNESS) {
    value = 0;
  } else if (value > 1023-STICKYNESS) {
    value = 1023;
  } else if (value > mid_deadzone[selector]-STICKYNESS && value < mid_deadzone[selector]+STICKYNESS) {
    value = mid_deadzone[selector];
  }

  if (abs(value - prev_value[selector]) > DEAD_ZONE ) {
    byte midi_value = map(value,0,1023,0,127);
    controlChange(1,effects[selector],midi_value);
    #ifdef DEBUG
      Serial.print("Mux effect:");
      Serial.print(effects[selector]);
      Serial.print(", address:");
      Serial.print(selector);
      Serial.print(":");
      Serial.print(bitRead(selector,0));
      Serial.print(bitRead(selector,1));
      Serial.print(bitRead(selector,2));
      Serial.print(bitRead(selector,3));
      Serial.print(", prev.value:");
      Serial.print(prev_value[selector]);
      Serial.print(", value:");
      Serial.println(value);
    #endif
    prev_value[selector] = value;
    #ifndef DUMMY
      MidiUSB.flush();
    #endif
  }
}

//Callback function for the encoders
void eb_Encoder(EncoderButton& eb) {
 int inc = eb.increment();
 if (inc > 0) {
  controlChange(1,eb.userId()+1,1);
 } else {
  controlChange(1,eb.userId(),1);
 }
 #ifndef DUMMY
   MidiUSB.flush();
 #endif
}
