//
// a 2-dimensional ringringrain touch-pad(board)
//
// D. Yi @ 2023 4 9
//

////network!

#include <ConfigurableFirmata.h>

const char* ssid     = "nolink2";
const char* password = "2222277777";
const int NETWORK_PORT = 27016;

#include <WiFi.h>
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
WiFiServerStream serverStream(NETWORK_PORT);

////pad structure

// #define FLIPPED
// --> comment/uncomment for horizontal flipping of the installation.

#define NUMKEYS 42 // columns : 14, rows : 3
// --> how many keys are available for this pad

int keystat[NUMKEYS] = {0, };
// --> a buffer to store statuses of all keys
// --> needed to extract only 'the change' of key statuses

#define MAXCHANGES 10
int keychanges[MAXCHANGES] = {0, };
int n_keychg = 0;
// --> for monitoring only 'changed' keys.

#define NUMCOLS 14
#ifdef FLIPPED
int pins_cols[NUMCOLS] = {16, 15, 17, 18, 20, 19, 33, 34, 35, 36, 37, 38, 39, 40}; //14
// --> cols is horizontal offsets on the board
// --> to be read as input.
#else
int pins_cols[NUMCOLS] = {40, 39, 38, 37, 36, 35, 34, 33, 19, 20, 18, 17, 15, 16}; //14
// --> cols is horizontal offsets on the board
// --> to be read as input.
#endif

#define NUMROWS 2
int pins_rows[NUMROWS] = {7, 6}; //2
// #define NUMROWS 3
// int pins_rows[NUMROWS] = {8, 7, 6}; //3
// --> rows is vertical offsets on the board
// --> to be driven as output (or high impedance)

void setup()
{
  //serial
  Serial.begin(115200);

  //pinmodes
  // cols : to be read as input. (PULL-UP)
  for (int idx = 0; idx < NUMCOLS; idx++) {
    pinMode(pins_cols[idx], INPUT_PULLUP);
  }

  //wifi
  WiFi.mode(WIFI_STA);
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Mac address: " + WiFi.macAddress());

  //firmata
  Firmata.disableBlinkVersion();
  Firmata.begin(serverStream);
}

void loop()
{
  // perform a full scan of the key statuses!

  // update row voltages
  static int row = 0;
  pinMode(pins_rows[row], INPUT); // let old 'on' row goes 'hi-z' first.
  row++;
  if (row >= NUMROWS) {
    row = 0;
  }
  pinMode(pins_rows[row], OUTPUT);
  digitalWrite(pins_rows[row], LOW); // let new 'hi-z' row goes 'on'.. -> ready to scan.

  // 100us ~ 200us, waiting for the electricity ready...
  //delayMicroseconds(200);
  delay(2); // 2000us == 2ms

  // read all cols
  for (int col = 0; col < NUMCOLS; col++) {
    int key = row * NUMCOLS + col;
    if (key < NUMKEYS) {
      int cur_key = !digitalRead(pins_cols[col]); //logic inversion. (HIGH = noteoff, LOW = noteon.)

      // if it is 'changed',
      if (cur_key != keystat[key]) {
        // if there is a room to record this change,
        if (n_keychg < MAXCHANGES) {
          // make a memo.
          int keycoded = (key) * 10 + cur_key; // for example : C4 note-on --> 601 ( == 60*10 + 1)
          keychanges[n_keychg] = keycoded;
          n_keychg++;
          // send Firmata msg. over WIFI.
          Firmata.sendAnalog(0, keycoded);
          //@Pd
          //[pduino/arduino] will divide this by 1023.0 to map 0-1023 -> 0.0-1.0
          //so, to recover original values, multiply back by 1023.0.
        }
      }

      // okay. good. now, apply the change.
      keystat[key] = cur_key;
    }
  }

  // at the moment of 'full scan done'
  if (row == NUMROWS - 1) {

    // // print out the buffer status.
    // for (int key = 0; key < NUMKEYS; key++) {
    //   Serial.print(keystat[key]); Serial.print(" ");
    // }
    // Serial.println();

    // print out the changes.
    for (int chg = 0; chg < n_keychg; chg++) {
      Serial.print(keychanges[chg]);
      Serial.print(" ");
    }

    // clear 'keychanges' array
    for (int idx = 0; idx < MAXCHANGES; idx++) {
      keychanges[idx] = 0;
      n_keychg = 0;
    }

  }
  //
  // // discard all incoming MIDI msgs.
  // while (usbMIDI.read()) { }

  serverStream.maintain();
}
