/*
  NodeMCU Ankomsttavle WebConfig
  Version: 1.3
  Jens Krogsgaard / ChatGPT

  ESP8266 NodeMCU + OLED SSD1306 + MQTT

  Pins:
    OLED SCL: D1 / GPIO5
    OLED SDA: D2 / GPIO4

  Libraries:
    ESP8266 core, PubSubClient, ArduinoJson,
    Adafruit_GFX, Adafruit_SSD1306
*/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Updater.h>
#include <ESP8266mDNS.h>
#include <DNSServer.h>
#include <WiFiClientSecureBearSSL.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define APP_NAME       "NodeMCU Ankomsttavle"
#define APP_VERSION    "1.3"

const char ICON_ARRIVAL[] PROGMEM = R"SVG(<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'>
  <defs>
    <linearGradient id='bg' x1='0' y1='0' x2='0' y2='1'>
      <stop offset='0' stop-color='#ffffff'/><stop offset='1' stop-color='#f4f7fa'/>
    </linearGradient>
    <linearGradient id='board' x1='0' y1='0' x2='0' y2='1'>
      <stop offset='0' stop-color='#1c2b34'/><stop offset='1' stop-color='#0b141a'/>
    </linearGradient>
    <linearGradient id='train' x1='0' y1='0' x2='0' y2='1'>
      <stop offset='0' stop-color='#26313b'/><stop offset='1' stop-color='#05080b'/>
    </linearGradient>
  </defs>
  <rect x='7' y='7' width='114' height='114' rx='22' fill='url(#bg)' stroke='#dfe5ea' stroke-width='5'/>
  <rect x='22' y='20' width='72' height='54' rx='8' fill='url(#board)' stroke='#050b10' stroke-width='3'/>
  <rect x='29' y='29' width='34' height='4' rx='2' fill='#25e04e'/>
  <rect x='68' y='29' width='17' height='4' rx='2' fill='#25e04e'/>
  <rect x='29' y='41' width='30' height='4' rx='2' fill='#25e04e'/>
  <rect x='68' y='41' width='17' height='4' rx='2' fill='#25e04e'/>
  <rect x='29' y='53' width='34' height='4' rx='2' fill='#25e04e'/>
  <rect x='68' y='53' width='17' height='4' rx='2' fill='#25e04e'/>
  <g fill='none' stroke='#0b74e8' stroke-width='6' stroke-linecap='round'>
    <path d='M99 20c13 3 22 13 25 26'/>
    <path d='M100 33c6 2 11 7 13 14'/>
  </g>
  <circle cx='99' cy='48' r='4' fill='#0b74e8'/>
  <path d='M28 75v17M88 75v17' stroke='#0b74e8' stroke-width='6' stroke-linecap='round'/>
  <path d='M32 108h64M43 99l-12 9M83 99l12 9' stroke='#0b74e8' stroke-width='6' stroke-linecap='round'/>
  <g transform='translate(36 55)'>
    <rect x='23' y='0' width='14' height='25' rx='4' fill='url(#train)' stroke='#38424c' stroke-width='2'/>
    <ellipse cx='30' cy='4' rx='14' ry='5' fill='#1b2229' stroke='#cfd5dc' stroke-width='2'/>
    <path d='M12 27c4-9 13-15 18-15s14 6 18 15' fill='#151b22' stroke='#38424c' stroke-width='2'/>
    <circle cx='30' cy='34' r='21' fill='url(#train)' stroke='#1c232b' stroke-width='4'/>
    <circle cx='30' cy='34' r='13' fill='#111820' stroke='#303943' stroke-width='3'/>
    <circle cx='30' cy='34' r='6' fill='#ffb400' stroke='#875900' stroke-width='2'/>
    <circle cx='30' cy='34' r='3' fill='#fff0a0'/>
    <rect x='3' y='52' width='54' height='10' rx='3' fill='#111820' stroke='#303943' stroke-width='2'/>
    <circle cx='8' cy='50' r='6' fill='#ffb400' stroke='#875900' stroke-width='2'/>
    <circle cx='52' cy='50' r='6' fill='#ffb400' stroke='#875900' stroke-width='2'/>
    <path d='M0 62h60l-5 16H5z' fill='#111820' stroke='#303943' stroke-width='2'/>
    <path d='M12 65l-3 10M24 65l-2 10M36 65l2 10M48 65l3 10' stroke='#39424d' stroke-width='3' stroke-linecap='round'/>
  </g>
</svg>)SVG";

#define EEPROM_SIZE    3072
#define OLED_ADDR      0x3C
#define SCREEN_WIDTH   128
#define SCREEN_HEIGHT  64
#define MAX_TRACKS     6

struct AppConfig {
  uint32_t magic;
  char hostname[32];
  char language[4];
  char wifiSsid[64];
  char wifiPassword[64];
  char mqttHost[96];
  uint16_t mqttPort;
  char mqttUser[64];
  char mqttPassword[64];
  bool mqttTls;
  bool retained;
  char boardName[40];
  uint8_t trackCount;
  char rowTopic[96];
  char homeStation[16];
  uint32_t displayIntervalMs;
};

const uint32_t CONFIG_MAGIC = 0x41525632; // ARV2
AppConfig cfg;

ESP8266WebServer server(80);
DNSServer dnsServer;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

WiFiClient plainClient;
BearSSL::WiFiClientSecure secureClient;
PubSubClient mqtt;

bool portalMode = false;
String trackTrain[MAX_TRACKS];
String trackFrom[MAX_TRACKS];
String trackTime[MAX_TRACKS];
String lastMqttStatus = "-";
String lastPayloadTopic = "-";
String lastPayloadValue = "-";
unsigned long lastWifiAttempt = 0;
unsigned long lastMqttAttempt = 0;
unsigned long lastLog = 0;
unsigned long lastDisplayUpdate = 0;
uint32_t minFreeHeap = 0xFFFFFFFF;
uint32_t lastFreeHeap = 0;
