#include <Arduino.h>
#include <Control_Surface.h>
#include <Adafruit_MPR121.h>

#define s * 1000

#define NOTE_VELOCITY 127
#define ON 127
#define OFF 0

#ifndef __PLATFORMIO_BUILD_DEBUG__
// Instantiate a MIDI Interface to use
USBMIDI_Interface midi;
#else  /* __PLATFORMIO_BUILD_DEBUG__ */
// Instantiate a MIDI Interface to use
USBDebugMIDI_Interface midi{115200};
#endif /* __PLATFORMIO_BUILD_DEBUG__ */

Channel channel{CHANNEL_2};

/* Pins */
/* GPIO (I2C) */
const uint8_t gpio_int{0};
const uint8_t sda{2};
const uint8_t scl{3};
/* Multiplexer */
const pin_t mux_sig{A10};
const Array<pin_t, 4> mux_addr{18, 19, 20, 21};
const pin_t mux_en{15};
/* ShiftRegister (OUT) */
#define SHIFT_OUTPUTSIZE 16
const uint8_t shift_data{14};
const uint8_t shift_latch{16};
const uint8_t shift_clock{1};
/* keyboard (IN) */
#define KEYBOARD_INSIZE 6
const uint8_t keyboard_in[KEYBOARD_INSIZE]{4, 5, 6, 7, 8, 9};

/* Components */
/* CapaSensor (I2C) */
Adafruit_MPR121 capa_sensor = Adafruit_MPR121();
/* GPIO (I2C) */
/* Multiplexer */
CD74HC4067 multiplexer{mux_sig, mux_addr, mux_en};
/* ShiftRegister (OUT) */
// ShiftRegisterOut<2> shiftRegister(shift_data, shift_clock, shift_latch, MSBFIRST);
/* keyboard (IN) */

CCPotentiometer potentiometers[] = {
  {multiplexer.pin(0), {1, channel}},
  {multiplexer.pin(1), {2, channel}},
  {multiplexer.pin(2), {3, channel}},
  {multiplexer.pin(3), {4, channel}},
  {multiplexer.pin(4), {5, channel}},
  {multiplexer.pin(5), {6, channel}},
  {multiplexer.pin(6), {7, channel}},
  {multiplexer.pin(7), {8, channel}},
  {multiplexer.pin(8), {9, channel}},
  {multiplexer.pin(9), {10, channel}},
};

byte matrix[16][6][2] = {
  /*        0   |    1   |    2   |    3   |    4   |    5    */
  /*0*/	{{1,  0}, {1,  1}, {1,  2}, {1,  3}, {1,  4}, {1,  5}},
  /*1*/	{{1,  6}, {1,  7}, {1,  8}, {1,  9}, {1, 10}, {1, 11}},
  /*2*/	{{1, 18}, {1, 19}, {1, 20}, {1, 21}, {1, 22}, {1, 23}},
  /*3*/	{{1, 48}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*4*/	{{1, 42}, {1, 43}, {1, 44}, {1, 45}, {1, 46}, {1, 47}},
  /*5*/	{{1, 36}, {1, 37}, {1, 38}, {1, 39}, {1, 40}, {1, 41}},
  /*6*/	{{1, 30}, {1, 31}, {1, 32}, {1, 33}, {1, 34}, {1, 35}},
  /*7*/	{{1, 24}, {1, 25}, {1, 26}, {1, 27}, {1, 28}, {1, 29}},
  /*8*/	{{1, 12}, {1, 13}, {1, 14}, {1, 15}, {1, 16}, {1, 17}},
  /*9*/	{{0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*a*/	{{0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*b*/	{{0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*c*/	{{2,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*d*/	{{0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*e*/	{{0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
  /*f*/	{{2,  1}, {0,  0}, {0,  0}, {0,  0}, {0,  0}, {0,  0}},
};

bool ref[16][6] = {{false}};

unsigned char shift = 12;

void setup() {
	for (size_t i = 0; i < KEYBOARD_INSIZE; ++i) {
		ExtIO::pinMode(keyboard_in[i], INPUT);
	}

	for (size_t i = 0; i < 16; ++i) {
		for (size_t j = 0; j < 6; j++)
		{
			ref[i][j] = false;
		}
	}

	pinMode(shift_data, OUTPUT);
	pinMode(shift_latch, OUTPUT);
	pinMode(shift_clock, OUTPUT);

	capa_sensor.begin(0x5A);

	Control_Surface.begin();
}

void scanPin(int pin);

void button_matrix_mgt(void);

void capa_sensor_mgt(void);

void loop()
{
	button_matrix_mgt();

	capa_sensor_mgt();

	Control_Surface.loop();
}

void button_matrix_mgt(void) {
  for (size_t i = 0; i < 16; ++i) {
    scanPin(i);
    uint8_t rowValue[KEYBOARD_INSIZE];
    for (size_t j = 0; j < 6; ++j) {
      if ((rowValue[i] = ExtIO::digitalRead(keyboard_in[j])) == HIGH) {
        if (!ref[i][j]) {
#ifndef DEBUG_MATRIX
          byte type = matrix[i][j][0];
          byte key = matrix[i][j][1];
#ifdef __PLATFORMIO_BUILD_DEBUG__
          midi.sendNoteOn({type, CHANNEL_3}, key);
#endif /* __PLATFORMIO_BUILD_DEBUG__ */
          if (type == 1) {
            midi.sendNoteOn({key + shift, channel}, NOTE_VELOCITY);
          } else if (type == 2) {
            // todo : note off
            if (key == 0 && shift >= 12) {
              shift -= 12;
            } else if (key == 1 && shift <= 6 * 12) {
              shift += 12;
            }
          }
#else  /* DEBUG_MATRIX */
          midi.sendNoteOn(i, j);
#endif /* DEBUG_MATRIX */
          ref[i][j] = true;
        }
      } else {
        if (ref[i][j]) {
#ifndef DEBUG_MATRIX
          if (matrix[i][j][0] == 1) {
            midi.sendNoteOff({matrix[i][j][1] + shift, channel}, NOTE_VELOCITY);
          }
#else  /* DEBUG_MATRIX */
          midi.sendNoteOff(i, j);
#endif /* DEBUG_MATRIX*/
          ref[i][j] = false;
        }
      }
    }
  }
}

void capa_sensor_mgt(void) {
	// Get the currently touched pads
	int static lasttouched;
	int currtouched = capa_sensor.touched();

	for (uint8_t i = 0; i < 12; i++) {
		// it if *is* touched and *wasnt* touched before, alert!
		if ((currtouched & _BV(i)) && !(lasttouched & _BV(i))) {
            midi.sendCC({i, channel}, ON);
        }
		// if it *was* touched and now *isnt*, alert!
        if (!(currtouched & _BV(i)) && (lasttouched & _BV(i))) {
          midi.sendCC({i, channel}, OFF);
        }
    }

	// reset our state
	lasttouched = currtouched;
}

void scanPin(int pin) {
  ExtIO::digitalWrite(shift_latch, LOW);
  if (pin < 8) {
    byte bitsToSend = 0b00000001 << pin;
    ExtIO::shiftOut(shift_data, shift_clock, MSBFIRST, 0b00000000);
    ExtIO::shiftOut(shift_data, shift_clock, MSBFIRST, bitsToSend);
  } else {
    byte bitsToSend = 0b00000001 << (pin - 8);
    ExtIO::shiftOut(shift_data, shift_clock, MSBFIRST, bitsToSend);
    ExtIO::shiftOut(shift_data, shift_clock, MSBFIRST, 0b00000000);
  }
  ExtIO::digitalWrite(shift_latch, HIGH);
}