#include "Sim808Manager.h"







//--------------------------------------------------------------------------------------------------------
DataGPSSimManager::DataGPSSimManager()
{
  initValues();
}

char* DataGPSSimManager::getPrecision(char*buf)
{
  if (hdop <= 0)
    strcpy(buf, "NoValido");
  else if (hdop < 1)
    strcpy(buf, "Perfecta");
  else if (hdop < 2)
    strcpy(buf, "Excelente");
  else if (hdop < 5)
    strcpy(buf, "Buena");
  else if (hdop < 10)
    strcpy(buf, "Media");
  else if (hdop < 20)
    strcpy(buf, "Mala");
  else
    strcpy(buf, "MuyMala");
  return buf;
}
char* DataGPSSimManager::getFecha(char*buf)
{
  sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hour, minute, second);
  return buf;
}
char* DataGPSSimManager::getTimeStamp(char*buf)
{
  sprintf(buf, "%02d%02d%02d%02d", day, hour, minute, second);
  return buf;
}
void DataGPSSimManager::initValues()
{
  year = 0;
  month = 0;
  day = 0;
  hour = 0;
  minute = 0;
  second = 0;
  centisecond = 0;
  speed_kmh = 0;
  heading = 0;
  altitude = 0;
  nSatelites = 0;
  IsValid = false;
}

//--------------------------------------------------------------------------------------------------------------------
Sim808Manager::Sim808Manager()
{
  pk = -1;
  simIniciada = false;
  gpsIniciada = false;
  AjusteHora = 1;
}

bool Sim808Manager::inicia(HardwareSerial *mySerial, int powerKey = -1, int baud=0)
{
  pSeria = mySerial;
  pk = powerKey;
  if (pk >= 0)
    pinMode(pk, OUTPUT);
  if(baud)
      return checkeaConfig(baud);
  return checkea();
 /* if (!CheckAt())
    enciende();
  return CheckAt();*/
}

bool Sim808Manager::EnviaSMS(char* tlf, char *sms)
{
  Utiles::printCOM("env sms");
  if (!checkea())
    return false;
  if (!iniciaSIM())
    return false;
  //envia numero telefono----------------------
  sprintf(buffer, "AT+CMGS=\"%s\",145", tlf);
  //sprintf(buffer,"AT+CMGS=\"%s\"",tlf);
  int kk = strlen(buffer);
  envia(buffer);
  char * res = recibe();
  if (!strstr(res, ">"))
  {
    Utiles::printCOM(res);
    return false;
  }
  //envia sms---------------------------------
  kk = strlen(sms);

  enviaSolo(sms, kk);

  buffer[0] = (char)26; //0x1A;//(ctrl-z)
  buffer[1] = 0;
  enviaSolo(buffer, 1);
  for (int i = 0; i < 70; i++)
  {
    res = recibe();
    if (strstr(res, "ERROR"))
    {
      Utiles::printCOM(res);
      buffer[0] = (char)0x1B; //enviamos Esc
      buffer[1] = 0;
      enviaSolo(buffer, 1);
      recibe();
      return false;
    }
    if (strstr(res, "OK"))
      return true;
    delay(1000);
  }
  Utiles::printCOM("fin espera env sms");
  return false;

}

DataGPSSimManager* Sim808Manager::GetGPSValid()
{
  GetGPS();
  while (!dataGPS.IsValid)
  {

    delay(1000);
    GetGPS();
  }
  return &dataGPS;
}

char* Sim808Manager::GetFecha(char *buf)
{
  GetGPSValid();
  return dataGPS.getFecha(buf);
}
DataGPSSimManager* Sim808Manager::GetUltGPS()
{
  return &dataGPS;
}

DataGPSSimManager* Sim808Manager::GetGPS()
{
  dataGPS.initValues();
  if (!checkea())
    return &dataGPS;
  if (!iniciaGPS())
    return &dataGPS;
  if (!recibeGPRMC())
    Utiles::printCOM("GPRMC no valido");
  if (!recibeCGPSINF_0())
    Utiles::printCOM("info 0 no valido");
  if (!recibeGPGSA())
  {
    Utiles::printCOM("GPGSA no valida");
  }
  return &dataGPS;
}

//funciones auxiliares----------------------

bool Sim808Manager::iniciaGPS()
{
  if (gpsIniciada)
    return true;
  envia("AT+CGPSPWR=1");
  if (!reciveOK())
    return false;
  envia("AT+CGPSRST=1");
  if (!reciveOK())
    return false;
  gpsIniciada = true;
  return true;
}
bool Sim808Manager::iniciaSIM()
{

  if (simIniciada)
    return true;
  Utiles::printCOM("inicia sim");
  //verifica que no este configurado el pin
  envia("AT+CPIN?");
  char* res = recibe();
  if (!strstr(res, "+CPIN: READY"))
  {
    Utiles::printCOM("Sim Necesita Pin");
    return false;
  }
  //configura sms en modo texto
  envia("AT+CMGF=1");
  if (!reciveOK())
    return false;
  simIniciada = true;

  return true;
}
char* Sim808Manager::recibeDatos()
{
    return recibe();
}
char* Sim808Manager::recibe()
{
  unsigned long start = millis();
  char d;
  char *buff = buffer;
  char *buf = buffer;
  int len = 0;
  while (millis() - start < 100)
  {
    while (pSeria->available() > 0 && len< (LEN_BUFFER_DATA_SIMMANAGER-3))
    {
      start = millis();
      d = pSeria->read();
      *buf = d;
      buf++;
      len++;
    }
  }
  *buf = 0;


  //log sim
    /*
    if(*buff)
    Utiles::printCOM(buff);
    */
  return buff;
}

void Sim808Manager::envia(char* dat)
{
  pSeria->println(dat);
  //Utiles::printCOM(dat);
}

void Sim808Manager::enviaSolo(char* dat, int  ndata)
{
  for ( int i = 0; i < ndata; i++)
  {
    pSeria->write(dat[i]);
  }
  //Utiles::printCOM(dat);
}
bool Sim808Manager::CheckAt()
{

  envia("AT");
  bool res = reciveOK();
  return res;
}
void Sim808Manager::enciende()
{
  if (pk < 0)
    return;
  digitalWrite(pk, HIGH);
  delay (2000);
  digitalWrite (pk, LOW);
  delay (2000);
}
bool Sim808Manager::reciveOK()
{
  char* resp = recibe();
  if (strstr(resp, "OK"))
    return true;
  return false;
}
//traza nmea gprmc
bool Sim808Manager::recibeGPRMC()
{
  bool datoValido = true;
  char buf[16];
  envia("AT+CGPSINF=32");
  char *res = recibe();
  if (!strstr(res, "+CGPSINF:"))
  {
    Utiles::printCOM("info no valido\n");
    return false;
  }
  StringSplit sp(res, ",");
  sp.get();
  char* res2 = sp.get();
  if (!res2 || strlen(res2) < 6)
  {
    Utiles::printCOM("longitud hora\n");
    return false;
  }
  int i = 0;
  //HORA----------------
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.hour = atoi(buf) + AjusteHora;
  //MIN---------------
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.minute = atoi(buf);
  //SEG---------------
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.second = atoi(buf);
  //dato valido-------
  res2 = sp.get();
  if (!res2 || strlen(res2) < 1)
  {
    Utiles::printCOM("longitud valido\n");
    return false;
  }
  if (!strstr(res2, "A"))
  {
    //Utiles::printCOM("Dato no valido\n");
    datoValido = false;
  }

  //latitud----------
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 5)
  {
    return false;
  }
  //grados
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.lat = atof(buf) + (atof(&res2[i]) / 60.);
  //hemisferio
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
  {
    return false;
  }
  if (!strstr(res2, "N"))
    dataGPS.lat = -dataGPS.lat;

  //longitud----------
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 5)
  {
    return false;
  }
  //grados
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = res2[i++];
  buf[3] = 0;
  dataGPS.lon = atof(buf) + (atof(&res2[i]) / 60.);
  //Signo
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  if (res2[0] != 'E')
    dataGPS.lon = -dataGPS.lon;

  //velocidad---------
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.speed_kmh = atof(res2) * 1.852;
  //direccion--------
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.heading = atof(res2);
  //fecha------------
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 6)
    return false;
  //dia
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.day = atoi(buf);
  //mes
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.month = atoi(buf);
  //anio
  buf[0] = res2[i++];
  buf[1] = res2[i++];
  buf[2] = 0;
  dataGPS.year = 2000 + atoi(buf);

  dataGPS.IsValid = datoValido;
  return true;

}
bool Sim808Manager::recibeCGPSINF_0()
{
  char buf[16];
  envia("AT+CGPSINF=0");
  char *res = recibe();
  if (!strstr(res, "+CGPSINF:"))
  {
    return false;
  }
  int i = 0;
  StringSplit sp(res, ",");
  sp.get();//cabecera
  sp.get();//longitud
  sp.get();//latidud
  //altura
  char* res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.altitude = atof(res2);
  sp.get();//tiempo
  sp.get();//TTF

  //numero de satelites
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.nSatelites = atoi(res2);

  return true;
}
bool Sim808Manager::recibeGPGSA()
{
  envia("AT+CGPSINF=8");
  char *res = recibe();
  if (!strstr(res, "+CGPSINF:"))
  {
    return false;
  }
  int i = 0;
  StringSplit sp(res, ",");
  //pasa datos hasta
  for (i = 0; i < 15; i++)
    sp.get();//cabecera
  //precision posicion
  char* res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.pdop = atof(res2);
  //precision horizontal
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.hdop = atof(res2);
  //precision vertical
  res2 = sp.get();
  i = 0;
  if (!res2 || strlen(res2) < 1)
    return false;
  dataGPS.vdop = atof(res2);

  return true;
}

bool Sim808Manager::checkeaConfig(int baud)
{
  if (Sim808Manager::configBaud(baud))
    return true;
  Utiles::printCOM("reinicia modulo sim");
  gpsIniciada = false;
  simIniciada = false;
  enciende();

  return Sim808Manager::configBaud(baud);

}

bool Sim808Manager::checkea()
{
  if (!CheckAt())
  {
    Utiles::printCOM("reinicia modulo sim");
    gpsIniciada = false;
    simIniciada = false;
    enciende();
  }
  else
    return true;
  return CheckAt();

}

bool Sim808Manager::configBaud(int baud)
{
  const uint32_t baudSIM808[] = {
  300,
  600,
  1200,
  2400,
  4800,
  9600,
  19200,
  38400,
  57600,
  74880,
  115200
};


  Utiles::printCOM("configBaud");
  pSeria->begin(baud);
  Utiles::printCOM("configurado p serial");
    //envia("AT+GMR");
    //reciveOK();     
  if (CheckAt())
    return true;
    Utiles::printCOM("se intenta configurar baud");
  char auxbuf[24];
 
  for (int i = 0; i < 11; i++)
  {
    
    uint32_t baudTest = baudSIM808[i];
    sprintf(auxbuf, "prueba: %u", baudTest );
    Utiles::printCOM(auxbuf);
    pSeria->begin(baudTest);
    if (CheckAt())
    {
      Utiles::printCOM("Conseguido");
      sprintf(auxbuf, "AT+IPR=%u", baud );
      envia(auxbuf);
      if (reciveOK())
        break;
    }
    Utiles::printCOM("fail");
  }
  pSeria->begin(baud);
  return CheckAt();

}
char* Sim808Manager::EnviaComando(char* at_command)
{
   envia(at_command);
   return recibe();
}
bool Sim808Manager::IniciaNetwork(const char* apn = 0, const char* userName = 0, const char* passWord = 0)
{
    if(!iniciaSIM())
      return false;

    char* res;


    

    
    envia("AT+CGATT?");
    if (!reciveOK())
            return false;
            
    envia("AT+CGATT=1");
    if (!reciveOK())
        return false;
    
  if(apn)
  {
    strcpy(buffer, "AT+SAPBR=3,1,\"APN\",\"");
    enviaSolo(buffer, strlen(buffer));
    enviaSolo(apn, strlen(apn));
    envia("\"");
  }
  else
   envia("AT+SAPBR=3,1,\"APN\",\"\"");
 
  if (!reciveOK())
        return false;

  if(userName)
  {
    strcpy(buffer, "AT+SAPBR=3,1,\"USER\",\"");
    enviaSolo(buffer, strlen(buffer));
    enviaSolo(userName, strlen(userName));
    envia("\"");
  }
  else
   envia("AT+SAPBR=3,1,\"USER\",\"\"");
 
  if (!reciveOK())
        return false;
  if(passWord)
  {
    strcpy(buffer, "AT+SAPBR=3,1,\"PWD\",\"");
    enviaSolo(buffer, strlen(buffer));
    enviaSolo(passWord, strlen(passWord));
    envia("\"");
  }
  else
   envia("AT+SAPBR=3,1,\"PWD\",\"\"");
 
  if (!reciveOK())
        return false;


        
  envia("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\""); 
  if (!reciveOK())
        return false;
  envia("AT+SAPBR=1,1"); 
  reciveOK();

  for(int i=0; i<10; i++)
  {
     envia("AT+SAPBR=2,1"); 
      res=recibe();
      if(res && !strstr(res,"0.0.0.0") && strstr(res,"OK"))
      {
        //envia("AT+CIPMUX=0"); 
        return true;
      }
     delay(1000);
  }
  return false;
}

uint32_t Sim808Manager::str_to_ip(const char* str)
{
    uint32_t ip = 0;
    char* p = (char*)str;
    for (int i = 0; i < 4; i++) {
        ip |= atoi(p);
        p = strchr(p, '.');
        if (p == NULL) {
            break;
        }
        ip <<= 8;
        p++;
    }
    return ip;
}
bool Sim808Manager::connect(ProtocolSimManager ptl, const char* host, int port)
{
    //char cmd[64];
    char num[4];
    char *res;

    //sim808_clean_buffer(cmd,64);
    if (ptl == SIM808_TCP) 
        strcpy(buffer, "AT+CIPSTART=\"TCP\",\"");
    else if (ptl == SIM808_UDP) 
        strcpy(buffer, "AT+CIPSTART=\"UDP\",\"");
    else
        return false;

    enviaSolo(buffer, strlen(buffer));
    enviaSolo(host, strlen(host));
    enviaSolo("\",",2);
    itoa(port, num, 10);
    envia(num);

    for (int i = 0; i < 70; i++)
    {
        res = recibe();
        if (strstr(res, "ERROR"))
        {
            Utiles::printCOM(res);
            buffer[0] = (char)0x1B; //enviamos Esc
            buffer[1] = 0;
            enviaSolo(buffer, 1);
            recibe();
            return false;
        }
        if (strstr(res, "CONNECT"))
            return true;
        delay(1000);
    }
    return false;
}

/*
 *  Serial.println(sim808.EnviaComando("AT+HTTPINIT"));
     Serial.println(sim808.EnviaComando("AT+HTTPPARA=\"CID\",1"));
   Serial.println(sim808.EnviaComando("AT+HTTPPARA=\"URL\",\"desa.narvaling.com:6265/setcoor/holaArdu\""));
   Serial.println(sim808.EnviaComando("AT+HTTPACTION=0"));
   Serial.println(sim808.EnviaComando("AT+HTTPREAD"));
 */
  bool Sim808Manager::httpGet(char* uri)
  {
      envia("AT+HTTPINIT"); 
      if (!reciveOK())
      {
          envia("AT+HTTPTERM");
          reciveOK();
          return false;
      }
      envia("AT+HTTPPARA=\"CID\",1");
      if (!reciveOK())
      {
          envia("AT+HTTPTERM");
          reciveOK();
          return false;
      }
     strcpy(buffer, "AT+HTTPPARA=\"URL\",\"");
     enviaSolo(buffer, strlen(buffer));
     enviaSolo(uri, strlen(uri));
     envia("\""); 
     if (!reciveOK())
     {
         envia("AT+HTTPTERM");
         reciveOK();
         return false;
     }
     envia("AT+HTTPACTION=0");
     bool reciv = 0;
     for (int i = 0; i < 120 && !reciv; i++)
     {
         char* res = recibe();
         if (res)
         {
             if (strstr(res, "ERROR"))
             {
                 Utiles::printCOM("error");
                 Utiles::printCOM(res);
                 buffer[0] = (char)0x1B; //enviamos Esc
                 buffer[1] = 0;
                 enviaSolo(buffer, 1);
                 recibe();
                 envia("AT+HTTPTERM");
                 reciveOK();
                 return false;
             }
             else if (strstr(res, "+HTTPACTION:"))
             {
                 reciv = 1;
             }
         }
         if(!reciv)
            delay(1000);
     }
     if (!reciv)
         return false;
     envia("AT+HTTPREAD");
     if (!reciveOK())
     {
         envia("AT+HTTPTERM");
         reciveOK();
         return false;
     }
    
     envia("AT+HTTPTERM");
     if (!reciveOK())
         return false;
     return true;
  }
    bool Sim808Manager::send(char* strbuf, int len)
    {
      Utiles::printCOM(strbuf);
       envia("AT+CIPQSEND=0"); 
        if(!reciveOK())
          return false;
        if (len <= 0)
            return false;
        strcpy(buffer, "AT+CIPSEND=");
        enviaSolo(buffer, strlen(buffer));
        itoa(len, buffer, 12);
        envia(buffer);
        char* res = recibe();
        if (!res || !strstr(res, ">")) { 
            return false;
        }
        
        enviaSolo(strbuf, len);
        buffer[0] = (char)26; //0x1A;//(ctrl-z)
        buffer[1] = 0;
        enviaSolo(buffer, strlen(buffer));
  
        for (int i = 0; i < 70; i++)  
        {
            res = recibe();
            if(res)
            {
              if (strstr(res, "ERROR"))
              {
                 Utiles::printCOM("error");
                Utiles::printCOM(res);
                buffer[0] = (char)0x1B; //enviamos Esc
                buffer[1] = 0;
                enviaSolo(buffer, 1);
                recibe();
                return false;
              }
              else if (strstr(res, "OK"))
               { 
                return true;
               }
             }
            
            delay(10000);
        }
        Utiles::printCOM("Error al enviar datos, envio sale por tiempo");
        return false;
    }
    bool Sim808Manager::ISConectado(void)
    {
        char resp[96];
        envia("AT+CIPSTATUS");
        char* res = recibe();
        if (res &&  strstr(res, "CONNECT OK")) {
            //+CIPSTATUS: 1,0,"TCP","216.52.233.120","80","CONNECTED"
            return true;
        }
        else {
            return false;
        }
    }

    bool Sim808Manager::Cierra()
    {
        // if not connected, return
        if (!ISConectado()) {
            return true;
        }

        envia("AT+CIPCLOSE");
        char* res = recibe();
        if (!res && NULL != strstr(res, "CLOSE OK")) {
            //+CIPSTATUS: 1,0,"TCP","216.52.233.120","80","CONNECTED"
            return true;
        }
        else {
            return false;
        }
    }

    void Sim808Manager::Desconecta()
    {
        Cierra();
        envia("AT+CIPSHUT");
        recibe();
    }
