#include <Keypad.h>
#include <Joystick.h>
#include <Encoder.h>

// -------------------- Joystick Setup --------------------
Joystick_ Joystick(
  JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_JOYSTICK,
  34,   // total buttons (matrix + encoders + latching toggle)
  0,    // hat switches
  false, false, false, // X, Y, Z axes
  false, false, false, // Rx, Ry, Rz
  false, false,        // rudder, throttle
  false, false, false  // accelerator, brake, steering
);

// -------------------- Button Matrix --------------------
const byte ROWS = 5;
const byte COLS = 5;

byte rowPins[ROWS] = {A3, A2, A1, A0, 15};
byte colPins[COLS] = {14, 16, 10, 9, 8};

// Keymap: assign button numbers (0–24) to matrix
char keys[ROWS][COLS] = {
  {0, 1, 2, 3, 4},
  {5, 6, 7, 8, 9},
  {10, 11, 12, 13, 14},
  {15, 16, 17, 18, 19},
  {20, 21, 22, 23, 24}
};

Keypad buttonBox = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// -------------------- Encoders --------------------
Encoder enc1(7, 6);
Encoder enc2(5, 4);
Encoder enc3(3, 2);
Encoder enc4(0, 1); // RX=0, TX=1

long lastPos[4] = {0, 0, 0, 0};

// Map encoders to joystick buttons
int encBtnA[4] = {25, 27, 29, 31};
int encBtnB[4] = {26, 28, 30, 32};

// -------------------- Latching Toggle --------------------
const int latchingButtonNumber = 5; // matrix key number of the latching toggle

// -------------------- Setup --------------------
void setup() {
  Joystick.begin();
  for (int i = 0; i < 34; i++) {
    Joystick.setButton(i, 0);
  }
}

// -------------------- Pulse Helper --------------------
void pulseButton(int btn) {
  Joystick.setButton(btn, 1);
  Joystick.sendState();
  delay(15); // long enough for USB HID to catch
  Joystick.setButton(btn, 0);
  Joystick.sendState();
}

// -------------------- Loop --------------------
void loop() {
  // --- Scan Button Matrix ---
  if (buttonBox.getKeys()) {
    for (int i = 0; i < LIST_MAX; i++) {
      if (buttonBox.key[i].stateChanged) {
        int btn = buttonBox.key[i].kchar;

        // Special handling for the latching toggle
        if (btn == latchingButtonNumber) {
          // Any change = one pulse
          pulseButton(5);
        } else {
          // Normal momentary buttons
          if (buttonBox.key[i].kstate == PRESSED) {
            Joystick.setButton(btn, 1);
          } else if (buttonBox.key[i].kstate == RELEASED) {
            Joystick.setButton(btn, 0);
          }
        }
      }
    }
  }

  // --- Handle Encoders ---
  long newPos;

  newPos = enc1.read();
  if (newPos != lastPos[0]) {
    if (newPos > lastPos[0]) pulseButton(encBtnA[0]);
    else pulseButton(encBtnB[0]);
    lastPos[0] = newPos;
  }

  newPos = enc2.read();
  if (newPos != lastPos[1]) {
    if (newPos > lastPos[1]) pulseButton(encBtnA[1]);
    else pulseButton(encBtnB[1]);
    lastPos[1] = newPos;
  }

  newPos = enc3.read();
  if (newPos != lastPos[2]) {
    if (newPos > lastPos[2]) pulseButton(encBtnA[2]);
    else pulseButton(encBtnB[2]);
    lastPos[2] = newPos;
  }

  newPos = enc4.read();
  if (newPos != lastPos[3]) {
    if (newPos > lastPos[3]) pulseButton(encBtnA[3]);
    else pulseButton(encBtnB[3]);
    lastPos[3] = newPos;
  }

  delay(1); // keep loop fast but not too tight
}
