#include <esp_camera.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ----- LED STRIP -----
#define LED_PIN 2
#define NUM_LEDS 1
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

// ----- OLED -----
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define I2C_SDA 15
#define I2C_SCL 14
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ----- CAMERA PINS -----
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM     0
#define SIOD_GPIO_NUM    26
#define SIOC_GPIO_NUM    27
#define Y9_GPIO_NUM      35
#define Y8_GPIO_NUM      34
#define Y7_GPIO_NUM      39
#define Y6_GPIO_NUM      36
#define Y5_GPIO_NUM      21
#define Y4_GPIO_NUM      19
#define Y3_GPIO_NUM      18
#define Y2_GPIO_NUM       5
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

// ----- COLOR NAME DETECTION -----
String getColorName(uint8_t r, uint8_t g, uint8_t b) {
  if (r > 200 && g < 80 && b < 80) return "Red";
  if (r > 200 && g > 100 && b < 80) return "Orange";
  if (r > 220 && g > 220 && b < 100) return "Yellow";
  if (r < 100 && g > 180 && b < 100) return "Green";
  if (r < 100 && g < 100 && b > 180) return "Blue";
  if (r > 130 && b > 130 && g < 100) return "Purple";
  if (r > 200 && g > 200 && b > 200) return "White";
  if (r < 50 && g < 50 && b < 50) return "Black";
  return "Unknown";
}

void setup() {
  Serial.begin(115200);

  // LED Strip
  strip.begin();
  strip.show();
  strip.setBrightness(100);

  // I2C OLED Init
  Wire.begin(I2C_SDA, I2C_SCL);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED not found");
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Starting...");
    display.display();
  }

  // Camera Config
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_RGB565;
  config.frame_size = FRAMESIZE_QQVGA; // 160x120
  config.fb_count = 1;

  if (esp_camera_init(&config) != ESP_OK) {
    Serial.println("Camera init failed");
    return;
  }

  delay(1000);
}

void loop() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return;
  }

  uint32_t r = 0, g = 0, b = 0;
  uint16_t count = 0;

  // Calculate average RGB
  for (int i = 0; i < fb->len; i += 4) {
    uint8_t byte1 = fb->buf[i];
    uint8_t byte2 = fb->buf[i + 1];

    uint8_t r5 = (byte1 & 0xF8) >> 3;
    uint8_t g6 = ((byte1 & 0x07) << 3) | ((byte2 & 0xE0) >> 5);
    uint8_t b5 = byte2 & 0x1F;

    r += (r5 << 3);
    g += (g6 << 2);
    b += (b5 << 3);

    count++;
  }

  r /= count;
  g /= count;
  b /= count;

  String colorName = getColorName(r, g, b);

  Serial.printf("Detected: %s - R=%d G=%d B=%d\n", colorName.c_str(), r, g, b);

  // Update LED Strip
  strip.fill(strip.Color(r, g, b));
  strip.show();

  // Update OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(1);
  display.printf("Color: %s\nR: %d\nG: %d\nB: %d", colorName.c_str(), r, g, b);
  display.display();

  esp_camera_fb_return(fb);
  delay(2000);
}
