/**
 * @file       TinyGsmGSMLocation.h
 * @author     Volodymyr Shymanskyy
 * @license    LGPL-3.0
 * @copyright  Copyright (c) 2016 Volodymyr Shymanskyy
 * @date       Nov 2016
 */

#ifndef SRC_TINYGSMGSMLOCATION_H_
#define SRC_TINYGSMGSMLOCATION_H_

#include "TinyGsmCommon.h"

#define TINY_GSM_MODEM_HAS_GSM_LOCATION

template <class modemType>
class TinyGsmGSMLocation {
 public:
  /*
   * GSM Location functions
   */
  String getGsmLocationRaw() {
    return thisModem().getGsmLocationRawImpl();
  }

  String getGsmLocation() {
    return thisModem().getGsmLocationRawImpl();
  }

  bool getGsmLocation(float* lat, float* lon, float* accuracy = 0,
                      int* year = 0, int* month = 0, int* day = 0,
                      int* hour = 0, int* minute = 0, int* second = 0) {
    return thisModem().getGsmLocationImpl(lat, lon, accuracy, year, month, day,
                                          hour, minute, second);
  };

  bool getGsmLocationTime(int* year, int* month, int* day, int* hour,
                          int* minute, int* second) {
    float lat      = 0;
    float lon      = 0;
    float accuracy = 0;
    return thisModem().getGsmLocation(&lat, &lon, &accuracy, year, month, day,
                                      hour, minute, second);
  }

  /*
   * CRTP Helper
   */
 protected:
  inline const modemType& thisModem() const {
    return static_cast<const modemType&>(*this);
  }
  inline modemType& thisModem() {
    return static_cast<modemType&>(*this);
  }

  /*
   * GSM Location functions
   * Template is based on SIMCOM commands
   */
 protected:
  // String getGsmLocationImpl() {
  //   thisModem().sendAT(GF("+CIPGSMLOC=1,1"));
  //   if (thisModem().waitResponse(10000L, GF("+CIPGSMLOC:")) != 1) { return
  //   ""; } String res = thisModem().stream.readStringUntil('\n');
  //   thisModem().waitResponse();
  //   res.trim();
  //   return res;
  // }

  String getGsmLocationRawImpl() {
    // AT+CLBS=<type>,<cid>
    // <type> 1 = location using 3 cell's information
    //        3 = get number of times location has been accessed
    //        4 = Get longitude latitude and date time
    thisModem().sendAT(GF("+CLBS=1,1"));
    // Should get a location code of "0" indicating success
    if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return ""; }
    int8_t locationCode = thisModem().streamGetIntLength(2);
    // 0 = success, else, error
    if (locationCode != 0) {
      thisModem().waitResponse();  // should be an ok after the error
      return "";
    }
    String res = thisModem().stream.readStringUntil('\n');
    thisModem().waitResponse();
    res.trim();
    return res;
  }

  bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
                          int* year = 0, int* month = 0, int* day = 0,
                          int* hour = 0, int* minute = 0, int* second = 0) {
    // AT+CLBS=<type>,<cid>
    // <type> 1 = location using 3 cell's information
    //        3 = get number of times location has been accessed
    //        4 = Get longitude latitude and date time
    thisModem().sendAT(GF("+CLBS=4,1"));
    // Should get a location code of "0" indicating success
    if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return false; }
    int8_t locationCode = thisModem().streamGetIntLength(2);
    // 0 = success, else, error
    if (locationCode != 0) {
      thisModem().waitResponse();  // should be an ok after the error
      return false;
    }

    // init variables
    float ilat      = 0;
    float ilon      = 0;
    float iaccuracy = 0;
    int   iyear     = 0;
    int   imonth    = 0;
    int   iday      = 0;
    int   ihour     = 0;
    int   imin      = 0;
    int   isec      = 0;

    ilat      = thisModem().streamGetFloatBefore(',');  // Latitude
    ilon      = thisModem().streamGetFloatBefore(',');  // Longitude
    iaccuracy = thisModem().streamGetIntBefore(',');    // Positioning accuracy

    // Date & Time
    iyear  = thisModem().streamGetIntBefore('/');
    imonth = thisModem().streamGetIntBefore('/');
    iday   = thisModem().streamGetIntBefore(',');
    ihour  = thisModem().streamGetIntBefore(':');
    imin   = thisModem().streamGetIntBefore(':');
    isec   = thisModem().streamGetIntBefore('\n');

    // Set pointers
    if (lat != NULL) *lat = ilat;
    if (lon != NULL) *lon = ilon;
    if (accuracy != NULL) *accuracy = iaccuracy;
    if (iyear < 2000) iyear += 2000;
    if (year != NULL) *year = iyear;
    if (month != NULL) *month = imonth;
    if (day != NULL) *day = iday;
    if (hour != NULL) *hour = ihour;
    if (minute != NULL) *minute = imin;
    if (second != NULL) *second = isec;

    // Final OK
    thisModem().waitResponse();
    return true;
  }
};

#endif  // SRC_TINYGSMGSMLOCATION_H_
