#include "SAB.h"
#include "webApi.h"
#include "tasks.h"

boolean apiTesterApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient* wc, void* pData)
{
  (void)HRD;
  (void)pData;
  char buffer[] = "{\"status\":\"ok\",\"API\":\"available\"}";
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean viewByUIDApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  char buffer[200];
  DictionaryHelper::StringEntity *pSE = HRD.getParams("UID");

  if(pSE == NULL)
  {
    strcpy(buffer, "{\"status\":\"failed\",\"message\":\"expected UID parameter\"}");
  }
  else if(strlen(pSE->getString()) > 0)
  {
    if(p->getScreenManager().displayView(atoi(pSE->getString())))
      sprintf(buffer, "{\"status\":\"ok\",\"ViewUID\":\"%d\"}", p->getScreenManager().getCurrentViewUID());
    else
      sprintf(buffer, "{\"status\":\"failed\",\"message\":\"%s\"}", p->getScreenManager().getErrorMessage());
  }
  else
    strcpy(buffer, "{\"status\":\"failed\",\"message\":\"UID parameter empty\"}");
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean nextViewApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[200];
  
  if(p->getScreenManager().displayNextView())
    sprintf(buffer, "{\"status\":\"ok\",\"ViewUID\":\"%d\"}", p->getScreenManager().getCurrentViewUID());
  else
    sprintf(buffer, "{\"status\":\"failed\",\"message\":\"%s\"}", p->getScreenManager().getErrorMessage());
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean reloadViewApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[200];

  if(p->getScreenManager().init())
    strcpy(buffer, "{\"status\":\"ok\",\"message\":\"config reloaded\"}");
  else
    strcpy(buffer, "{\"status\":\"failed\",\"message\":\"unable to reload config\"}");

  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean rtcGetTimeApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[200];
  DateTime d = p->getRtcManager().getDateTime();
  
  sprintf(buffer, "{\"status\":\"ok\",\"date\":\"%d/%d/%d\",\"time\":\"%d:%d:%d\"}", d.day(), d.month(), d.year(), d.hour(), d.minute(), d.second());
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  
  wc->print(buffer);
  return true;
}

boolean rtcSetTimeApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  char buffer[200];
  DictionaryHelper::StringEntity *pSE = HRD.getParams("datetime");
  if(pSE == NULL)
  {
    strcpy(buffer,"{\"status\":\"failed\",\"message\":\"expected datetime parameter\"}");
  }
  else if(strlen(pSE->getString()) > 0)
  {
    Dictionary<DictionaryHelper::StringEntity> *dictio = pSE->split('_');
    if(dictio == NULL)
    {
      strcpy(buffer,"{\"status\":\"failed\",\"message\":\"datetime format error\"}");
    }
    else
    {
      if(dictio->count() != 6)
      {
        strcpy(buffer,"{\"status\":\"error\",\"message\":\"datetime format error\"}"); 
      }
      else
      {
        DateTime dt( 
          atoi(dictio->getAt(2)->getString()),
          atoi(dictio->getAt(1)->getString()),
          atoi(dictio->getAt((unsigned int)0)->getString()),
          atoi(dictio->getAt(3)->getString()),
          atoi(dictio->getAt(4)->getString()),
          atoi(dictio->getAt(5)->getString())
          );
        p->getRTC_DS3231().adjust(dt);
        p->getRtcManager().setDateTime(dt);
        DateTime d = p->getRtcManager().getDateTime();
        sprintf(buffer,"{\"status\":\"ok\",\"date\":\"%d/%d/%d\",\"time\":\"%d:%d:%d\"}", d.day(), d.month(), d.year(), d.hour(), d.minute(), d.second());
      }
      delete dictio;
    }
  }
  else
  {
    strcpy(buffer,"{\"status\":\"failed\",\"message\":\"datetime parameter is empty\"}");
  }
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean sdCardActionApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SdCardApiPacket *pV = (SdCardApiPacket*)pData;
  SAB *p = (SAB *)pV->pSab;
  float *sdCardSize = (float *) &pV->pView->sdCardSize;
  char buffer[200];

  //We first retrieve the parameter that indicates the action :
  if(HRD.getParams.count() != 1)
  {
    strcpy_P(buffer, PSTR("{\"status\":\"error\",\"message\":\"One and only one parameter is expected\"}"));
  }
  else
  {
    if(HRD.getParams("action") != NULL)
    {
      if(strcmp_P(HRD.getParams("action")->getString(), PSTR("mount")) == 0)
      {
        if(p->getSdCardManager().mountSD())
        {
          strcpy_P(buffer, PSTR("{\"status\":\"ok\",\"card\":\"mounted\"}"));
          *sdCardSize = p->getSdCardManager().getSize(GBYTE);
        }
        else
          strcpy_P(buffer, PSTR("{\"status\":\"error\",\"message\":\"begin failed\"}"));
      }
      else if(strcmp_P(HRD.getParams("action")->getString(), PSTR("unmount")) == 0)
      {
        p->getSdCardManager().unMountSD();
        *sdCardSize = 0.0;
        strcpy_P(buffer, PSTR("{\"status\":\"ok\",\"card\":\"unmounted\"}"));
      }
      else //Error
      {
        strcpy_P(buffer, PSTR("{\"status\":\"error\",\"message\":\"Parameter 'action' should be mount or unmount\"}"));
      }
    }
    else //Error
    {
      strcpy_P(buffer, PSTR("{\"status\":\"error\",\"message\":\"Parameter should be 'action'\"}"));
    }
  }
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  
  wc->print(buffer);
  return true;
}

boolean espRestartApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *sab = (SAB*)pData;
  char  buffer[200];
  
  sprintf(buffer,"{\"status\":\"ok\",\"message\":\"module %s in 10 seconds\"}",strstr(HRD.httpResource,"reset") == NULL ? "restarting" : "resetting");
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  
  sab->getTaskSchedulerManager().addTask(sab->getTaskSchedulerManager().findFreeTaskId(), TaskSchedulerManagerHelper::Schedule::scheduleBuilder()
  ->setSeconds(10)
  ->setTriggerType(TaskSchedulerManagerHelper::Schedule::TRIGGER_ONCE_AND_DELETE)
  ->setTriggerRightAway(false), &(task_esp_reset_restart), NULL);
  
  return true;
}

boolean espResetApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  return espRestartApi(ws, HRD, wc, pData);
}

boolean sdCardSizeApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
	SAB *p = (SAB *)pData;
	char buffer[200];
	double size = p->getSdCardManager().getSize(GBYTE);
	if(size == 0.0)
		strcpy(buffer,"{\"status\":\"ok\",\"card\":\"not present\",\"size\":\"0\"}");
	else
		sprintf(buffer,"{\"status\":\"ok\",\"card\":\"present\",\"size\":\"%.2f\",\"unit\":\"GByte\"}",size);
	
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
	wc->print(buffer);
	return true;
}

boolean staWifiInfoApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
	SAB *p = (SAB *)pData;
	char buffer[300];
  IPAddress IP = p->getConnectivityManager().localIP();

  sprintf(buffer,"{\"status\":\"ok\",\"RSSI\":%d,\"RSSI2\":%d,\"local IP\":\"%u.%u.%u.%u\",\"MAC\":\"%s\"}", p->getConnectivityManager().RSSI(), p->getConnectivityManager().RSSIPercent(), IP[0], IP[1], IP[2], IP[3], p->getConnectivityManager().macAddress().c_str());
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
	return true;
}

boolean apWifiInfoApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[300];

  //We check if the AP is enabled
  boolean apEnabled = p->getConnectivityManager().getMode() == WIFI_AP || p->getConnectivityManager().getMode() == WIFI_AP_STA;
  
  if(apEnabled)
  {
    IPAddress apIP = p->getConnectivityManager().softAPIP();

    sprintf(buffer, "{\"status\":\"ok\",\"AP\":\"enabled\",\"AP IP\":\"%u.%u.%u.%u\",\"MAC\":\"%s\",\"STA count\":%u,\"SSID\":\"%s\",\"Password\":\"%s\"}",
    apIP[0], apIP[1], apIP[2], apIP[3],
    p->getConnectivityManager().softAPmacAddress().c_str(),
    p->getConnectivityManager().softAPgetStationNum(),
    p->getConnectivityManager().softAPSSID().c_str(),
    p->getConnectivityManager().softAPPSK().c_str());
  }
  else
  {
    sprintf(buffer, "{\"status\":\"ok\",\"AP\":\"disabled\",\"SSID\":\"%s\",\"Password\":\"%s\"}",
    p->getConnectivityManager().softAPSSID().c_str(),
    p->getConnectivityManager().softAPPSK().c_str());
  }

  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean apScannerApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  (void)pData;
  uint8_t number = WiFi.scanNetworks();
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON));
  wc->print("[");
    
  for(uint8_t i(0); i < number; i++)
  {
    wc->printf("%s{\"SSID\":\"%s\",\"RSSI\":%d,\"Protected\":\"%s\"}", i == 0 ? "":",", WiFi.SSID(i).c_str(),  WiFi.RSSI(i), WiFi.encryptionType(i) == ENC_TYPE_NONE ? "No":"Yes");
  }
  
  wc->print("]");
  return true;
}

boolean systemInfoApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[300];
  uint32_t freeMem;
  uint16_t biggestContigMemBlock;
  uint8_t frag;
  ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag);
  TimeSpan ts(p->getUpTime());
  
  sprintf(buffer, "{\"status\":\"ok\",\"version\":\"%s\",\"CPU freq\":%u,\"free RAM\":%u,\"heap frag\":%u,\"max block\":%u,\"nb views\":%u,\"up time\":{\"days\":%d,\"hours\":%d,\"minutes\":%d,\"seconds\":%d},\"temperature\":{\"level\":%.2f, \"unit\":\"°C\"}}", p->getSoftVersion(), ESP.getCpuFreqMHz(), freeMem, frag, biggestContigMemBlock, p->getScreenManager().getViewCount(), ts.days(), ts.hours(), ts.minutes(), ts.seconds(), p->getRTC_DS3231().getTemperature());
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  
  wc->print(buffer);
  return true;
}

boolean powerInfoApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  SAB *p = (SAB *)pData;
  char buffer[300];
  PowerManager::PowerInfo pi = p->getPowerManager().getPowerInfo();
   
  sprintf(buffer,"{\"status\":\"ok\",\"power type\":\"%s\",\"level\":\"%u\",\"unit\":\"%%\"}", pi.powerType == PowerManager::USB ? "USB" : "BAT", pi.level);
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}
/* Changing the MCU frequency on the fly isn"t supported anymore by the SDK.
boolean powerSettingsApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  char buffer[300];

  if(HRD.getParams.count() != 1)
  {
    strcpy(buffer, "{\"status\":\"error\",\"message\":\"One and only one parameter is expected\"}");
  }
  else
  {
    if(HRD.getParams("FREQ") != NULL)
    {
      uint8_t frequency = strtoul(HRD.getParams("FREQ")->getString(), NULL, 10);
      if(frequency != 80 && frequency != 160)
      {
        strcpy(buffer, "{\"status\":\"error\",\"message\":\"Parameter FREQ should be 80 or 160\"}");
      }
      else
      {
        sprintf(buffer, "{\"status\":\"%s\",\"CPU freq\":%u}", p->getPowerManager().setCpuFreq(frequency) ? "ok" : "failed", ESP.getCpuFreqMHz());
      }
    }
    else
      strcpy(buffer, "{\"status\":\"error\",\"message\":\"Parameter FREQ expected\"}");
  }
   
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  
  wc->print(buffer);
  return true;
}
*/

boolean ioGetLevelApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  boolean *ioState = (boolean *)pData;
  char buffer[300] = "";
  char helperBuffer[20] = "";

  if(HRD.getParams.count() == 0)//We send every IO state
  {
    sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%d\",\"P1\":\"%d\",\"P2\":\"%d\",\"P3\":\"%d\",\"P4\":\"%d\",\"P5\":\"%d\",\"P6\":\"%d\",\"P7\":\"%d\"}",ioState[0],ioState[1],ioState[2],ioState[3],ioState[4],ioState[5],ioState[6],ioState[7]);
  }
  else
  {
    strcpy(buffer,"{\"status\":\"ok\"");
    if(HRD.getParams("P0") != NULL)
    {
      sprintf(helperBuffer,",\"P0\":\"%d\"",ioState[0]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P1") != NULL)
    {
      sprintf(helperBuffer,",\"P1\":\"%d\"",ioState[1]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P2") != NULL)
    {
      sprintf(helperBuffer,",\"P2\":\"%d\"",ioState[2]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P3") != NULL)
    {
      sprintf(helperBuffer,",\"P3\":\"%d\"",ioState[3]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P4") != NULL)
    {
      sprintf(helperBuffer,",\"P4\":\"%d\"",ioState[4]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P5") != NULL)
    {
      sprintf(helperBuffer,",\"P5\":\"%d\"",ioState[5]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P6") != NULL)
    {
      sprintf(helperBuffer,",\"P6\":\"%d\"",ioState[6]);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P7") != NULL)
    {
      sprintf(helperBuffer,",\"P7\":\"%d\"",ioState[7]);
      strcat(buffer,helperBuffer);
    }
    strcat(buffer,"}");
  }

  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean ioSetLevelApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  char buffer[300] = "";
  boolean ioState[8] = {0};

  if(HRD.getParams("P0") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P0,atoi(HRD.getParams("P0")->getString()));
  }
  if(HRD.getParams("P1") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P1,atoi(HRD.getParams("P1")->getString()));
  }
  if(HRD.getParams("P2") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P2,atoi(HRD.getParams("P2")->getString()));
  }
  if(HRD.getParams("P3") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P3,atoi(HRD.getParams("P3")->getString()));
  }
  if(HRD.getParams("P4") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P4,atoi(HRD.getParams("P4")->getString()));
  }
  if(HRD.getParams("P5") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P5,atoi(HRD.getParams("P5")->getString()));
  }
  if(HRD.getParams("P6") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P6,atoi(HRD.getParams("P6")->getString()));
  }
  if(HRD.getParams("P7") != NULL)
  {
    p->getIOManager().getPcf().digitalWrite(PCF8574::P7,atoi(HRD.getParams("P7")->getString()));
  }

  p->getIOManager().getPcf().digitalReadAll(ioState);//We retrieve the IO state
  sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%d\",\"P1\":\"%d\",\"P2\":\"%d\",\"P3\":\"%d\",\"P4\":\"%d\",\"P5\":\"%d\",\"P6\":\"%d\",\"P7\":\"%d\"}",ioState[0],ioState[1],ioState[2],ioState[3],ioState[4],ioState[5],ioState[6],ioState[7]);
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean ioGetModeApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  boolean ioMode[8] = {0};
  char buffer[300] = "";
  char helperBuffer[20] = "";
  const char * const IN = "IN", * const OUT = "OUT";

  p->getIOManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT

  if(HRD.getParams.count() == 0)//We send every IO mode
  {
    sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%s\",\"P1\":\"%s\",\"P2\":\"%s\",\"P3\":\"%s\",\"P4\":\"%s\",\"P5\":\"%s\",\"P6\":\"%s\",\"P7\":\"%s\"}",
    ioMode[0] ? OUT:IN,
    ioMode[1] ? OUT:IN,
    ioMode[2] ? OUT:IN,
    ioMode[3] ? OUT:IN,
    ioMode[4] ? OUT:IN,
    ioMode[5] ? OUT:IN,
    ioMode[6] ? OUT:IN,
    ioMode[7] ? OUT:IN);
  }
  else
  {
    strcpy(buffer,"{\"status\":\"ok\"");
    if(HRD.getParams("P0") != NULL)
    {
      sprintf(helperBuffer,",\"P0\":\"%s\"",ioMode[0] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P1") != NULL)
    {
      sprintf(helperBuffer,",\"P1\":\"%s\"",ioMode[1] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P2") != NULL)
    {
      sprintf(helperBuffer,",\"P2\":\"%s\"",ioMode[2] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P3") != NULL)
    {
      sprintf(helperBuffer,",\"P3\":\"%s\"",ioMode[3] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P4") != NULL)
    {
      sprintf(helperBuffer,",\"P4\":\"%s\"",ioMode[4] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P5") != NULL)
    {
      sprintf(helperBuffer,",\"P5\":\"%s\"",ioMode[5] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P6") != NULL)
    {
      sprintf(helperBuffer,",\"P6\":\"%s\"",ioMode[6] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    if(HRD.getParams("P7") != NULL)
    {
      sprintf(helperBuffer,",\"P7\":\"%s\"",ioMode[7] ? OUT:IN);
      strcat(buffer,helperBuffer);
    }
    strcat(buffer," }");
  }

  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean ioSetModeApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;
  char buffer[300] = "";
  const char * const IN = "IN", * const OUT = "OUT";
  boolean ioMode[8] = {0};

  if(HRD.getParams("P0") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P0,strcmp(HRD.getParams("P0")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P1") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P1,strcmp(HRD.getParams("P1")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P2") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P2,strcmp(HRD.getParams("P2")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P3") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P3,strcmp(HRD.getParams("P3")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P4") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P4,strcmp(HRD.getParams("P4")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P5") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P5,strcmp(HRD.getParams("P5")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P6") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P6,strcmp(HRD.getParams("P6")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }
  if(HRD.getParams("P7") != NULL)
  {
    p->getIOManager().getPcf().pinMode(PCF8574::P7,strcmp(HRD.getParams("P7")->getString(), IN) == 0 ? INPUT:OUTPUT);
  }

  p->getIOManager().getPcf().getPinModeAll(ioMode);//We retrieve the IO modes aka INPUT or OUTPUT
  sprintf(buffer,"{\"status\":\"ok\",\"P0\":\"%s\",\"P1\":\"%s\",\"P2\":\"%s\",\"P3\":\"%s\",\"P4\":\"%s\",\"P5\":\"%s\",\"P6\":\"%s\",\"P7\":\"%s\"}",ioMode[0] ? OUT:IN,ioMode[1] ? OUT:IN,ioMode[2] ? OUT:IN,ioMode[3] ? OUT:IN,ioMode[4] ? OUT:IN,ioMode[5] ? OUT:IN,ioMode[6] ? OUT:IN,ioMode[7] ? OUT:IN);
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);
  return true;
}

boolean otaUpdateUploadApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)HRD;
  (void)pData;
  Serial.printf("OTA Update Upload resquest\n#");

  char buffer[30] = "";
  size_t read(0), cnt(0);
  unsigned long ts(millis());

  while(wc->available() || millis() - ts < 10)
  {
    read = wc->read(buffer, 30);
    if(read)ts = millis();
    //buffer[read] = '\0';
    for(uint8_t i(0); i < read; i++)
      Serial.printf("%02X",buffer[i]);
    Serial.println();
    //Serial.printf("%s",buffer);
    yield();
  }
  Serial.println("#");

  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN), 2);
  wc->print("OK");

  return true;
}

boolean otaUpdateRemoteApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  SAB *p = (SAB *)pData;

  char buffer[100] = "";
  boolean updateDevice(false);

  if(!p->getOTAManager().isEnabled())
  {
    strcpy(buffer ,"{\"status\":\"ok\",\"ota\":\"disabled\"}");
  }
  else
  {
    OTAUpdater::UpdateInfo updateInfo = p->getOTAManager().getOTAUpdater().fetchUpdateInfo(p->getSoftVersion());
    
    switch(updateInfo.info)
    {
      case OTAUpdater::UpdateInfo::HTTP_NO_UPDATE :
        strcpy(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"No update available\"}");
        break;
      case OTAUpdater::UpdateInfo::HTTP_UPDATE_AUTH_ERROR :
        strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"OTA auth error\"}");
        break;
      case OTAUpdater::UpdateInfo::HTTP_UPDATE_AVAILABLE :
        //Then we update the firmware
        if(HRD.getParams("update") != NULL)
        {
          sprintf(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"Updating the device, please wait\",\"version\":\"%s\"}", updateInfo.version);
          updateDevice = true;
        }
        else //This means, we only want to get the new version number
        {
          sprintf(buffer ,"{\"status\":\"ok\",\"ota\":\"enabled\",\"msg\":\"Update available\",\"version\":\"%s\"}", updateInfo.version);
        }
        break;
      case OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR :
        strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"OTA reach error\"}");
        break;
      default:
        strcpy(buffer ,"{\"status\":\"error\",\"ota\":\"enabled\",\"msg\":\"Undefined error\"}");
        break;
    }
  }
  
  ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
  wc->print(buffer);

  if(updateDevice)
  {
    p->getOTAManager().getOTAUpdater().update(p->getSoftVersion());
  }

  return true;
}

boolean cookieTestApi(WEBServer<WEBClient> *ws, WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc, void *pData)
{
  (void)pData;

  //We first check what kind of action we want to do:
  if(HRD.getParams("set") != NULL)
  {
    uint8_t cookieCount(HRD.getParams.count() - 1);

    for(uint8_t i(0); i < HRD.getParams.count(); i++)
    {
      if(strcmp(HRD.getParams.getParameter(i), "set") != 0)
      {
        if(!strlen(HRD.getParams.getAt(i)->getString())) //We ask to remove the cookie
          ws->addCookies(HRD.getParams.getParameter(i), HRD.getParams.getAt(i)->getString(), 0);
        else //We add the cookie
          ws->addCookies(HRD.getParams.getParameter(i), HRD.getParams.getAt(i)->getString(), 3600);
      }
    }

    ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN)); //We dont know the content length
    wc->printf("Setting/Unsetting %u cookies : \n", cookieCount);
    for(uint8_t i(0); i < HRD.getParams.count(); i++)
    {
      if(strcmp(HRD.getParams.getParameter(i), "set") != 0)
      {
        if(!strlen(HRD.getParams.getAt(i)->getString())) //We ask to remove the cookie
          wc->printf("Unsetting cookie %s->%s\n", HRD.getParams.getParameter(i), HRD.getParams.getAt(i)->getString());
        else //We add the cookie
          wc->printf("Setting cookie %s->%s\n", HRD.getParams.getParameter(i), HRD.getParams.getAt(i)->getString());
      }
    }
  }
  else //We print the setted cookies
  {
    ws->sendHTTPResponse(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN)); //We dont know the content length
    wc->printf("Cookies to display : %u\n", HRD.cookies.count());
    for(uint8_t i(0); i < HRD.cookies.count(); i++)
    {
      wc->printf("cookie %u : %s->%s\n", i, HRD.cookies.getParameter(i), HRD.cookies.getAt(i)->value.getString());
    }
  }

  return true;
}
