#include <SPI.h>



#include <PubSubClient.h>
#include <Wire.h>

#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

#include "MqttUtiles.h"
//**************************************************************************************************************************************************

bool MqttUtiles::pasa_incre( unsigned long *tt, unsigned long incre)
{
  unsigned long t=millis();
  if(t<*tt)
  {
    *tt=t;
    return true;
  }
  if((*tt+incre)>=t)
    return false;
 *tt=t;
  return true;
}

bool MqttUtiles::pasa_incre( volatile unsigned long *tt, unsigned long incre)
{
  unsigned long t=millis();
  if(t<*tt)
  {
    *tt=t;
    return true;
  }
  if((*tt+incre)>=t)
    return false;
 *tt=t;
  return true;
}
//**************************************************************************************************************************************************
WifiManager::WifiManager()
{
    
}
  
void WifiManager::inicia( WiFiClient *espclient,char *ssid, char* key, char *idArdu)
{
   espClient= espclient;
   strcpy(nred, ssid);
   strcpy(pass, key);
   strcpy(idArduino,idArdu);
  conecta();
   
    //return WiFi.status() == WL_CONNECTED;
}
bool WifiManager::conecta()
{
   #ifdef DEBUG_PS
     Serial.println("Conectando wifi");
     Serial.println(nred);
     Serial.println(pass);
     #endif
    
    WiFi.mode(WIFI_STA);
    WiFi.begin(nred, pass);

    for(int i=0; i<100 && WiFi.status() != WL_CONNECTED; i++)
    {
      delay(500);
         #ifdef DEBUG_PS
      Serial.print(".");
     #endif
    }
     #ifdef DEBUG_PS
     Serial.println("");
     if(WiFi.status() == WL_CONNECTED)
      Serial.println("Wifi Conectado");
      else
      {   
        Serial.println("No se pudo conectar");
        
      }
     #endif
     if(WiFi.status() != WL_CONNECTED)
        return false;

  //ota----------------------------------
  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
   ArduinoOTA.setHostname(idArduino);

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_FS
      type = "filesystem";
    }
    #ifdef DEBUG_PS
    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
    #endif
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    #ifdef DEBUG_PS
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    #endif
  });
  ArduinoOTA.onError([](ota_error_t error) {
    #ifdef DEBUG_PS
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
    #endif
  });
  ArduinoOTA.begin();
  #ifdef DEBUG_PS
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  #endif
  return true;
}
bool WifiManager::loop()
{
    if((WiFi.status() == WL_CONNECTED))
    {
      ArduinoOTA.handle();
      return true;
    }
    #ifdef DEBUG_PS
    Serial.println("Fallo de conexion, se reinicia arduino");
    #endif
    ESP.reset();
    return false;    
}
//**************************************************************************************************************************************************
MqttManager::MqttManager()
{
  
}

void MqttManager::inicia(PubSubClient *mqttClient,char *ideEsp, char *host, int port, MqttReceiver* classReceiver)
{
  strcpy(idEsp,ideEsp);
  Mqttlistener=classReceiver;
  client_mqtt=mqttClient;
  client_mqtt->setServer(host, port);
  client_mqtt->setCallback(MqttManager::OnMqtt);
}

bool MqttManager::loop()
{
   if(client_mqtt->loop())
    return true;
    #ifdef DEBUG_PS
     if(WiFi.status() == WL_CONNECTED)
      Serial.println("Conectando a broker");
     #endif
   if(client_mqtt->connect(idEsp))
  {
    #ifdef DEBUG_PS
     if(WiFi.status() == WL_CONNECTED)
      Serial.println("Conectado a broker");
     #endif
     subscribe_mqtt();
     return true;
     
  }
  #ifdef DEBUG_PS
  Serial.println("Fallo de conexion, se reinicia arduino");
  #endif
  ESP.reset();
  return false;
}
void MqttManager::subscribe_mqtt()
{
  if(Mqttlistener==NULL)
    return;
   Mqttlistener->SubscribeMqtt(client_mqtt);
}
//auxiliar------------------
void MqttManager::OnMqtt(char* topic, byte* payload, unsigned int length)
{
  if(Mqttlistener==NULL)
    return;
   int i;
  char buf[MAXTOPICVAR];
  i= MAXTOPICVAR-1;
  if(i>length)
    i=length;
  memcpy(buf, payload, i);
  buf[i]=0;
  Mqttlistener->OnMqtt(topic, buf);
 
}

//**************************************************************************************************************************************************
void Ceprom_manager::fin_grabacion()
{
   EEPROM.commit();
}

Ceprom_manager::Ceprom_manager()
{
  nb=0;
}

void Ceprom_manager::leeb(byte *p)
{
  *p=EEPROM.read(nb++);
}

void Ceprom_manager::grabab(byte p)
{
  EEPROM.write(nb++, p);
}

void Ceprom_manager::graba_st(char *st)
{
  for(int i=strlen(st); i>0; i--)
  {
    Ceprom_manager::grabab(*st);
    st++;
  }
}

void Ceprom_manager::cursor(int pos)
{
  nb=pos;
}
//**************************************************************************************************************************************************
MelodiaBuzzer::MelodiaBuzzer()
{
  melodia[0]=0;
}
int MelodiaBuzzer::st2nota(char st)
{
  switch(st)
  {
    case 'd':
    return 262;
    case 'r':
    return 294;
    case 'm':
    return 330;
    case 'f':
    return 349;
    case 's':
    return 392;
    case 'l':
    return 440;
    case 'i':
    return 494;
    case 'D':
    return 277;
    case 'R':
    return 311;
    case 'M':
    return 345;
    case 'F':
    return 350;
    case 'S':
    return 415;
    case 'L':
    return 466;
    case 'I':
    return 554,36;
    default:
    return 0;
  }
}
int MelodiaBuzzer::str2t(char* st, int *t)
{
  char buf[8];
  int n=0;
  int i;
  while(st[i]<='9' && st[i]>='0')
  {
    buf[n++]=st[i++];
  }
  buf[n]=0;
  (*t)=atoi(buf);
  return i;
}
void MelodiaBuzzer::Toca(int pin)
{
  int i=0;
  int h=0, t=0;
  
  while (melodia[i])
  {
    h=st2nota(melodia[i]);
    i++;
    t=0;
    i+=str2t(&melodia[i], &t);
    if(h>0)
      tone(pin, h);
    else
      noTone(pin);  
    delay(t);
  }
}
//*************************************************
SonidoBuzzer::SonidoBuzzer()
{
  n=0;
}

void SonidoBuzzer::Toca(int pin, int imel)
{
  mel[imel].Toca(pin);
}

int SonidoBuzzer::Add(char* melo)
{
  strcpy(mel[n].melodia, melo);
  return n++;
}
