#include <WiFi.h>
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
#include "time.h"


// Wi-Fi & API settings
const char* ssid = "";
const char* password = "";
const char* apiKey = ""; // API key OpenWeatherMap
const char* city = "Warszawa"; // City
const char* units = "metric";


// ================= TFT =================

TFT_eSPI tft = TFT_eSPI();


// ================= Time =================

const char* ntpServerName = "pool.ntp.org";

int currentHour = 0;
int currentMinute = 0;
int currentSecond = 0;


// ================= Date =================

String dateStr = "";
String dayStr = "";


// ================= weather =================

float weatherTemp = NAN;
String weatherDescription = "";

int weatherHumidity = -1;
float weatherWindSpeed = NAN;


// ================= Screen memory =================

String lastTimeStr = "";
String lastDateStr = "";

float lastTemp = NAN;
int lastHumidity = -1;
float lastWindSpeed = NAN;
String lastWeatherDescription = "";


// ================= Brightness =================

#define BL_CHANNEL 0

#define DAY_BRIGHTNESS 255
#define NIGHT_BRIGHTNESS 40

int currentBrightness = -1;


// ================= TIMERS =================

unsigned long previousMillis = 0;
unsigned long lastWeatherFetch = 0;

const unsigned long interval = 1000;


// ================= Colors =================

uint16_t customWatch = 0xF81F;
uint16_t customText = 0xDA2;



// =====================================================
// SETUP
// =====================================================


void setup()
{

  Serial.begin(115200);


  // Bright PWM

  ledcSetup(BL_CHANNEL,5000,8);
  ledcAttachPin(TFT_BL,BL_CHANNEL);


  setBrightness();



  // TFT

  tft.init();

  tft.setRotation(3);

  tft.fillScreen(TFT_BLACK);


  tft.setTextDatum(TL_DATUM);
  tft.setTextColor(TFT_WHITE,TFT_BLACK);
  tft.setTextSize(2);


  tft.setCursor(10,10);
  tft.println("Connecting WiFi...");



  // WIFI

  WiFi.begin(ssid,password);


  while(WiFi.status()!=WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }


  Serial.println("WiFi OK");


  tft.fillScreen(TFT_BLACK);

  tft.setTextDatum(MC_DATUM);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextSize(2);

  tft.drawString(
  "WiFi OK",
  tft.width()/2,
  tft.height()/2
);

delay(1000);

tft.fillScreen(TFT_BLACK);



  // NTP

  configTime(0,0,ntpServerName);



  getTime();
  getDate();



  fetchWeather();


  lastWeatherFetch=millis();


  displayScreen();

}




// =====================================================
// LOOP
// =====================================================


void loop()
{


  unsigned long now=millis();



  if(now-previousMillis>=interval)
  {

    previousMillis=now;


    getTime();

    getDate();

    setBrightness();



    if(now-lastWeatherFetch>600000)
    {
      fetchWeather();

      lastWeatherFetch=now;
    }


    displayScreen();

  }


}
// =====================================================
// Time set
// =====================================================


void getTime()
{

  struct tm timeinfo;


  if(getLocalTime(&timeinfo))
  {

    currentHour=(timeinfo.tm_hour+2)%24;

    currentMinute=timeinfo.tm_min;

    currentSecond=timeinfo.tm_sec;

  }

}



// =====================================================
// DATE and Week Day
// =====================================================


void getDate()
{

  struct tm timeinfo;


  if(getLocalTime(&timeinfo))
  {


    char buffer[20];


    sprintf(buffer,
            "%02d.%02d.%04d",
            timeinfo.tm_mday,
            timeinfo.tm_mon+1,
            timeinfo.tm_year+1900);



    dateStr=String(buffer);



    const char* days[] =
    {
      "Sunday",
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday",
      "Saturday"
    };


    dayStr=days[timeinfo.tm_wday];

  }

}



// =====================================================
// Auto bright
// =====================================================


void setBrightness()
{

  struct tm timeinfo;


  if(getLocalTime(&timeinfo))
  {

    int h=timeinfo.tm_hour;


    int value;


    if(h>=22 || h<7)
    {
      value=NIGHT_BRIGHTNESS;
    }
    else
    {
      value=DAY_BRIGHTNESS;
    }



    if(value!=currentBrightness)
    {

      ledcWrite(
        BL_CHANNEL,
        value
      );


      currentBrightness=value;

    }

  }

}



// =====================================================
// Weather icon
// =====================================================


void drawWeatherIcon(int x,int y)
{


  tft.fillCircle(x,y,22,TFT_BLACK);



  if(weatherDescription.indexOf("clear")>=0)
  {

    // Sun

    tft.fillCircle(
      x,
      y,
      15,
      TFT_YELLOW
    );


    for(int i=0;i<8;i++)
    {

      float a=i*PI/4;


      int x1=x+cos(a)*22;
      int y1=y+sin(a)*22;

      int x2=x+cos(a)*30;
      int y2=y+sin(a)*30;


      tft.drawLine(
        x1,y1,
        x2,y2,
        TFT_YELLOW
      );

    }

  }



  else if(weatherDescription.indexOf("cloud")>=0)
  {

    // cloud


    tft.fillCircle(
      x-10,
      y,
      12,
      TFT_WHITE
    );


    tft.fillCircle(
      x+8,
      y-5,
      15,
      TFT_WHITE
    );


    tft.fillRect(
      x-25,
      y,
      50,
      15,
      TFT_WHITE
    );

  }



  else if(weatherDescription.indexOf("rain")>=0)
  {

    // rain


    tft.fillCircle(
      x,
      y-5,
      18,
      TFT_DARKGREY
    );


    for(int i=-1;i<=1;i++)
    {

      tft.drawLine(
        x+i*10,
        y+15,
        x+i*10-5,
        y+28,
        TFT_BLUE
      );

    }

  }



  else
  {

    // default

    tft.fillCircle(
      x,
      y,
      15,
      TFT_CYAN
    );

  }

}



// =====================================================
// Display
// =====================================================


void displayScreen()
{


  int w=tft.width();



  String timeStr =
  String(currentHour<10?"0":"")+
  String(currentHour)+":"+
  String(currentMinute<10?"0":"")+
  String(currentMinute)+":"+
  String(currentSecond<10?"0":"")+
  String(currentSecond);



  // Watch


if(timeStr!=lastTimeStr)
{

  tft.setTextDatum(MC_DATUM);

  tft.setTextSize(4);

  // auto clear previous screen


  tft.setTextColor(
    customWatch,
    TFT_BLACK
  );


  tft.drawString(
    timeStr,
    w/2,
    27
  );


  lastTimeStr=timeStr;

}



  // DATE


  if(dateStr!=lastDateStr)
  {


    tft.fillRect(
      0,
      60,
      w,
      35,
      TFT_BLACK
    );


    tft.setTextDatum(MC_DATUM);

    tft.setTextSize(2);


    tft.setTextColor(
      TFT_WHITE,
      TFT_BLACK
    );


    tft.drawString(
      dateStr+" "+dayStr,
      w/2,
      75
    );


    lastDateStr=dateStr;

  }



  bool changed =
    weatherTemp!=lastTemp ||
    weatherHumidity!=lastHumidity ||
    weatherWindSpeed!=lastWindSpeed ||
    weatherDescription!=lastWeatherDescription;



  if(changed)
  {


    tft.fillRect(
      0,
      100,
      320,
      180,
      TFT_BLACK
    );



    drawWeatherIcon(
      45,
      145
    );



    tft.setTextDatum(TL_DATUM);

    tft.setTextSize(2);



    int y=115;



    tft.setTextColor(customText);

    tft.drawString(
      "Pogoda:",
      100,
      y
    );


    tft.setTextColor(TFT_WHITE);

    tft.drawString(
      city,
      190,
      y
    );



    y+=30;


    tft.setTextColor(customText);

    tft.drawString(
      "Temp:   ",
      100,
      y
    );


    tft.setTextColor(TFT_WHITE);

    tft.drawFloat(
      weatherTemp,
      1,
      190,
      y
    );


    tft.drawString(
      " C",
      240,
      y
    );



    y+=25;


    tft.setTextColor(customText);

    tft.drawString(
      "Wilg:   ",
      100,
      y
    );


    tft.setTextColor(TFT_WHITE);

    tft.drawNumber(
      weatherHumidity,
      190,
      y
    );


    tft.drawString(
      " %",
      220,
      y
    );



    y+=25;


    tft.setTextColor(customText);

    tft.drawString(
      "Wiatr:  ",
      100,
      y
    );


    tft.setTextColor(TFT_WHITE);

    tft.drawFloat(
      weatherWindSpeed,
      1,
      190,
      y
    );


    tft.drawString(
      "m/s",
      240,
      y
    );



    lastTemp=weatherTemp;
    lastHumidity=weatherHumidity;
    lastWindSpeed=weatherWindSpeed;
    lastWeatherDescription=weatherDescription;

  }


}



// =====================================================
// OPENWEATHER
// =====================================================


void fetchWeather()
{

  if(WiFi.status()!=WL_CONNECTED)
    return;



  String url =
  "http://api.openweathermap.org/data/2.5/weather?q="
  +String(city)+
  "&units=metric&appid="+
  String(apiKey);



  HTTPClient http;


  http.begin(url);



  int code=http.GET();



  if(code==200)
  {

    String payload=http.getString();



    JsonDocument doc;



    DeserializationError error =
    deserializeJson(
      doc,
      payload
    );



    if(!error)
    {

      weatherTemp =
      doc["main"]["temp"].as<float>();


      weatherHumidity =
      doc["main"]["humidity"].as<int>();


      weatherWindSpeed =
      doc["wind"]["speed"].as<float>();


      //weatherDescription =
      //doc["weather"][0]["description"].as<String>();

    }

  }


  http.end();

}