/* Includes ------------------------------------------------------------------*/
#include "env.h"

#include "src/epd/epd_cfg.h"
#include "src/epd/epd_driver.h"
#include "src/epd/epd_paint.h"

#include "src/ntp/ntp.h"
#include "src/owm/owm.h"

#include "src/sensor/sensor.h" 
#include "src/touch/gt1151.h"

#include "src/influxdb/influx.h"

#include "src/display/display.h"
#include "src/timedfun/timedfun.h"

#include <Arduino.h>

#include <Wire.h>
#include <stdlib.h>
#include <WiFi.h>
#include <WiFiMulti.h>

#define PIN_SDA 18
#define PIN_SCL 23


/* Global Variables -----------------------------------------------------------*/


static WiFiMulti wifiMulti;
static TwoWire I2C = TwoWire(0);
TaskHandle_t task_measure;
TaskHandle_t task_draw;


static NTPtime* ntp = new NTPtime(NTP_URL);
static Sensor* sensor = new Sensor(&I2C);
static GT1151* touch = new GT1151(&I2C);
static OWM* owm[6] = {new OWM(LOCATION_0, OPENWEATHER_API), 
                     new OWM(LOCATION_1, OPENWEATHER_API), 
                     new OWM(LOCATION_2, OPENWEATHER_API), 
                     NULL,NULL,NULL};

static Influx* iflx = new Influx(NULL, owm);//TODO: Add sensor

static TimedFun* tf = new TimedFun();

static bool time_update = false, view_update = true, view_refresh = true;
static bool shutdownt = false;
static int  shutdownc = 0;


static const int8_t views_size = 6;
static DisplayWrapper* views[views_size] = {
  new MainDisplay(),
  new WeatherDisplay(owm[0]),
  new WeatherDisplay(owm[1]),
  new TempDisplay(sensor),
  new AirDisplay(sensor),
  new LightDisplay(sensor),
};



/* IRQ ------------------------------------------------------------------------*/
hw_timer_t * timer = NULL; 
void IRAM_ATTR onSecondTimer() {
  strDateTime* dt = &(ntp->dt);
  dt->second += 1;
  if (dt->second == 60) {
    dt->minute += 1;
    dt->second = 0;
    if (dt->minute == 60) {
      dt->hour += 1;
      dt->minute = 0;
      if (dt->hour == 24) {
        dt->hour = 0;
        dt->minute = 0;
        dt->second = 0;
      }
      view_refresh =true;
    }
    view_update = true;
    time_update = true;
    tf->minuteTick();
  }
  if(touch->dev.holding) shutdownc++;
  else shutdownc = 0;
}

void IRAM_ATTR onTouch(int8_t contacts, GTPoint *points) {
  if(touch->dev.holding){
    Serial.printf("Draging:\n");
    Serial.printf("C0: #0 %d,%d \n", touch->dev.dx, touch->dev.dy);
    return;
  }
  Serial.printf("Contacts: %d\n", contacts);
  for (uint8_t i = 0; i < contacts; i++) {
    Serial.printf("C%d: #%d %d,%d s:%d\n", i, points[i].id, points[i].x, points[i].y, points[i].a);
    yield();
  }
  if(points[0].y < 125) Display::nextView();
  else Display::prevView();
  view_update = true;
}

/* Setup Functions -----------------------------------------------------------*/
void setup_EPD(){
  Display::setup();
}

void setup_TOUCH() {
  touch->setHandler(onTouch);
  touch->begin();
}

void setup_WIFI(){
  uint8_t tries = 3;
  WIFI_REGISTER_AP(wifiMulti);
  while(wifiMulti.run() != WL_CONNECTED && tries-- >0) delay(500);
}

void setup_TIMER(){
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onSecondTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
}

#define REGISTER_TIMEDFUN(name,code) static void name (){code;}
#define REGISTER_TIMEDFUN_CALL(name,min) tf->registerFun(name,min)

REGISTER_TIMEDFUN(SENSOR_MEASURE_TF, sensor->measure());
REGISTER_TIMEDFUN(OWM_MEASURE_TF, do{uint8_t i = 0;while(owm && owm[i]){owm[i]->update(); ++i;}}while(0));
REGISTER_TIMEDFUN(RECORD_LOCAL_TF, iflx->record_local());
REGISTER_TIMEDFUN(RECORD_WEATHER_TF, iflx->record_weather());
REGISTER_TIMEDFUN(NTP_TF, ntp->updateNTPtime());

void setup_TIMEDFUN() {
  REGISTER_TIMEDFUN_CALL(SENSOR_MEASURE_TF,5);
  REGISTER_TIMEDFUN_CALL(OWM_MEASURE_TF,5);
  REGISTER_TIMEDFUN_CALL(RECORD_LOCAL_TF,5);
  REGISTER_TIMEDFUN_CALL(RECORD_WEATHER_TF,5);
  REGISTER_TIMEDFUN_CALL(NTP_TF, 60);
}

/* Setup Functions --------------------------------------------------------------*/
void setup(){
  Serial.begin(115200);

  I2C.begin(PIN_SDA, PIN_SCL, 100000);
  I2C.setPins(PIN_SDA,PIN_SCL);
  setup_EPD();
  setup_TOUCH();
  setup_WIFI();
  setup_TIMER();
  setup_TIMEDFUN();

  iflx->check();
  Display::setViews(views,views_size);


  tf->updateForce();

  xTaskCreatePinnedToCore(
    loop_MEASURE, "Task LMeasure",
    10000, NULL, 1, &task_measure, 0);
  
  xTaskCreatePinnedToCore(
    loop_DRAW, "Task LDraw",
    10000, NULL, 1, &task_draw, 1);

}


/* The main loop -------------------------------------------------------------*/
void loop(){
  delay(5000);
}

void loop_DRAW(void * pvParameters ){
  for(;;){
    touch->update();
    if(shutdownc >= 10){
      Display::refresh();
      EPD_2IN13_V2_Sleep();
      esp_deep_sleep_start();
    }
    if(!touch->dev.holding) shutdownc = 0;

    if(view_refresh) Display::refresh();
    if(view_update) Display::update();
    if(time_update) Display::drawTime(&(ntp->dt));

    view_refresh = false;
    view_update = false;
    time_update = false;
    vTaskDelay(10);
  }
}


void loop_MEASURE(void * pvParameters ){
  for(;;){
    tf->update();
    vTaskDelay(10);
  }
}