#include <Preferences.h>
#include <NmraDcc.h>
#include <FastLED.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Update.h>

#include "config.h"

#define DCC_PIN 26

#define OLED_SDA_PIN 32
#define OLED_SCL_PIN 33

#define SETUP_BUTTON_PIN 27
#define SETUP_BUTTON_HOLD_MS 3000

// DCC/MQTT Lysdekoder V20.0 - Hostname + firmware upload
// Maks. 5 LED-bånd:
// A = GPIO5
// B = GPIO18
// C = GPIO19
// D = GPIO21
// E = GPIO22
// OLED I2C:
// SDA = GPIO32
// SCL = GPIO33
#define MAX_STRIPS 5
#define MAX_LEDS_PER_STRIP 30
#define TOTAL_LEDS (MAX_STRIPS * MAX_LEDS_PER_STRIP)

// Config-version hæves, fordi vi nu har 5 bånd og ledCount pr. bånd i flash.
#define CONFIG_VERSION 8

const uint8_t stripPins[MAX_STRIPS] = { 5, 18, 19, 21, 22 };

Preferences prefs;
NmraDcc Dcc;
WebServer server(80);
DNSServer dnsServer;
WiFiClient plainClient;
WiFiClientSecure secureClient;
PubSubClient mqttClient;

bool setupMode = false;
bool forceWifiSetupMode = false;
String connectedWifiSsid = "";
const char* SETUP_AP_SSID = "LysDekoder-Setup";
const char* SETUP_AP_PASSWORD = "";   // tom = aabent setup-netvaerk
const byte DNS_PORT = 53;

CRGB leds[MAX_STRIPS][MAX_LEDS_PER_STRIP];

struct LedConfig {
  uint16_t dccAddress;
  uint8_t mode;
  uint8_t brightness;
};

LedConfig config[MAX_STRIPS][MAX_LEDS_PER_STRIP];
uint8_t ledCount[MAX_STRIPS];
bool ledState[MAX_STRIPS][MAX_LEDS_PER_STRIP];
uint32_t nextEffectUpdate[MAX_STRIPS][MAX_LEDS_PER_STRIP];
uint8_t effectPhase[MAX_STRIPS][MAX_LEDS_PER_STRIP];

struct RandomStripConfig {
  uint16_t dccAddress;
};

RandomStripConfig randomConfig[MAX_STRIPS];
uint16_t randomDayMinutes = 2;
uint16_t randomNightMinutes = 5;

struct RandomStripState {
  bool running;
  bool night;
  uint32_t phaseStart;
  uint32_t nextAction;
};

RandomStripState randomState[MAX_STRIPS];


// Input mode:
// 1 = kun DCC
// 2 = kun MQTT
// 3 = DCC + MQTT
uint8_t inputMode = 1;

// Webinterface-sprog:
// 0 = Dansk, 1 = English, 2 = Deutsch
uint8_t language = 0;

struct MqttSettings {
  bool enabled;
  char broker[64];
  uint16_t port;
  char user[40];
  char password[40];
  char baseTopic[64];
  bool useTls;
  bool tlsInsecure;   // true = nem TLS uden certifikat-validering / setInsecure()
};

MqttSettings mqttSettings;

char stripName(int strip) {
  return char('A' + strip);
}

int globalLedIndex(int strip, int led) {
  return strip * MAX_LEDS_PER_STRIP + led;
}

bool globalToStripLed(int globalIndex, int &strip, int &led) {
  if (globalIndex < 0 || globalIndex >= TOTAL_LEDS) return false;
  strip = globalIndex / MAX_LEDS_PER_STRIP;
  led = globalIndex % MAX_LEDS_PER_STRIP;
  return led < ledCount[strip];
}

bool isLedActive(int strip, int led) {
  if (strip < 0 || strip >= MAX_STRIPS) return false;
  if (led < 0 || led >= MAX_LEDS_PER_STRIP) return false;
  return led < ledCount[strip];
}

void setupFastLed() {
  FastLED.addLeds<WS2812B, 5, GRB>(leds[0], MAX_LEDS_PER_STRIP);
  FastLED.addLeds<WS2812B, 18, GRB>(leds[1], MAX_LEDS_PER_STRIP);
  FastLED.addLeds<WS2812B, 19, GRB>(leds[2], MAX_LEDS_PER_STRIP);
  FastLED.addLeds<WS2812B, 21, GRB>(leds[3], MAX_LEDS_PER_STRIP);
  FastLED.addLeds<WS2812B, 22, GRB>(leds[4], MAX_LEDS_PER_STRIP);

  FastLED.clear();
  FastLED.show();
}

bool handleSetupButtonAtStartup() {
  pinMode(SETUP_BUTTON_PIN, INPUT_PULLUP);

  // Kort tryk skal ikke gøre noget. Kun hvis knappen holdes nede
  // i hele SETUP_BUTTON_HOLD_MS under opstart, slettes gemt WiFi.
  if (digitalRead(SETUP_BUTTON_PIN) == HIGH) return false;

  Serial.println("SETUP-knap holdes nede ved opstart");
  oledShowMessage("SETUP", "Hold 3 sek", "for WiFi reset", "");

  unsigned long start = millis();
  while (millis() - start < SETUP_BUTTON_HOLD_MS) {
    if (digitalRead(SETUP_BUTTON_PIN) == HIGH) {
      Serial.println("SETUP-knap sluppet - WiFi reset annulleret");
      oledShowMessage("SETUP", "Annulleret", "WiFi beholdes", "");
      delay(900);
      return false;
    }
    delay(50);
  }

  clearSavedWifi();
  forceWifiSetupMode = true;
  Serial.println("Gemt WiFi slettet - starter setup mode");
  oledShowMessage("WiFi reset", "SSID slettet", "Starter setup", "");
  delay(1200);
  return true;
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  randomSeed(esp_random());

  setupOled();
  oledShowMessage("DCC/MQTT", "LysDekoder", "V20.0", "Starter...");
  forceWifiSetupMode = handleSetupButtonAtStartup();

  setupFastLed();

  if (!loadConfig()) {
    Serial.println("Ingen gyldig config fundet - bruger standard.");
    setDefaultConfig();
    saveConfig();
  }

  // printConfig();  // sparet i v20.0

  WiFi.mode(WIFI_STA);
  bool wifiOk = false;
  if (!forceWifiSetupMode) {
    wifiOk = connectWiFi();
  } else {
    Serial.println("Springer normal WiFi-forbindelse over pga. SETUP-knap");
  }
  if (wifiOk) {
    setupMode = false;
    String host = getWifiHostname();
    if (MDNS.begin(host.c_str())) {
      MDNS.addService("http", "tcp", 80);
      Serial.println("mDNS startet: http://" + host + ".local");
    } else {
      Serial.println("mDNS kunne ikke starte");
    }
    oledShowMessage("WiFi OK", "IP:", WiFi.localIP().toString(), "Starter web...");
  } else {
    startWifiSetupMode();
  }
  setupWebServer();

  Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, true);
  Dcc.init(MAN_ID_DIY, 10, FLAGS_DCC_ACCESSORY_DECODER, 0);

  Serial.println();
  setupMqtt();

  Serial.println("DCC/MQTT Lysdekoder V20.0 - Hostname + firmware upload");
  // printHelp() fjernet i slim-versionen for at spare programplads.
}

void loop() {
  Dcc.process();
  // Serial editor fjernet i v20.0 for at give plads til OTA.
  if (setupMode) dnsServer.processNextRequest();
  server.handleClient();
  handleMqtt();
  updateRandomHouse();
  updateEffects();
  updateOled();
}
