#ifndef OTAUPDATER_H
#define OTAUPDATER_H
#include <ESP8266httpUpdate.h>

#define OTAUPDATER_STRING //PSTR

class OTAUpdater : public ESP8266HTTPUpdate
{
  public:
    struct UpdateInfo
    {
      enum HTTPUpdateInfo {
                          HTTP_NO_UPDATE, //There is no new update available
                          HTTP_UPDATE_AVAILABLE, //There is an update ready to be applied
                          HTTP_UPDATE_AUTH_ERROR, //There was an error during the authentification process by the update service
                          HTTP_UPDATE_REACH_ERROR, //Unable to reach the update service
                          HTTP_UPDATE_UNDEFINED_ERROR
                        };

      UpdateInfo(){}
      UpdateInfo(const UpdateInfo &object)
      {
        info = object.info;
        if(object.version)
        {
          version = (char *) malloc((strlen(object.version) + 1) * sizeof(char));
          if(version != NULL)
            strcpy(version, object.version);
        }
      }
      ~UpdateInfo(){ dispose(); }
      UpdateInfo& operator=(const UpdateInfo &object)
      {
        info = object.info;
        if(version)
        {
          free(version);version = NULL;
        }
        
        if(object.version)
        {
          version = (char *) malloc((strlen(object.version) + 1) * sizeof(char));
          if(version != NULL)
            strcpy(version, object.version);
        }
        return *this;
      }
      void dispose()
      {
        if(version)
        {
          free(version);version = NULL;
        }
      }
      const char *HTTPUpdateInfoToString(void)
      {
        switch(info)
        {
          case HTTP_NO_UPDATE:
            return OTAUPDATER_STRING("HTTP_NO_UPDATE");
            break;
          case HTTP_UPDATE_AVAILABLE:
            return OTAUPDATER_STRING("HTTP_UPDATE_AVAILABLE");
            break;
          case HTTP_UPDATE_AUTH_ERROR:
            return OTAUPDATER_STRING("HTTP_UPDATE_AUTH_ERROR");
            break;
          case HTTP_UPDATE_REACH_ERROR:
            return OTAUPDATER_STRING("HTTP_UPDATE_REACH_ERROR");
            break;
          default:
            return OTAUPDATER_STRING("HTTP_UPDATE_UNDEFINED_ERROR");
            break;
        }
      }
      HTTPUpdateInfo info = HTTP_NO_UPDATE;
      char *version = NULL;
    };
    OTAUpdater(const char *serverAddress = NULL, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80);
    OTAUpdater(int httpClientTimeout, const char *serverAddress, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80);
    
    ~OTAUpdater();
    //Enables the binary signing verification
    void enableSHA256UpdateVerification(const char *publicKey);
    void setServerAddress(const char *serverAddress);
    void setPath(const char *path = NULL);
    void setOtaAuthKey(const char *ota_auth_key = NULL);
    void setPort(uint16_t port = 80);
    UpdateInfo fetchUpdateInfo(const char *softwareVersion, uint32_t timeout = 500);
    HTTPUpdateResult update(const char *softwareVersion);
  protected:
    BearSSL::PublicKey *_publicKey = nullptr;
    BearSSL::HashSHA256 *_hash = nullptr;
    BearSSL::SigningVerifier *_signingVerifier = nullptr;

    char *_serverAddress = nullptr;
    char *_path = nullptr;
    char *_ota_auth_key = nullptr;
    uint16_t _port;
  private:
};

#endif //OTAUPDATER_H
