/*
 * AI Embed Live Lab — device firmware
 * https://aiembed.com/lab.html
 *
 * Works on ESP32, Arduino Uno/Nano, or anything with USB serial.
 * Speaks a JSON line protocol at 115200 baud so the lab's LLM agent
 * can drive real GPIO: LED, servo, and sensor reads.
 *
 * Wiring (all optional — firmware degrades gracefully):
 *   LED    : builtin (pin 2 on most ESP32 devkits, 13 on Uno)
 *   Servo  : signal on SERVO_PIN (default 18 on ESP32, 9 on Uno)
 *   Sensor : analog on SENSOR_PIN (potentiometer, LDR, anything)
 *
 * Protocol (one JSON object per line):
 *   -> {"cmd":"led","on":true}
 *   -> {"cmd":"blink","n":3}
 *   -> {"cmd":"servo","angle":90}
 *   -> {"cmd":"read"}
 *   <- {"ok":true,"sensors":{"analog":512,"uptime_s":42,"led":1,"servo":90}}
 *
 * For ESP32 install the "ESP32Servo" library; for AVR use built-in Servo.
 */

#if defined(ESP32)
  #include <ESP32Servo.h>
  const int LED_PIN = 2;
  const int SERVO_PIN = 18;
  const int SENSOR_PIN = 34;
#else
  #include <Servo.h>
  const int LED_PIN = 13;
  const int SERVO_PIN = 9;
  const int SENSOR_PIN = A0;
#endif

Servo servo;
bool ledState = false;
int servoAngle = 90;
String line;

void reply(bool ok, const char *msg = nullptr) {
  Serial.print("{\"ok\":");
  Serial.print(ok ? "true" : "false");
  if (msg) { Serial.print(",\"msg\":\""); Serial.print(msg); Serial.print("\""); }
  Serial.print(",\"sensors\":{\"analog\":");
  Serial.print(analogRead(SENSOR_PIN));
  Serial.print(",\"uptime_s\":");
  Serial.print(millis() / 1000);
  Serial.print(",\"led\":");
  Serial.print(ledState ? 1 : 0);
  Serial.print(",\"servo\":");
  Serial.print(servoAngle);
  Serial.println("}}");
}

/* tiny extractors — avoids pulling in a JSON library */
bool jsonBool(const String &s, const char *key) {
  int i = s.indexOf(String("\"") + key + "\"");
  return i >= 0 && s.indexOf("true", i) > i && (s.indexOf("true", i) - i) < 12;
}
long jsonNum(const String &s, const char *key, long dflt) {
  int i = s.indexOf(String("\"") + key + "\"");
  if (i < 0) return dflt;
  i = s.indexOf(':', i);
  if (i < 0) return dflt;
  return s.substring(i + 1).toInt();
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  servo.attach(SERVO_PIN);
  servo.write(servoAngle);
  Serial.println("{\"ok\":true,\"msg\":\"AI Embed lab device ready\"}");
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    if (c == '\n') {
      handle(line);
      line = "";
    } else if (line.length() < 200) {
      line += c;
    }
  }
}

void handle(const String &cmd) {
  if (cmd.indexOf("\"led\"") >= 0) {
    ledState = jsonBool(cmd, "on");
    digitalWrite(LED_PIN, ledState ? HIGH : LOW);
    reply(true, "led set");
  } else if (cmd.indexOf("\"blink\"") >= 0) {
    long n = constrain(jsonNum(cmd, "n", 3), 1, 20);
    for (long i = 0; i < n; i++) {
      digitalWrite(LED_PIN, HIGH); delay(160);
      digitalWrite(LED_PIN, LOW);  delay(160);
    }
    digitalWrite(LED_PIN, ledState ? HIGH : LOW);
    reply(true, "blinked");
  } else if (cmd.indexOf("\"servo\"") >= 0) {
    servoAngle = constrain(jsonNum(cmd, "angle", 90), 0, 180);
    servo.write(servoAngle);
    reply(true, "servo moved");
  } else if (cmd.indexOf("\"read\"") >= 0) {
    reply(true);
  } else if (cmd.length()) {
    reply(false, "unknown cmd");
  }
}
