      const uint32_t baudSIM808[] = {   
                       300,
                       600,
                      1200,
                      2400,
                      4800,
                      9600,
                     19200,
                     38400,
                     57600,
                     74880,
                    115200
                  };
class DataGPSSimManager
{                
  public:
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t centisecond;
    float lat;
    float lon;
    float speed_kmh;
    float heading;
    float altitude;
    uint8_t nSatelites;
    bool IsValid;
    //precision------------
    float pdop;//precision posicion  (position Dilution of precision)
    float hdop;//precision horizontal
    float vdop;//precision vertical 
    DataGPSSimManager()
    {
      initValues();
    }

    char* 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* getFecha(char*buf)
    {
      sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hour, minute, second);
      return buf;
    }
    char* getTimeStamp(char*buf)
    {
      sprintf(buf, "%02d%02d%02d%02d", day, hour, minute, second);
      return buf;
    }
    void 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;
    }
};

class Sim808Manager
{
    char buffer[256];
    bool simIniciada;
    bool gpsIniciada;
    int pk;
    DataGPSSimManager dataGPS;
    HardwareSerial *pSeria;
    int AjusteHora;
  public:
    Sim808Manager()
    {
      pk = -1;
      simIniciada = false;
      gpsIniciada = false;
      AjusteHora = 1;
    }

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

    bool EnviaSMS(char* tlf, char *sms)
    {
      printCOM("env sms");
      if(!checkea())
        return false;
        printCOM("mira si inicia");
      if (!iniciaSIM())
        return false;
        printCOM("proce envia");
      //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, ">"))
       {
        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"))
        {
          printCOM(res);
          buffer[0] = (char)0x1B; //enviamos Esc
          buffer[1] = 0;
          enviaSolo(buffer, 1);
          recibe();
          return false;
        }
        if (strstr(res, "OK"))
         {
          printCOM(res);
          return true;
         }
        delay(1000);
      }
      printCOM("fin espera env sms");
      return false;

    }

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

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

    char* GetFecha(char *buf)
    {
      GetGPSValid();
      return dataGPS.getFecha(buf);
    }
    DataGPSSimManager* GetUltGPS()
    {
      return &dataGPS;
    }
    
    DataGPSSimManager* GetGPS()
    {
      printCOM("GetGPS");
      if(!checkea())
        return &dataGPS;
       printCOM("PassCheck");
      if(!iniciaGPS())
        return &dataGPS;
      printCOM("PassInit");
      if (!recibeGPRMC())
        printCOM("GPRMC no valido");
      if (!recibeCGPSINF_0())
          printCOM("info 0 no valido");
      if(!recibeGPGSA())
      {
          printCOM("GPGSA no valida");
      }
      return &dataGPS;
    }

    //funciones auxiliares

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

    char* recibe()
    {
      unsigned long start = millis();
      char d;
      char *buff = buffer;
      char *buf = buffer;
      while (millis() - start < 100)
      {
        while (pSeria->available() > 0)
        {
          start = millis();
          d = pSeria->read();
          *buf = d;
          buf++;
        }
      }
      *buf = 0;
      if(*buff)
        printCOM(buff);
      
      return buff;
    }

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

    void enviaSolo(char* dat, int  ndata)
    {
      for ( int i = 0; i < ndata; i++)
      {
        pSeria->write(dat[i]);
      }
      //printCOM(dat);
    }
    bool CheckAt()
    {
      envia("AT");
      return reciveOK();
    }
    void enciende()
    {
      if (pk < 0)
        return;
      digitalWrite(pk, HIGH);
      delay (2000);
      digitalWrite (pk, LOW);
      delay (2000);
    }
    bool reciveOK()
    {
      char* resp = recibe();
      if (strstr(resp, "OK"))
        return true;
      return false;
    }
    //traza nmea gprmc
    bool recibeGPRMC()
    {
      dataGPS.initValues();
      bool datoValido=true;
      char buf[16];
      envia("AT+CGPSINF=32");
      char *res = recibe();
      if (!strstr(res, "+CGPSINF:"))
      {
        printCOM("info no valido\n");
        return false;
      }
      StringSplit sp(res, ",");
      sp.get();
      char* res2 = sp.get();
      if (!res2 || strlen(res2) < 6)
      {
        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)
      {
        printCOM("longitud valido\n");
        return false;
      }
      if (!strstr(res2, "A"))
      {
        //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 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 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 checkea()
    {
       if (!CheckAt())
          {
            printCOM("Reinicia modulo Sim");
            gpsIniciada=false;
            simIniciada=false;
            enciende();
          }
          else
           return true;
       return CheckAt();

    }
    
    bool configBaud(int baud)
    {
      pSeria->begin(baud);
      if (checkea())
        return true;
      char auxbuf[16];
      sprintf(auxbuf,"AT+IPR=%d", baud );
      for(int i=0; i<=11; i++)
      {
        int baudTest=baudSIM808[i];
        pSeria->begin(baudTest);
        if (checkea())
        {
            envia(auxbuf);
            if(reciveOK())
              break;
        }
      }
      pSeria->begin(baud);
      return checkea();
      
    }
};
