#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "logo.h"
#include "menu.h"

JsonObject countries;
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
const int buttonPinA = 12;
const int buttonPinB = 14;
const int buttonPinC = 22;
int lastButtonStateA = HIGH;
int lastButtonStateB = HIGH;
int lastButtonStateC = HIGH;

const char* ssid = "Wokwi-GUEST";
const char* password = "";


// API keys
const char* ipinfoApiKey = "bdc3cb7e8d43c9";
const char* openWeatherMapApiKey = "403e1de5dd6b90cb7e64a7869033cd0d";

String city = "";
String country = "";
String coordinates = "";
String temperature = "";
String pressure = "";
String humidity = "";
String windSpeed = "";
String winddeg = "";
String weatherMain;
String weatherDescription;

String userCity = "";
String userCountry = "";

int selectedMenuItem = 0;
int menuSize = sizeof(menuItems) / sizeof(menuItems[0]);

String initialMenuItems[] = { "Local", "Global", "List" };
int initialMenuSize = sizeof(initialMenuItems) / sizeof(initialMenuItems[0]);

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

unsigned long lastFetchTime = 0;
const unsigned long fetchInterval = 10000;  // 10 seconds

void setup() {
  Serial.begin(115200);
  delay(1000);
  // Connect to Wi-Fi
  connectToWiFi();

  initializeDisplay();
  displayLogo();
  pinMode(buttonPinA, INPUT_PULLUP);
  pinMode(buttonPinB, INPUT_PULLUP);
  pinMode(buttonPinC, INPUT_PULLUP);
  lastButtonStateA = digitalRead(buttonPinA);
  lastButtonStateB = digitalRead(buttonPinB);
  lastButtonStateC = digitalRead(buttonPinC);
}

void loop() {
  int buttonStateA = digitalRead(buttonPinA);
  int buttonStateB = digitalRead(buttonPinB);
  int buttonStateC = digitalRead(buttonPinC);

  if (buttonStateC == LOW && lastButtonStateC == HIGH) {
    InitialMenu();
    return;
  }

  lastButtonStateA = buttonStateA;
  lastButtonStateB = buttonStateB;
  lastButtonStateC = buttonStateC;
}

void InitialMenu() {
  tft.fillScreen(ILI9341_BLACK);
  selectedMenuItem = 0;
  displayInitialMenu();

  while (true) {
    int buttonStateA = digitalRead(buttonPinA);
    int buttonStateB = digitalRead(buttonPinB);
    int buttonStateC = digitalRead(buttonPinC);

    if (buttonStateA == LOW && lastButtonStateA == HIGH) {
      // Move up in the menu
      selectedMenuItem = (selectedMenuItem - 1 + initialMenuSize) % initialMenuSize;
      updateInitialMenuDisplay();
    }

    if (buttonStateB == LOW && lastButtonStateB == HIGH) {
      // Move down in the menu
      selectedMenuItem = (selectedMenuItem + 1) % initialMenuSize;
      updateInitialMenuDisplay();
    }

    if (buttonStateC == HIGH && lastButtonStateC == LOW) {
      executeInitialMenuAction();
      return;
    }

    lastButtonStateA = buttonStateA;
    lastButtonStateB = buttonStateB;
    lastButtonStateC = buttonStateC;

    delay(100);
  }
}

void executeInitialMenuAction() {
  int buttonStateA = digitalRead(buttonPinA);
  int buttonStateB = digitalRead(buttonPinB);
  int buttonStateC = digitalRead(buttonPinC);
  switch (selectedMenuItem) {
    case 0:
    lastButtonStateC = buttonStateC;
      fetchAndDisplayInfo();
      break;
    case 1:
    lastButtonStateC = buttonStateC;
      getUserInput();
      fetchAndDisplayInfo_Globle();
      break;
    case 2:
    lastButtonStateC = buttonStateC;
      menuMode();
      break;
  }
}

void displayInitialMenu() {
  tft.setCursor(0, (tft.height() - initialMenuSize * 20) / 2); // Centered vertically
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);

  for (int i = 0; i < initialMenuSize; i++) {
    if (i == selectedMenuItem) {
      tft.setTextColor(ILI9341_YELLOW);
    } else {
      tft.setTextColor(ILI9341_WHITE);
    }

    tft.print(initialMenuItems[i]);
    tft.println();
  }
}

void updateInitialMenuDisplay() {
  tft.setCursor(0, (tft.height() - initialMenuSize * 20) / 2); // Centered vertically
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);

  for (int i = 0; i < initialMenuSize; i++) {
    if (i == selectedMenuItem) {
      tft.setTextColor(ILI9341_YELLOW);
    } else {
      tft.setTextColor(ILI9341_WHITE);
    }

    tft.print(initialMenuItems[i]);
    tft.println();
  }
}


void menuMode() {
  tft.fillScreen(ILI9341_BLACK);
  displayMenu();

  while (true) {
    int buttonStateA = digitalRead(buttonPinA);
    int buttonStateB = digitalRead(buttonPinB);
    int buttonStateC = digitalRead(buttonPinC);

    if (buttonStateA == LOW && lastButtonStateA == HIGH) {
      // Move up in the menu
      selectedMenuItem = (selectedMenuItem - 1 + menuSize) % menuSize;
      updateMenuDisplay();
    }

    if (buttonStateB == LOW && lastButtonStateB == HIGH) {
      // Move down in the menu
      selectedMenuItem = (selectedMenuItem + 1) % menuSize;
      updateMenuDisplay();
    }

    if (buttonStateC == HIGH && lastButtonStateC == LOW) {
      parseMenuSelection(menuItems[selectedMenuItem]);
      return;
    }

    lastButtonStateA = buttonStateA;
    lastButtonStateB = buttonStateB;
    lastButtonStateC = buttonStateC;

    delay(100);
  }
}

void updateMenuDisplay() {
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);

  for (int i = 0; i < menuSize; i++) {
    if (i == selectedMenuItem) {
      tft.setTextColor(ILI9341_YELLOW);
    } else {
      tft.setTextColor(ILI9341_WHITE);
    }

    tft.print(menuItems[i]);
    tft.println();
  }
}

void displayMenu() {
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);

  for (int i = 0; i < menuSize; i++) {
    if (i == selectedMenuItem) {
      tft.setTextColor(ILI9341_YELLOW);  // Highlight the selected item
    } else {
      tft.setTextColor(ILI9341_WHITE);
    }

    tft.print(menuItems[i]);
    tft.println();
  }
}

void parseMenuSelection(String menuSelection) {
  int commaIndex = menuSelection.indexOf(',');
  if (commaIndex != -1) {
    userCountry = menuSelection.substring(0, commaIndex);
    userCity = menuSelection.substring(commaIndex + 1);
  }
  fetchAndDisplayInfo_Globle();
}

void displayLogo() {
  tft.fillScreen(ILI9341_BLACK);
  tft.drawRGBBitmap(0, 0, logo, 320, 240);
  delay(500);
}
void getUserInput() {
  Serial.println("Enter Country:");
  while (!Serial.available()) {
    delay(100);
  }
  userCountry = Serial.readStringUntil('\n');

  Serial.println("Enter City:");
  while (!Serial.available()) {
    delay(100);
  }
  userCity = Serial.readStringUntil('\n');

  Serial.print("Entered Country: ");
  Serial.println(userCountry);
  Serial.print("Entered City: ");
  Serial.println(userCity);
}

void connectToWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
}

void initializeDisplay() {
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
}

void fetchAndDisplayInfo() {
  String ipinfoData = getIPinfoData();

  // Parse IPinfo JSON data
  parseIPinfoData(ipinfoData);

  // Get weather data
  String weatherData = getWeatherData();

  // Parse weather JSON data
  parseWeatherData(weatherData);

  // Display information on ILI9341
  displayInfo();
}

void fetchAndDisplayInfo_Globle() {
  // Update city and country based on user input
  city = userCity;
  country = userCountry;

  // Get weather data
  String weatherData = getWeatherData();

  // Parse weather JSON data
  parseWeatherData(weatherData);

  // Display information on ILI9341
  displayInfo_Globle();
}

void displayInfo_Globle() {
  tft.fillScreen(ILI9341_BLACK);  // Clear the screen

  displayText("Country", country, 10);
  displayText("City", city, 30);
  displayText("Temperature", temperature + " C", 50);
  displayText("Pressure", pressure + " hPa", 70);
  displayText("Humidity", humidity + " %", 90);
  displayText("Wind", windSpeed + " m/s, " + winddeg, 110);
  displayText("Weather", "\n " + weatherMain + ", " + weatherDescription, 130);
}

String getIPinfoData() {
  String url = "http://ipinfo.io/json?token=" + String(ipinfoApiKey);
  return fetchData(url, "IPinfo data");
}

void parseIPinfoData(String json) {
  DynamicJsonDocument doc(1024);
  deserializeJson(doc, json);

  city = doc["city"].as<String>();
  country = doc["region"].as<String>();
  coordinates = doc["loc"].as<String>();
}

String getWeatherData() {
  String weatherUrl = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + country + "&units=metric&APPID=" + openWeatherMapApiKey;
  return fetchData(weatherUrl, "weather data");
}

void parseWeatherData(String json) {
  DynamicJsonDocument doc(1024);
  deserializeJson(doc, json);

  temperature = doc["main"]["temp"].as<String>();
  pressure = doc["main"]["pressure"].as<String>();
  humidity = doc["main"]["humidity"].as<String>();
  windSpeed = doc["wind"]["speed"].as<String>();
  winddeg = doc["wind"]["deg"].as<String>();
  weatherMain = doc["weather"][0]["main"].as<String>();
  weatherDescription = doc["weather"][0]["description"].as<String>();

  // Remove double quotes around numerical values
  if (winddeg >= "337" || winddeg < "23") {
    winddeg = "N";  // North
  } else if (winddeg >= "23" && winddeg < "68") {
    winddeg = "NE";  // Northeast
  } else if (winddeg >= "68" && winddeg < "113") {
    winddeg = "E";  // East
  } else if (winddeg >= "113" && winddeg < "158") {
    winddeg = "SE";  // Southeast
  } else if (winddeg >= "158" && winddeg < "203") {
    winddeg = "S";  // South
  } else if (winddeg >= "203" && winddeg < "248") {
    winddeg = "SW";  // Southwest
  } else if (winddeg >= "248" && winddeg < "293") {
    winddeg = "W";  // West
  } else if (winddeg >= "293" && winddeg < "337") {
    winddeg = "NW";  // Northwest
  } else {
    winddeg = "Unknown";  // Invalid wind angle
  }
}

String fetchData(String url, const char* dataType) {
  HTTPClient http;

  Serial.print("Fetching ");
  Serial.print(dataType);
  Serial.print("...");

  http.begin(url);
  int httpCode = http.GET();
  String payload = "{}";

  if (httpCode == HTTP_CODE_OK) {
    payload = http.getString();
    Serial.println("Done");
  } else {
    Serial.println("Failed");
  }

  http.end();
  return payload;
}

void displayInfo() {
  tft.fillScreen(ILI9341_BLACK);  // Clear the screen

  displayText("Country", country, 10);
  displayText("City", city, 30);
  displayText("lng,lat", coordinates, 50);
  displayText("Temperature", temperature + " C", 70);
  displayText("Pressure", pressure + " hPa", 90);
  displayText("Humidity", humidity + " %", 110);
  displayText("Wind", windSpeed + " m/s , " + winddeg, 130);
  displayText("Weather", "\n " + weatherMain + ", " + weatherDescription, 150);
}

void displayText(const char* label, String value, int yPos) {
  tft.setCursor(10, yPos);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);  // Adjust the text size
  tft.print(label + String(": ") + value);
}