

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>

// ====== WiFi & API Config ======
const char* ssid     = "home"; //replace with your wifi name
const char* password = "password"; //replace with your wifi password
const String apiKey  = "// Replace with your key"; // Replace with your key
const float latitude = 1123.23;  //replace with your latitude
const float longitude = 235.2785; //replace with your longitude

// ====== LED Config ======
#define LED_PIN    2
#define LED_COUNT  3
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

// ====== Animation Variables ======
uint32_t baseColor = strip.Color(0, 255, 0); // Current color
uint32_t targetColor = baseColor;            
uint32_t prevColor = baseColor;              
unsigned long colorChangeStart = 0;
unsigned long colorChangeDuration = 3000; // 3 sec smooth fade

float phaseOffsets[LED_COUNT] = {0.0, 0.33, 0.66};
float speed = 0.015; // breathing speed
unsigned long startTime;

// ====== Timing ======
unsigned long lastUpdate = 0;
unsigned long weatherUpdateInterval = 15 * 60 * 1000; // 15 minutes

// ====== Interpolate Colors ======
uint32_t interpolateColor(uint32_t c1, uint32_t c2, float t) {
  uint8_t r1 = (c1 >> 16) & 0xFF;
  uint8_t g1 = (c1 >> 8) & 0xFF;
  uint8_t b1 = c1 & 0xFF;

  uint8_t r2 = (c2 >> 16) & 0xFF;
  uint8_t g2 = (c2 >> 8) & 0xFF;
  uint8_t b2 = c2 & 0xFF;

  uint8_t r = r1 + (r2 - r1) * t;
  uint8_t g = g1 + (g2 - g1) * t;
  uint8_t b = b1 + (b2 - b1) * t;

  return strip.Color(r, g, b);
}

// ====== Map Weather to Colors ======
void mapWeatherToColors(float temp, String weather) {
  weather.toLowerCase();
  Serial.printf("[MAP] Temp: %.1f°C, Weather: %s\n", temp, weather.c_str());

  if (temp > 32) {
    targetColor = strip.Color(255, 0, 0); // Hot = Red
  } else if (temp < 20) {
    targetColor = strip.Color(0, 255, 0); // Cool = Orange
  } else if (weather.indexOf("clear") != -1) {
    targetColor = strip.Color(255, 255, 0); // Sunny = Yellow
  } else if (weather.indexOf("cloud") != -1) {
    targetColor = strip.Color(0, 255, 255); // Cloudy = Green (changed from Cyan)
  } else if (weather.indexOf("rain") != -1) {
    targetColor = strip.Color(0, 0, 255); // Rain = Blue
  } else if (weather.indexOf("thunderstorm") != -1) {
    targetColor = strip.Color(128, 0, 255); // Purple
  } else if (weather.indexOf("drizzle") != -1) {
    targetColor = strip.Color(0, 128, 255); // Light Blue
  } else if (weather.indexOf("mist") != -1 || weather.indexOf("haze") != -1 || weather.indexOf("fog") != -1) {
    targetColor = strip.Color(255, 105, 180); // Pink
  } else {
    targetColor = strip.Color(0, 255, 0); // Default Green
  }

  prevColor = baseColor;
  colorChangeStart = millis();
}

// ====== Fetch Weather ======
void fetchWeather() {
  Serial.println("[API] Fetching weather data...");
  WiFiClient client;
  HTTPClient http;

  String url = "http://api.openweathermap.org/data/2.5/weather?lat=" + String(latitude, 4) +
               "&lon=" + String(longitude, 4) +
               "&units=metric&appid=" + apiKey;

  if (http.begin(client, url)) {
    int httpCode = http.GET();
    if (httpCode == HTTP_CODE_OK) {
      String payload = http.getString();
      StaticJsonDocument<1024> doc;
      if (!deserializeJson(doc, payload)) {
        float temp = doc["main"]["temp"];
        String weather = doc["weather"][0]["main"].as<String>();
        mapWeatherToColors(temp, weather);
      } else {
        Serial.println("[ERROR] JSON parse failed");
      }
    } else {
      Serial.printf("[ERROR] HTTP GET failed, code: %d\n", httpCode);
    }
    http.end();
  } else {
    Serial.println("[ERROR] HTTP begin failed");
  }
}

// ====== Breathing Effect ======
void updateBreathing() {
  // Breathing cycle: 5 seconds per breath (adjust for slower/faster)
  float speed = 5000.0;
  float cycle = (millis() % (unsigned long)speed) / speed; // 0 to 1
  float sineValue = sin(cycle * 2 * PI) * 0.5 + 0.5; // 0 to 1

  // Keep brightness between 40% and 100%
  float brightnessScale = 0.3 + (sineValue * 0.6); // 0.4 → 100%

  // Color transition blend
  uint8_t r = ((prevColor >> 16) & 0xFF) + (((targetColor >> 16) & 0xFF) - ((prevColor >> 16) & 0xFF)) * easeInOutQuad((millis() - colorChangeStart) / 2000.0);
  uint8_t g = ((prevColor >> 8) & 0xFF) + (((targetColor >> 8) & 0xFF) - ((prevColor >> 8) & 0xFF)) * easeInOutQuad((millis() - colorChangeStart) / 2000.0);
  uint8_t b = (prevColor & 0xFF) + (((targetColor & 0xFF) - (prevColor & 0xFF)) * easeInOutQuad((millis() - colorChangeStart) / 2000.0));

  // Apply breathing brightness
  strip.setPixelColor(0, r * brightnessScale, g * brightnessScale, b * brightnessScale);
  strip.setPixelColor(1, r * brightnessScale, g * brightnessScale, b * brightnessScale);
  strip.setPixelColor(2, r * brightnessScale, g * brightnessScale, b * brightnessScale);

  strip.show();
}

// Smooth easing function
float easeInOutQuad(float t) {
  if (t < 0) return 0;
  if (t > 1) return 1;
  return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}


// ====== Blink Blue while Connecting ======
void blinkWhileConnecting() {
  bool on = false;
  while (WiFi.status() != WL_CONNECTED) {
    uint32_t color = on ? strip.Color(0, 120, 255) : strip.Color(0, 0, 0);
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, color);
    }
    strip.show();
    on = !on;
    delay(500);
  }
}

// ====== Setup ======
void setup() {
  Serial.begin(115200);
  strip.begin();
  strip.show();
  startTime = millis();

  Serial.println("[WiFi] Connecting...");
  WiFi.begin(ssid, password);
  blinkWhileConnecting();
  Serial.println("\n[WiFi] Connected!");

  fetchWeather();
  lastUpdate = millis();
}

// ====== Loop ======
void loop() {
  updateBreathing();

  if (millis() - lastUpdate > weatherUpdateInterval) {
    fetchWeather();
    lastUpdate = millis();
  }
}
