/*
 * KylinBot Arduino Uno Firmware
 *
 * Listens for JSON commands on Serial (115200 baud), executes gpio_read/gpio_write,
 * responds with JSON. Compatible with KylinBot SerialPeripheral protocol.
 *
 * Protocol (newline-delimited JSON):
 *   Request:  {"id":"1","cmd":"gpio_write","args":{"pin":13,"value":1}}
 *   Response: {"id":"1","ok":true,"result":"done"}
 *
 * Arduino Uno: Pin 13 has built-in LED. Digital pins 0-13 supported.
 *
 * 1. Open in Arduino IDE
 * 2. Select Board: Arduino Uno
 * 3. Select correct Port (Tools -> Port)
 * 4. Upload
 */

#define BAUDRATE 115200
#define MAX_LINE 256

char lineBuf[MAX_LINE];
int lineLen = 0;

// Parse integer from JSON: "pin":13 or "value":1
int parseArg(const char* key, const char* json) {
  char search[32];
  snprintf(search, sizeof(search), "\"%s\":", key);
  const char* p = strstr(json, search);
  if (!p) return -1;
  p += strlen(search);
  return atoi(p);
}

// Extract "id" for response
void copyId(char* out, int outLen, const char* json) {
  const char* p = strstr(json, "\"id\":\"");
  if (!p) {
    out[0] = '0';
    out[1] = '\0';
    return;
  }
  p += 6;
  int i = 0;
  while (i < outLen - 1 && *p && *p != '"') {
    out[i++] = *p++;
  }
  out[i] = '\0';
}

// Check if cmd is present
bool hasCmd(const char* json, const char* cmd) {
  char search[64];
  snprintf(search, sizeof(search), "\"cmd\":\"%s\"", cmd);
  return strstr(json, search) != NULL;
}

void handleLine(const char* line) {
  char idBuf[16];
  copyId(idBuf, sizeof(idBuf), line);

  if (hasCmd(line, "ping")) {
    Serial.print("{\"id\":\"");
    Serial.print(idBuf);
    Serial.println("\",\"ok\":true,\"result\":\"pong\"}");
    return;
  }

  // Phase C: Dynamic discovery — report GPIO pins and LED pin
  if (hasCmd(line, "capabilities")) {
    Serial.print("{\"id\":\"");
    Serial.print(idBuf);
    Serial.print("\",\"ok\":true,\"result\":\"{\\\"gpio\\\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],\\\"led_pin\\\":13}\"}");
    Serial.println();
    return;
  }

  if (hasCmd(line, "gpio_read")) {
    int pin = parseArg("pin", line);
    if (pin < 0 || pin > 13) {
      Serial.print("{\"id\":\"");
      Serial.print(idBuf);
      Serial.print("\",\"ok\":false,\"result\":\"\",\"error\":\"Invalid pin ");
      Serial.print(pin);
      Serial.println("\"}");
      return;
    }
    pinMode(pin, INPUT);
    int val = digitalRead(pin);
    Serial.print("{\"id\":\"");
    Serial.print(idBuf);
    Serial.print("\",\"ok\":true,\"result\":\"");
    Serial.print(val);
    Serial.println("\"}");
    return;
  }

  if (hasCmd(line, "gpio_write")) {
    int pin = parseArg("pin", line);
    int value = parseArg("value", line);
    if (pin < 0 || pin > 13) {
      Serial.print("{\"id\":\"");
      Serial.print(idBuf);
      Serial.print("\",\"ok\":false,\"result\":\"\",\"error\":\"Invalid pin ");
      Serial.print(pin);
      Serial.println("\"}");
      return;
    }
    pinMode(pin, OUTPUT);
    digitalWrite(pin, value ? HIGH : LOW);
    Serial.print("{\"id\":\"");
    Serial.print(idBuf);
    Serial.println("\",\"ok\":true,\"result\":\"done\"}");
    return;
  }

  // Unknown command
  Serial.print("{\"id\":\"");
  Serial.print(idBuf);
  Serial.println("\",\"ok\":false,\"result\":\"\",\"error\":\"Unknown command\"}");
}

void setup() {
  Serial.begin(BAUDRATE);
  lineLen = 0;
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n' || c == '\r') {
      if (lineLen > 0) {
        lineBuf[lineLen] = '\0';
        handleLine(lineBuf);
        lineLen = 0;
      }
    } else if (lineLen < MAX_LINE - 1) {
      lineBuf[lineLen++] = c;
    } else {
      lineLen = 0;  // Overflow, discard
    }
  }
}
