#include <Arduino.h>
#include <Wire.h>
#include "RTClib.h"
#include "web_services.h"
#include <DNSServer.h>

#define DEBUG 1

// Constants
#define PUMP_PIN D7
#define AP_INDICATOR_PIN D6
#define BTN_PIN D5

//#define BTN_LED D13

#define PRESSED_INTERVAL 3000
// #define STARTUP_INTERVAL 6000
#define AKTIVE_INTERVAL 500000
#define Ts 1000

#define AKTIV HIGH
#define INAKTIV LOW

#define BTN_AKTIV LOW
#define BTN_INAKTIV HIGH

#define API_AKTIV HIGH
#define API_INAKTIV LOW
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 4, 1);
DNSServer dnsServer;
enum day
{
  Mon,
  Tue,
  Wed,
  Th,
  Fri,
  Sat,
  Sun
};

// Variables
uint16_t start_time[7] = {t2min(7, 30),
                          t2min(7, 30),
                          t2min(7, 30),
                          t2min(7, 30),
                          t2min(7, 30),
                          t2min(8, 00),
                          t2min(8, 00)}; // time in minutes i.e. h*60+min

uint16_t stop_time[7] = {t2min(14, 00),
                         t2min(14, 00),
                         t2min(14, 00),
                         t2min(14, 00),
                         t2min(14, 00),
                         t2min(12, 00),
                         t2min(12, 00)};

uint8_t status[7] = {AKTIV,
                     AKTIV,
                     AKTIV,
                     AKTIV,
                     AKTIV,
                     INAKTIV,
                     INAKTIV}; // aktiv/inaktiv

String config_file;

uint8_t current_day = Tue;
uint8_t state = STARTUP;
uint8_t btn_state = BTN_INAKTIV;
uint32_t time_stamp = 0;

uint8_t result;
RTC_DS3231 rtc;
DateTime now;
uint8_t ps;
uint8_t apis;
uint16_t session_timeout;

unsigned long prev_time;

uint8_t get_pump_state()
{
  /*
    return current state of the pump based on time and day given
    time should be in minutes format and currrent day should be on the
    enum constant
  */
  uint8_t pump_state = INAKTIV;
  uint8_t h, m;
  int8_t current_day;
  uint16_t current_time;

  now = rtc.now();
  h = now.hour();
  m = now.minute();

  current_day = now.dayOfTheWeek();
  current_day = (current_day-1) < 0? 6:(current_day-1);

  current_time = t2min(h, m);
  if(status[current_day] == AKTIV){
    if (current_time >= start_time[current_day] && current_time < stop_time[current_day])
    {
      pump_state = AKTIV;
    }

    else
    {
      pump_state = INAKTIV;
    }
  }
  else{
    pump_state = INAKTIV;
  }
  return pump_state;
}

void setup()
{
  Serial.begin(115200);

  pinMode(AP_INDICATOR_PIN, OUTPUT);
  pinMode(PUMP_PIN, OUTPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
//  pinMode(BTN_LED, OUTPUT);

  while (!rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    delay(1000);
  }

  bool success = SPIFFS.begin();

  if (!success)
  {
    Serial.println("Error mounting the file system");
    return;
  }
  File file = SPIFFS.open(DEFAULT_CONFIG_FILE, "r");
  if (!file)
  {
    int result = save_config(start_time, stop_time, status, DEFAULT_CONFIG_FILE);

    if (!result)
    {
      Serial.println("Failed to load configuration file.");
    }
  }
  else
  {
    file.close();
  }
#ifdef DEBUG
  Serial.println("Files in SPIFFS");
  Serial.print(getDir_tags());
#endif
  config_file = DEFAULT_CONFIG_FILE;

#ifdef DEBUG
  for (uint8_t i = 0; i < 7; i++)
  {
    // Print values.
    Serial.printf("%d,%d,%d\n", start_time[i], stop_time[i], status[i]);
  }
#endif

  web_services_init();
  delay(100);

  dnsServer.setTTL(300);
  dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
  dnsServer.start(DNS_PORT, "www.update.com", apIP);
  // remove_file("/file.txt");
  int result = load_config(start_time, stop_time, status, config_file);
  // Set Compile Time to the RTC
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // rtc.adjust(DateTime(2022, 1, 24, 8, 29, 40));
  prev_time = time_stamp = millis();
  state = STARTUP;

  //state = IDLE;
  //disable_ap();
  //WiFi.mode(WIFI_OFF);

}

void loop()
{
  // if(state != IDLE){
    dnsServer.processNextRequest();
  // }
 
//digitalWrite(BTN_LED, digitalRead(BTN_PIN));

if(millis() - prev_time > Ts){
  btn_state = digitalRead(BTN_PIN);
  ps = get_pump_state();

  prev_time = millis();
  switch (state)
  {
  case STARTUP:
    state = AP_ACTIVE;
    time_stamp = millis();
    session_timeout = 0;
    //no WLAN after power on 
    time_stamp = time_stamp + 350000; //AKTIVE_INTERVAL - 150000;
    
    // result = load_config(start_time, stop_time, status, config_file)
    //time_stamp = time_stamp + 350000; //AKTIVE_INTERVAL - 150000;
    
    break;

  case WAIT4RES:
    // waiting for finishing web session
    session_timeout++;

    if (session_timeout == 300)
    {
       session_timeout = 0;
       apis = API_INAKTIV;
       state = IDLE;
       disable_ap();
    }

    break;

  case IDLE:
    // Disable AP
   
    if (btn_state == BTN_AKTIV)
    {
      state = BTN_PRESSED;
      time_stamp = millis();
    }
    break;

  case BTN_PRESSED:
    if (btn_state == BTN_AKTIV)
    {
      if (millis() - time_stamp >= PRESSED_INTERVAL)
      {
        state = AP_ACTIVE;
        time_stamp = millis();
      }
    }
    else
    {
      state = IDLE;
    }
    break;

  case AP_ACTIVE:
    // enable AP
    apis = API_AKTIV;
    if (WiFi.getMode() != WIFI_AP){
       WiFi.mode(WIFI_AP);
       web_services_init();
    } 
    enable_ap();
   
    if (millis() - time_stamp >= AKTIVE_INTERVAL)
    {
      apis = API_INAKTIV;
      state = IDLE;
      disable_ap();
      WiFi.mode(WIFI_OFF);
    }
    break;
   
  default:
    state = IDLE;
    break;
  }

  digitalWrite(PUMP_PIN, ps);
  digitalWrite(AP_INDICATOR_PIN, apis);

#ifdef DEBUG
  Serial.print("State: ");
  //Serial.printf("%d\n", state);

 switch (state)
  {
  case STARTUP:
    Serial.print("Startup");
  
    break;

  case WAIT4RES:
    Serial.print("Wait4Res");
  
    break;

  case IDLE:
    Serial.print("Idle");
  
    break;

  case BTN_PRESSED:
    Serial.print("Button Pressed");
  
    break;

  case AP_ACTIVE:
    Serial.print("AP_Active");
  
    break;

  }


//  Serial.print(" Y ");
//  Serial.print(now.year());
//  Serial.print(" M ");
//  Serial.print(now.month());
//  Serial.print(" ");  
  
  Serial.print(" (");
  Serial.print(now.dayOfTheWeek());

  Serial.print(") ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  //Serial.println();

  Serial.print(" Fan: ");
  Serial.println(ps ? "aktiv" : "inaktiv");

#endif
}
  // delay(Ts);
}