//********************************//

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <FS.h>

#include "config.h"

//********* preprocessor block *********//

#ifdef DEVELOPMENT
#define LIGHT_NAME "Dimmable Hue Light (DEV)"
#else
#define LIGHT_NAME LIGHT_NAME_STR
#endif

//********* Config block *********//

// blue, warmwhite, purple, white&red&green
// blau, schwarz, rot, weiß
// ch1, ch2, ch3, ch4
// D1, D2, D7, D5
uint8_t pins[LIGHTS_COUNT] = { 5, 4, 13, 14 };

#ifndef DEVELOPMENT
IPAddress strip_ip   (192, 168,   0, 26);   // choose an unique IP Adress
#endif
#ifdef DEVELOPMENT
IPAddress strip_ip   (192, 168,   0, 27);   // choose an unique IP Adress
#endif
IPAddress gateway_ip (192, 168,   0,  1);  // Router IP
IPAddress subnet_mask(255, 255, 255,  0);
IPAddress dns        (192, 168,   0,  1);

//********************************//

#define LIGHT_VERSION 2.1

#define LAST_STATE_STARTUP_LIGHT_LAST_STATE 0
#define LAST_STATE_STARTUP_LIGHT_ON_STATE   1
#define LAST_STATE_STARTUP_LIGHT_OFF_STATE  2

#define LIGHT_STATE_ON  1
#define LIGHT_STATE_OFF 0

#define TIMING_CONTROL_ENABLED  1
#define TIMING_CONTROL_DISABLED 0

#define SCENE_RELEAX  0
#define SCENE_BRIGHT  1
#define SCENE_NIGHTLY 2

//********************************//

uint8_t scene;
uint8_t tc_enabled;
uint8_t tc_enabled_old;

bool light_state[LIGHTS_COUNT];
bool in_transition;

int default_transitiontime = 4; // 4 = 4 seconds

uint16_t transitiontime[LIGHTS_COUNT];
uint16_t bri[LIGHTS_COUNT];
uint16_t current_pwm[LIGHTS_COUNT];

float step_level[LIGHTS_COUNT];
float current_bri[LIGHTS_COUNT];
byte mac[6];

ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdateServer;

uint32_t last_lightengine_activity = 0;

//********************************//

void setup()
{
  EEPROM.begin(256);

  SPIFFS.begin();

  Serial.begin(SERIAL_BAUD_RATE);

  Serial.flush();
  delay(1000);

  //Serial.println("Flash size: " + (String)ESP.getFlashChipSize());

  Dir dir = SPIFFS.openDir("/");
  Serial.println("\n\nSPIFFS directory content:");
  while (dir.next())
  {
    String fileName = dir.fileName();
    size_t fileSize = dir.fileSize();
    Serial.printf("Datei Name: %s, Größe: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
  }

  if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) == 0)
  {
    WiFi.config(strip_ip, gateway_ip, subnet_mask, dns);
  }

  read_eeprom_config();

  for (int j = 0; j < 200; j++)
  {
    lightEngine();
  }

  WiFi.mode(WIFI_STA);
  WiFiManager wifiManager;
  wifiManager.setConfigPortalTimeout(120);
  wifiManager.autoConnect(LIGHT_NAME);

  IPAddress myIP = WiFi.localIP();
  Serial.print("IP: ");
  Serial.println(myIP);

  analogWriteFreq(PWM_FREQ);

  if (!light_state[0])
  {
    // Show that we are connected
    analogWrite(pins[0], 100);
    delay(500);
    analogWrite(pins[0], 0);
  }

  WiFi.macAddress(mac);

  pinMode(LED_BUILTIN, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
  digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH

  httpUpdateServer.setup(&server);  // start http server

  init_webserver();

  Serial.println("Init timinc control");
  tc_init();

  Serial.println("Starting webserver");
  server.begin();
}  // end of setup

//********************************//

void loop()
{
  static uint8_t state = 0;

  switch (state)
  {
    case 0:
      server.handleClient();
      state = 1;
      ESP.wdtFeed();
      break;
    case 1:
      lightEngine();
      state = 2;
      ESP.wdtFeed();
    case 2:
      tc_update_loop();
      state = 3;
      ESP.wdtFeed();
      break;
    case 3:
      test_pwm_main();
      state = 0;
      ESP.wdtFeed();
      break;
    default:
      state = 0;
  }

  delay(10);
}

//********************************//

void read_eeprom_config()
{

  // -------------------- //
  uint8_t tmp = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS);
  if (tmp == TIMING_CONTROL_DISABLED)
  {
    tc_enabled = TIMING_CONTROL_DISABLED;

  } else if (tmp == TIMING_CONTROL_ENABLED)
  {
    tc_enabled = TIMING_CONTROL_ENABLED;

  } else {
    // Write default value
    EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED);
    EEPROM.commit();
    tc_enabled = TIMING_CONTROL_DISABLED;
    Serial.println("Written default timing control config to EEPROM (disabled)");
  }
  Serial.println("Timing Control status: " + (String)tc_enabled);

  // -------------------- //
  if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) > 2)
  {
    // set the default value on uninitialized EEPROM
    EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, 0);
    EEPROM.commit();
    Serial.println("Written default 'last state' config to EEPROM");
  }
  Serial.println("Last state startup setting: " + (String)EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS));

  if (EEPROM.read(EEPROM_SCENE_ADDRESS) > 2)
  {
    // set the default value on uninitialized EEPROM
    EEPROM.write(EEPROM_SCENE_ADDRESS, 0);
    EEPROM.commit();
    Serial.println("Written default scene config to EEPROM");
  }
  Serial.println("Scene setting: " + (String)EEPROM.read(EEPROM_SCENE_ADDRESS));

  // -------------------- //
#ifdef USE_STATIC_IP
  if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1)
  {
    EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 0);
    EEPROM.commit();
    Serial.println("Written default dynamic IP setting (disabled) to EEPROM");
  }
#else
  if (EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS) > 1)
  {
    EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, 1);
    EEPROM.commit();
    Serial.println("Written default dynamic IP setting (enabled) to EEPROM");
  }
#endif
  Serial.println("Dynamic IP setting: " + (String)EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS));

  // -------------------- //
  for (uint8_t light = 0; light < LIGHTS_COUNT; light++)
  {
    apply_scene(EEPROM.read(EEPROM_SCENE_ADDRESS), light);
    step_level[light] = bri[light] / 150.0;

    if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_LAST_STATE ||
        (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) == LAST_STATE_STARTUP_LIGHT_ON_STATE &&
         EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON))
    {
      light_state[light] = true;  // set the light state to on
    }
    Serial.println("light[" + (String)light + "] = " + (String)light_state[light]);
  }

}

//********************************//

String formatBytes(size_t bytes)
{
  if (bytes < 1024)
  {
    return String(bytes) + " B";
  } else if (bytes < (1024 * 1024))
  {
    return String(bytes / 1024.0) + " KB";
  } else if (bytes < (1024 * 1024 * 1024))
  {
    return String(bytes / 1024.0 / 1024.0) + " MB";
  } else {
    return String(bytes / 1024.0 / 1024.0 / 1024.0) + " GB";
  }
}

//********************************//
