#include "led_clock_time.hpp"

TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};     // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};       // Central European Standard Time
Timezone CE(CEST, CET);

void led_clock_time::init(bool debug)
{
    Serial.println();
 
    http = new HTTPClient();
    timedebug = debug;
    Serial.println("Try to connect to wifi first time ... maybe captive portal will b started ...");
    connectWifi();              // isconnected should be true after here if not in debug mode ... 
    Serial.printf("Connection status :  %s\n", isconnected == true?"connected":"not connected");
    wasalreadyconnected = isconnected;
    rtc_ready = rtc_init();     // at least timeisvalid should be true after here ... 
    if(timeisvalid == false)
    {
      if(isconnected == false)
      {
        //nothing to show ... keep waiting forever in the captive portal ...
        captivepotal_timeout = 0;
        Serial.println("RTC time is not valid and there is no connection to Wifi - will go into cpative portal mode and wait for valid config. Please connect via Wifi to 'BlueLedclock'.");
        connectWifi();
      }
    }
}

bool led_clock_time::rtc_init()
{
    bool rc = true;
    Rtc = new RtcDS3231<TwoWire>(Wire);
    Rtc->Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.println();
    Serial.println("RTC init ...");

    if (!Rtc->IsDateTimeValid()) 
    {
        if (Rtc->LastError() != 0)
        {
            Serial.print("RTC communications error = ");
            Serial.println(Rtc->LastError());
            return(false);
        }
        else
        {
            Serial.println("RTC lost confidence in the DateTime!");
            if(false == rtc_settime(compiled))
            {
              return(false);
            }
        }
    }

    if (!Rtc->GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc->SetIsRunning(true);
    }

    Serial.println("Check RTC time - set to accurate time if needed and possible.");
    if(false == syncTime())
    {
      RtcDateTime now = Rtc->GetDateTime();
      if (now.TotalSeconds() < compiled.TotalSeconds()) 
      {
          rc =  rtc_settime(compiled);
      }
    }
    Rtc->Enable32kHzPin(false);
    Rtc->SetSquareWavePin(DS3231SquareWavePin_ModeNone); 

    rc = rtc_gettime();
    Serial.printf("RTC time is:  %d:%d:%d . Valid time bit is set to '%s'\n", int_hour, int_minute, int_second, timeisvalid==true?"true":"false");
    if(rc == false)
    {
        Serial.println("RTC setup finished with a failure! Time couldn't set to actual value!");
    }
    else
    {
        Serial.println("RTC setup finished with success!");
    }

    return(rc);
  
}

bool led_clock_time::rtc_settime(RtcDateTime newtime)
{
    bool rc = false;
    if((millis() - lastwrittentortc > WAITBEFOREWRITERTCAGAIN_MS) || (lastwrittentortc ==0))
    {
      lastwrittentortc = millis();
      //Serial.println("Try to write time to RTC.");
      Rtc->SetDateTime(newtime);
      RtcDateTime gettime = Rtc->GetDateTime();   
      
      if (newtime.TotalSeconds() <= gettime.TotalSeconds())
      {
          rc = true;
      }
      else
      {
        Serial.printf("Current RTC read time (%d) is smaller than the provided time (%d) that was written before to RTC. This is not correct!", gettime.TotalSeconds(), newtime.TotalSeconds());
      }
    }
    else
    {
      rc = true;
    }

    if(rc == false)
    {
      Serial.println("RTC set time finished with a failure! Time couldn't set to actual value!");
      timeisvalid = false;
    }
    else
    {
      timeisvalid = true;
    }

    return(rc);
}

bool led_clock_time::gettime(uint32_t *hour, uint32_t *minute, uint32_t *second)
{
    if(0 == timedebug)
    {
        syncTime();
        rtc_gettime();
    }
    else
    {
        dbg_gettime();
    }

    *hour = int_hour;
    *second = int_second;
    *minute = int_minute;
    return(timeisvalid);
}

bool led_clock_time::rtc_gettime(void)
{
    bool rc = true;
    RtcDateTime now = Rtc->GetDateTime();
    int_second = now.Second();
    int_minute = now.Minute();
    int_hour = now.Hour();
    if((int_hour == 0) && (int_minute == 0) && (int_second == 0) && (now.Year() == 2000))
    {
        rc = false;
        timeisvalid = false;
        Serial.printf("RTC is stucked at %02d:%02d:%02d %02d.%02d.%04d this is a failure! Time couldn't set to actual value!\n", int_hour, int_minute, int_second, now.Day(), now.Month(), now.Year());
    }

    return(rc);
}

void led_clock_time::dbg_gettime(void)
{
  timeisvalid = 0;
  timesynced = false;
  int_second = int_second + 1;
  if(int_second > 59)
  {
    int_second = 0;
    int_minute++;
    if(int_minute > 59)
    {
      int_minute = 0;
      int_hour++;
      if(int_hour > 11)
      {
        int_hour = 0;
      }
    }
  }
}

bool led_clock_time::connectWifi()
{
  if(timedebug == false)
  {
    if ((WiFi.status() != WL_CONNECTED))
    {
      isconnected = false; 
      Serial.printf("Looking for Wifi: captive portal is open for about %ld seconds.\n",captivepotal_timeout );

      wifiManager.setDebugOutput(false);
      //sets timeout until configuration portal gets turned off
      wifiManager.setTimeout(captivepotal_timeout); //in seconds
      captivepotal_timeout = 30;
      
      //and goes into a blocking loop awaiting configuration
      if(false == wifiManager.autoConnect("BlueLedClock")) {
        Serial.println("Failed to connect to WiFi and hit timeout ... (reset clock to retry). ");
      }
      else
      {
        Serial.println("Connect to WiFi. ");
        isconnected = true;
      }
      
    }
    else
    {
        isconnected = true;
    }
    
  }
  else
  {
    isconnected = false; 
  }
  return isconnected;
}

bool led_clock_time::isConnectedToWifi()
{
  if ((WiFi.status() != WL_CONNECTED))  
  {
    if(wasalreadyconnected == true)
    {
      connectWifi();
    }
    else
    {
      isconnected = false; 
    }
  }
  else{
    isconnected = true; 
  }
  return isconnected;
}

bool led_clock_time::isTimenSynced()
{
  return timesynced;
}


bool led_clock_time::syncTime()
{ 
  TimeChangeRule *tcr;        // pointer to the time change rule, use to get the TZ abbrev
  int httpCode = -1;
  if((timelastsync == 0) || (((millis()/1000)- timelastsync) > NTP_SYNC_CYCLE_SECS))
  {
    if(true == isConnectedToWifi())
    {
      http->begin(TIMEURL);
      // start connection and send HTTP header
      httpCode = http->GET();
      // httpCode will be negative on error
      if (httpCode == HTTP_CODE_OK) {
            String epoch_str = http->getString();
            epoch = epoch_str.toInt();
            epoch = CE.toLocal(epoch, &tcr);
            if(epoch > 0)
            {
          
                struct tm * timeinfo = localtime(&epoch);
                
                char timestr[9] = "";
                char datestr[12] = "";
                sprintf(timestr, "%02d:%02d:%02d",timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
                sprintf(datestr, "%s %d %d", MONTH_STR[timeinfo->tm_mon].c_str(), timeinfo->tm_mday, 1900+timeinfo->tm_year); 

                RtcDateTime rtctime = RtcDateTime(datestr, timestr);
                if(rtc_settime(rtctime) == true)
                {
                    timelastsync = millis()/1000;
                    //Serial.printf("Synced time: %s %s\n", datestr, timestr );
                    timesynced = true;
                }
            }
      }
    }
  }

  if( HTTP_CODE_OK != httpCode )
  {   
    if(((millis()/1000)- timelastsync) > (NTP_SYNC_MAX_TRIES * NTP_SYNC_CYCLE_SECS))
      timesynced = false;
  }

  return(timesynced);
}