// Learn about the ESP32 WiFi simulation in
// https://docs.wokwi.com/guides/esp32-wifi

#include <WiFi.h>
#include <MD_Parola.h>
#include <Ticker.h>

#include <sys/time.h>  // struct timeval
#include <time.h>      // time() ctime()

#include <map>
#include <memory>

constexpr auto updateInterval { 500 };

// ---- GPIO Pins ----
std::map<const char*, uint8_t> gpio{
    {"SCLK", 18},       // GPIO 18
    {"csPin", 5},       // GPIO 5
    {"MOSI", 23},       // GPIO 23
};

// ---- LED Matrix Display ---- 
//   GPIO    NodeMCU   Name  |   MAX7219
//   ===================================
//     12       D6     MISO  |   CS
//     13       D7     MOSI  |   DIN
//     15       D8     SS    |   Not connected
//     14       D5     SCK   |   CLK

constexpr auto hardwareType   { MD_MAX72XX::PAROLA_HW };
constexpr uint8_t numDevices  { 4 };

uint8_t slimColonChar[] { 1, 36 };
uint8_t slimSpaceChar[] { 1, 0 };
uint8_t serifedOneChar[] { 3, 66, 127, 64 };
uint8_t niceSevenChar[]{ 5, 1, 1, 121, 5, 3 };
uint8_t niceZeroChar[]{ 5, 62, 65, 65, 65, 62 };

// Hardware SPI connection
MD_Parola display { MD_Parola{ hardwareType, gpio["csPin"], numDevices} };

constexpr uint8_t scrollSpeed { 30 };    // default frame delay value
constexpr textEffect_t scrollEffect { PA_SCROLL_LEFT };
constexpr textPosition_t scrollAlign  { PA_LEFT };
constexpr uint16_t scrollPause { 1 * 1000 }; // in milliseconds

// ---- Setup ---- 
void setup() {
  Serial.begin(115200);
  Serial.println();

  Serial.println(F("--- Starting ---"));

  display.begin();

    // the destructor will stop the ticker when it goes out of scope
  Ticker animateTicker;
  animateTicker.attach_ms(10, []() {
    if (display.displayAnimate()) {
      display.displayReset();
    }
  });

  char scrollText[60] { "Connecting" };
  display.displayText(scrollText, scrollAlign, scrollSpeed, scrollPause,
                      scrollEffect, scrollEffect);

  WiFi.begin("Wokwi-GUEST", "", 6);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println(" Connected!");

  configTime(0, 0, "pool.ntp.org");
    setenv("TZ","	<+07>-7",1);
  tzset();

  Serial.println(F("Starting Clock"));

  display.setTextAlignment(PA_CENTER);
  display.addChar(149, slimColonChar);
  display.addChar(150, slimColonChar);
  display.addChar(151, slimSpaceChar);
  display.addChar(152, slimSpaceChar);

  display.addChar('1', serifedOneChar);
  display.addChar('7', niceSevenChar);
  display.addChar('0', niceZeroChar);

  static Ticker clockTicker;

  clockTicker.attach_ms(updateInterval, []() {
    static uint32_t blink { 0 };
    const auto now = time(nullptr);
    const auto tm = localtime(&now);
    constexpr char slimColon{149}, nbSpace{151};

    uint32_t hour = ((tm->tm_hour % 12) != 0) ? tm->tm_hour % 12 : 12;

    char timeStr[24]; // large because of the lack of width for hour
    sprintf(timeStr, "%d%c%02d", hour, slimColon + blink, tm->tm_min);
    strncat(timeStr, &nbSpace, 1);
    strcat(timeStr, (tm->tm_hour < 12) ? "A" : "P");
    display.print(timeStr);
    blink = (blink+ 1) % 4;
  });
}

void loop() {
}