/**
 **************************************************
 * @file        Inkplate2_Hourly_Weather_Station.ino
 * @brief       Hourly weather snapshot: fetch forecast data from OpenWeatherMap,
 *              render three time slots with icons and temperatures, then deep
 *              sleep between updates.
 *
 * @details     This example demonstrates how Inkplate 2 can fetch weather data
 *              from an online API (OpenWeatherMap) and display it as a simple
 *              dashboard. After connecting to WiFi, the sketch synchronizes the
 *              current time via NTP, requests weather/forecast data for the
 *              configured latitude/longitude, and renders three temperature
 *              values (now + two upcoming hours) together with condition icons.
 *
 *              The UI is drawn using Adafruit GFX-compatible calls, a custom
 *              font (SourceSansPro), and 48x48 bitmap icons included from
 *              icons.h. If WiFi connection fails, an error message is shown and
 *              the device sleeps briefly before retrying.
 *
 *              After drawing the screen, the ESP32 enters deep sleep and wakes
 *              after a fixed interval (DELAY_MS). Because deep sleep resets the
 *              ESP32, the program restarts from setup() on every wake cycle.
 *
 * Requirements:
 * - Board:      Soldered Inkplate 2
 * - Hardware:   Inkplate 2, USB cable
 * - Extra:      WiFi connection + Internet access, OpenWeatherMap API key
 *
 * Configuration:
 * - Boards Manager -> Inkplate Boards -> Soldered Inkplate2
 * - Serial Monitor: 115200 baud (recommended for debugging)
 * - WiFi:           set ssid/pass
 * - API key:        set apiKey (OpenWeatherMap)
 * - Location:       set lat/lon strings for your area
 * - Units:          optional AMERICAN define for °F/MPH (requires matching
 *                  change in Network.cpp as noted in the sketch)
 * - Update period:  set DELAY_MS (seconds between wakeups; multiplied by 1000)
 * - WiFi retry:     set DELAY_WIFI_RETRY_SECONDS for quick retry on failure
 *
 * Don't have Inkplate Boards in Arduino Boards Manager?
 * See https://docs.soldered.com/inkplate/10/quick-start-guide/
 *
 * How to use:
 * 1) Install ArduinoJson (required by the Network helper).
 * 2) Enter your WiFi SSID/password.
 * 3) Create an OpenWeatherMap API key and paste it into apiKey.
 * 4) Set lat/lon for your location.
 * 5) Upload the sketch and open Serial Monitor at 115200 baud.
 * 6) The device fetches data, renders the dashboard, then sleeps and repeats.
 *
 * Expected output:
 * - Display: three framed panels showing:
 *   - "Now" and the next two hour labels
 *   - temperature values (°C by default)
 *   - a 48x48 weather icon per panel
 * - On WiFi failure: an error screen prompting to check SSID/PASS, then a short
 *   deep sleep before retrying.
 * - Serial Monitor: NTP time printout and API fetch retries (if needed).
 *
 * Notes:
 * - Display mode is 1-bit with Inkplate 2 tri-color palette (BLACK/WHITE/RED).
 *   This example draws primarily in black/red on white background and uses a
 *   full refresh (display()) each cycle.
 * - Deep sleep restarts the ESP32; keep logic in setup() and leave loop() empty.
 * - API rate limits apply. The chosen update interval is intended to stay under
 *   typical free-tier limits, but you should verify current API quotas.
 * - Icons are selected by matching OpenWeatherMap condition abbreviations
 *   (e.g. "01d", "10n") against the built-in icon table.
 *
 * Docs:         https://docs.soldered.com/inkplate
 * Support:      https://forum.soldered.com/
 *
 * @author      e-radionica.com (community example), adapted for Soldered
 * @date        2022-04-05
 * @license     GNU GPL V3
 **************************************************/
// Next 3 lines are a precaution, you can ignore those, and the example would also work without them
#ifndef ARDUINO_INKPLATE2
#error "Wrong board selection for this example, please select Soldered Inkplate 2 in the boards menu."
#endif

#include "Inkplate.h" // Include Inkplate library to the sketch

#include "NetworkFunctions.h" // Header file for easier code readability

// Including fonts used
#include "Fonts/SourceSansPro_Regular8pt7b.h"

// Including icons generated by the py file
#include "icons.h"

// Delay between API calls, about 1000000 per month, which is the free tier limit
#define DELAY_MS 267800L
#define DELAY_WIFI_RETRY_SECONDS 10

// Inkplate object
Inkplate display;

// All our network functions are in this object, see Network.h
NetworkFunctions network;

// Change to your wifi ssid and password
char ssid[] = "Soldered Electronics";
char pass[] = "dasduino";

// Change to your api key, if you don't have one, head over to:
// https://openweathermap.org/guide , register and copy the key provided
char apiKey[] = "786f669a68d3a3d215e4b660fc27de77";

// Coordinates of your city sent to the api
char lon[] = "18.5947808";
char lat[] = "45.5510548";

// Uncomment this for MPH and Fahrenheit output, also uncomment it in the begining of Network.cpp
// #define AMERICAN

// Contants used for drawing icons
char abbrs[32][32] = {"01d", "02d", "03d", "04d", "09d", "10d", "11d", "13d", "50d",
                      "01n", "02n", "03n", "04n", "09n", "10n", "11n", "13n", "50n"};

const uint8_t *s_logos[18] = {
    icon_s_01d, icon_s_02d, icon_s_03d, icon_s_04d, icon_s_09d, icon_s_10d, icon_s_11d, icon_s_13d, icon_s_50d,
    icon_s_01n, icon_s_02n, icon_s_03n, icon_s_04n, icon_s_09n, icon_s_10n, icon_s_11n, icon_s_13n, icon_s_50n,
};

// Variables for storing temperature
char temps[3][8] = {
    "-",
    "-",
    "-",
};

// Variables for storing hour strings
char hours[3][8] = {
    "",
    "",
    "",
};

char currentTime[16] = "--:--";

char abbr1[16];
char abbr2[16];
char abbr3[16];

// function defined below
void drawTemps();

void setup()
{
    // Begin serial and display
    Serial.begin(115200);
    display.begin();

    // Initial cleaning of buffer and physical screen
    display.clearDisplay();

    // Connect Inkplate to the WiFi network
    // Try connecting to a WiFi network.
    // Parameters are network SSID, password, timeout in seconds and whether to print to serial.
    // If the Inkplate isn't able to connect to a network stop further code execution and print an error message.
    if (!display.connectWiFi(ssid, pass, WIFI_TIMEOUT, true))
    {
        //Can't connect to netowrk
        // Clear display for the error message
        display.clearDisplay();
        // Set the font size;
        display.setTextSize(1);
        // Set the cursor positions and print the text.
        display.setCursor(0, 0);
        display.print(F("Unable to connect to "));
        display.println(F(ssid));
        display.println(F("Please check SSID and PASS!"));
        // Display the error message on the Inkplate and go to deep sleep
        display.display();
        esp_sleep_enable_timer_wakeup(1000L * DELAY_WIFI_RETRY_SECONDS);
        (void)esp_deep_sleep_start();
    }

    // After connecting to WiFi we need to get internet time from NTP server
    time_t nowSec;
    struct tm timeInfo;
    // Fetch current time in epoch format and store it
    display.getNTPEpoch(&nowSec);
    gmtime_r(&nowSec, &timeInfo);
    Serial.print(F("Current time: "));
    Serial.print(asctime(&timeInfo));

    // Clear display
    display.clearDisplay();

    // Get all relevant data, see Network.cpp for info
    while (!network.getData(lon, lat, apiKey, temps[0], temps[1], temps[2], abbr1, abbr2, abbr3))
    {
        Serial.println("Retrying fetching data!");
        delay(5000);
    }
    network.getTime(currentTime);
    network.getHours(hours[0], hours[1], hours[2]);

    // Draw data, see functions below for info
    drawTemps();

    // Refresh full screen every fullRefresh times, defined above
    display.display();

    // Go to sleep before checking again
    // rtc_gpio_isolate(GPIO_NUM_12);   // Isolate/disable GPIO12 on ESP32 (only to reduce power consumption in sleep)
    esp_sleep_enable_timer_wakeup(1000L * DELAY_MS); // Activate wake-up timer
     
    esp_deep_sleep_start();       // Put ESP32 into deep sleep. Program stops here
}

void loop()
{
    // Never here! If you are using deep sleep, the whole program should be in setup() because the board restarts each
    // time. loop() must be empty!
}

// Function for drawing temperatures
void drawTemps()
{
    // Drawing 4 black rectangles in which temperatures will be written

    display.drawRect(5, 5, 60, 94, INKPLATE2_BLACK); // Draw some rectangles
    display.drawRect(75, 5, 60, 94, INKPLATE2_BLACK);
    display.drawRect(145, 5, 60, 94, INKPLATE2_BLACK);

    display.setFont(&SourceSansPro_Regular8pt7b);         // Set custom font
    display.setTextSize(1);                               // Set font size
    display.setTextColor(INKPLATE2_RED, INKPLATE2_BLACK); // Set font color and background color

    display.setCursor(
        18,
        20); // Set cursor, custom font uses different method for setting cursor
             // You can find more about that here https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
    display.println("Now");

    display.setCursor(92, 20);
    display.println(hours[1]);

    display.setCursor(162, 20);
    display.println(hours[2]);

    // Drawing temperature values into black rectangles
    display.setTextColor(INKPLATE2_BLACK, INKPLATE2_WHITE);

    display.setCursor(20, 40);
    display.print(temps[0]);
    display.println(F("C"));

    display.setCursor(90, 40);
    display.print(temps[1]);
    display.println(F("C"));

    display.setCursor(160, 40);
    display.print(temps[2]);
    display.println(F("C"));

    for (int i = 0; i < 18; ++i)
    {
        // If found draw specified icon
        if (strcmp(abbr1, abbrs[i]) == 0)
            display.drawBitmap(11, 48, s_logos[i], 48, 48, INKPLATE2_BLACK, INKPLATE2_WHITE);
    }

    for (int i = 0; i < 18; ++i)
    {
        // If found draw specified icon
        if (strcmp(abbr2, abbrs[i]) == 0)
            display.drawBitmap(81, 48, s_logos[i], 48, 48, INKPLATE2_BLACK, INKPLATE2_WHITE);
    }

    for (int i = 0; i < 18; ++i)
    {
        // If found draw specified icon
        if (strcmp(abbr3, abbrs[i]) == 0)
            display.drawBitmap(151, 48, s_logos[i], 48, 48, INKPLATE2_BLACK, INKPLATE2_WHITE);
    }
}
