/*
 * Stairs lighting
 * Copyright 2017 (c) Mark van Renswoude
 *
 * https://git.x2software.net/pub/Stairs
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncTCP.h>
#include <TimeLib.h>
#include <ArduinoJson.h>
#include <EspSaveCrash.h>

extern "C" {
  #include <user_interface.h>
}

#include "./config.h"
#include "./debug.h"
#include "./global.h"
#include "./components/PCA9685.h"
#include "./settings/connection.h"
#include "./server/static.h"
#include "./server/settings.h"
#include "./server/firmware.h"
#include "./server/api.h"

#include "./main.wifi.h"
#include "./main.debug.h"
#include "./main.led.h"
#include "./main.triggers.h"


ADC_MODE(ADC_VCC);


// Forward declarations
void handleNotFound(AsyncWebServerRequest* request);


AsyncWebServer server(80);
PCA9685* pwmDriver;


inline void waitForTransition()
{
  while (stairs->inTransition())
  {
    currentTime = millis();
    stairs->tick();
    delay(1);
  }
}


void setup()
{
  _dinit();

  currentTime = millis();

  if (!SPIFFS.begin())
    _dln("Setup :: failed to mount file system");

  connectionSettings->read();
  systemSettings->read();
  stepsSettings->read();
  timeTriggerSettings->read();
  motionTriggerSettings->read();

  pinMode(systemSettings->pinAPButton(), INPUT_PULLUP);
  pinMode(systemSettings->pinLEDAP(), OUTPUT);
  pinMode(systemSettings->pinLEDSTA(), OUTPUT);
  initMotionPins();


  _dln("Setup :: initializing PCA9685");
  pwmDriver = new PCA9685();
  pwmDriver->setAddress(systemSettings->pwmDriverAddress(), systemSettings->pinPWMDriverSDA(), systemSettings->pinPWMDriverSCL());
  pwmDriver->setPWMFrequency(systemSettings->pwmDriverFrequency());
  pwmDriver->setAll(0);

  _dln("Setup :: initializing Stairs");
  stairs = new Stairs();
  stairs->init(pwmDriver);

  _dln("Setup :: starting initialization sequence");
  uint8_t bottomStep = stepsSettings->count() - 1;

  for (uint8_t i = 0; i < InitialisationBlinkCount; i++)
  {
    stairs->set(bottomStep, InitialisationBrightness, InitialisationFadeTime);
    waitForTransition();

    stairs->set(bottomStep, 0, InitialisationFadeTime);
    waitForTransition();
  }

  _dln("Setup :: initializing WiFi");
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);

  initDebug();
  initWiFi();

  _dln("Setup :: registering routes");
  registerStaticRoutes(&server);
  registerAPIRoutes(&server);
  registerSettingsRoutes(&server);
  registerFirmwareRoutes(&server);

  _dln("Setup :: starting HTTP server");
  server.onNotFound(handleNotFound);
  server.begin();
}


void loop()
{
  if (shouldReboot || systemSettingsChanged)
  {
    _dln("Loop :: reboot requested, so long and thanks for all the fish!");
    delay(100);
    ESP.restart();
  }

  currentTime = millis();
  updateDebugStatus();


  if (connectionSettingsChanged)
  {
    _dln("Loop :: connection settings changed");
    initWiFi();
    connectionSettingsChanged = false;
  }


  updateWiFi();
  updateLED();
  updateNTPClient();
  checkTriggers();

  stairs->tick();
}


void handleNotFound(AsyncWebServerRequest *request)
{
  _d("HTTP :: not found: "); _dln(request->url());
  request->send(404);
}