// ublox gps code - big thanks to sparkfun 
// https://github.com/sparkfun/SparkFun_u-blox_SAM-M10Q
// needed to add extra delays to work with attiny3216

#include <Wire.h>
#define UBLOX_ADDR 0x42

const uint8_t UBX_SYNCH_1   = 0xB5;
const uint8_t UBX_SYNCH_2   = 0x62;
const uint8_t UBX_CLASS_NAV = 0x01;
const uint8_t UBX_NAV_PVT   = 0x07;

typedef struct {
  uint8_t  fixType;
  uint8_t  numSV;
  int32_t  lon;
  int32_t  lat;
  uint32_t unixTime;
  bool     dataReceived;
} GPSData_t;

extern GPSData_t gpsData;

bool gpsFound() {
  Wire.beginTransmission(UBLOX_ADDR);
  uint8_t error = Wire.endTransmission();
  delay(20);
  return (error == 0);
}

uint16_t getBytesAvailable() {
  Wire.beginTransmission(UBLOX_ADDR);
  Wire.write(0xFD);
  uint8_t err = Wire.endTransmission(false);
  if (err != 0) {
    Wire.endTransmission();
    delay(10);
    return 0;
  }

  delay(5);
  uint8_t bytesRead = Wire.requestFrom(UBLOX_ADDR, (uint8_t)2, (uint8_t)1);
  delay(3);

  if (bytesRead < 2) return 0;

  uint8_t msb = Wire.read();
  uint8_t lsb = Wire.read();
  delay(2);

  return ((uint16_t)msb << 8) | lsb;
}

uint16_t readBytesChunk(uint8_t *buffer, uint16_t length) {
  if (length > 32) length = 32;

  Wire.beginTransmission(UBLOX_ADDR);
  Wire.write(0xFF);
  uint8_t err = Wire.endTransmission(false);
  if (err != 0) {
    Wire.endTransmission();
    delay(10);
    return 0;
  }

  delay(5);
  uint8_t bytesRead = Wire.requestFrom(UBLOX_ADDR, (uint8_t)length, (uint8_t)1);
  delay(3);

  for (uint8_t i = 0; i < bytesRead; i++) {
    if (Wire.available()) buffer[i] = Wire.read();
  }

  delay(2);
  return bytesRead;
}

// Convert date/time to Unix timestamp (UTC)
uint32_t dateTimeToUnix(uint16_t year, uint8_t month, uint8_t day,
                        uint8_t hour, uint8_t min, uint8_t sec)
{
  const uint8_t daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  uint32_t days = 0;

  // Days from 1970 to current year
  for (uint16_t y = 1970; y < year; y++) {
    days += 365;
    if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) {
      days++; // leap year
    }
  }

  // Days from months this year
  for (uint8_t m = 1; m < month; m++) {
    days += daysInMonth[m - 1];
    if (m == 2) { // February leap day
      if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
        days++;
      }
    }
  }

  days += (day - 1);

  return days * 86400UL + hour * 3600UL + min * 60UL + sec;
}



bool readUBXMessage() {
  uint16_t available = getBytesAvailable();
  if (available < 8) return false;

  uint8_t buffer[120];
  uint16_t bufferIndex = 0;

  while (available > 0 && bufferIndex < 100) {
    uint16_t toRead = (available > 32) ? 32 : available;
    uint16_t read = readBytesChunk(&buffer[bufferIndex], toRead);
    if (read == 0) break;
    bufferIndex += read;
    available -= read;
    delay(10);
  }

  if (bufferIndex < 10) return false;

  int syncPos = -1;
  for (int i = 0; i < bufferIndex - 1; i++) {
    if (buffer[i] == UBX_SYNCH_1 && buffer[i+1] == UBX_SYNCH_2) {
      syncPos = i;
      break;
    }
  }

  if (syncPos < 0 || syncPos + 8 > bufferIndex) return false;

  uint8_t msgClass   = buffer[syncPos + 2];
  uint8_t msgID      = buffer[syncPos + 3];
  uint16_t payloadLen = buffer[syncPos + 4] | ((uint16_t)buffer[syncPos + 5] << 8);

  if (payloadLen > 100 || syncPos + 8 + payloadLen > bufferIndex) return false;

  uint8_t checksumA = 0, checksumB = 0;
  for (int i = 2; i < 6 + payloadLen; i++) {
    checksumA += buffer[syncPos + i];
    checksumB += checksumA;
  }

  uint8_t ckA = buffer[syncPos + 6 + payloadLen];
  uint8_t ckB = buffer[syncPos + 7 + payloadLen];
  if (checksumA != ckA || checksumB != ckB) return false;

  // NAV-PVT message
  if (msgClass == UBX_CLASS_NAV && msgID == UBX_NAV_PVT && payloadLen == 92) {
    int p = syncPos + 6;

    uint16_t year  = buffer[p + 4] | ((uint16_t)buffer[p + 5] << 8);
    uint8_t  month = buffer[p + 6];
    uint8_t  day   = buffer[p + 7];
    uint8_t  hour  = buffer[p + 8];
    uint8_t  min   = buffer[p + 9];
    uint8_t  sec   = buffer[p + 10];
    gpsData.unixTime = dateTimeToUnix(year, month, day, hour, min, sec);

    gpsData.fixType = buffer[p + 20];
    gpsData.numSV   = buffer[p + 23];

    gpsData.lon = (int32_t)(
      buffer[p + 24] |
      ((uint32_t)buffer[p + 25] << 8) |
      ((uint32_t)buffer[p + 26] << 16) |
      ((uint32_t)buffer[p + 27] << 24));

    gpsData.lat = (int32_t)(
      buffer[p + 28] |
      ((uint32_t)buffer[p + 29] << 8) |
      ((uint32_t)buffer[p + 30] << 16) |
      ((uint32_t)buffer[p + 31] << 24));

    gpsData.dataReceived = true;
    return true;
  }

  return false;
}

// static uint8_t ubxBuffer[120];
// bool readUBXMessage() {
//   uint16_t available = getBytesAvailable();
//   if (available < 8) return false;

//   // uint8_t ubxBuffer[120];
//   uint16_t bufferIndex = 0;

//   // while (available > 0 && bufferIndex < 100) {
//   while (available > 0 && bufferIndex < sizeof(ubxBuffer)) {
//     uint16_t toRead = (available > 32) ? 32 : available;
//     uint16_t read = readBytesChunk(&ubxBuffer[bufferIndex], toRead);
//     if (read == 0) break;
//     bufferIndex += read;
//     available -= read;
//     delay(10);
//   }

//   if (bufferIndex < 10) return false;

//   int syncPos = -1;
//   for (int i = 0; i < bufferIndex - 1; i++) {
//     if (ubxBuffer[i] == UBX_SYNCH_1 && ubxBuffer[i+1] == UBX_SYNCH_2) {
//       syncPos = i;
//       break;
//     }
//   }

//   if (syncPos < 0 || syncPos + 8 > bufferIndex) return false;

//   uint8_t msgClass   = ubxBuffer[syncPos + 2];
//   uint8_t msgID      = ubxBuffer[syncPos + 3];
//   uint16_t payloadLen = ubxBuffer[syncPos + 4] | ((uint16_t)ubxBuffer[syncPos + 5] << 8);

//   if (payloadLen > 100 || syncPos + 8 + payloadLen > bufferIndex) return false;

//   uint8_t checksumA = 0, checksumB = 0;
//   for (int i = 2; i < 6 + payloadLen; i++) {
//     checksumA += ubxBuffer[syncPos + i];
//     checksumB += checksumA;
//   }

//   uint8_t ckA = ubxBuffer[syncPos + 6 + payloadLen];
//   uint8_t ckB = ubxBuffer[syncPos + 7 + payloadLen];
//   if (checksumA != ckA || checksumB != ckB) return false;

//   // NAV-PVT message
//   if (msgClass == UBX_CLASS_NAV && msgID == UBX_NAV_PVT && payloadLen == 92) {
//     int p = syncPos + 6;

//     uint16_t year  = ubxBuffer[p + 4] | ((uint16_t)ubxBuffer[p + 5] << 8);
//     uint8_t  month = ubxBuffer[p + 6];
//     uint8_t  day   = ubxBuffer[p + 7];
//     uint8_t  hour  = ubxBuffer[p + 8];
//     uint8_t  min   = ubxBuffer[p + 9];
//     uint8_t  sec   = ubxBuffer[p + 10];
//     gpsData.unixTime = dateTimeToUnix(year, month, day, hour, min, sec);

//     gpsData.fixType = ubxBuffer[p + 20];
//     gpsData.numSV   = ubxBuffer[p + 23];

//     gpsData.lon = (int32_t)(
//       ubxBuffer[p + 24] |
//       ((uint32_t)ubxBuffer[p + 25] << 8) |
//       ((uint32_t)ubxBuffer[p + 26] << 16) |
//       ((uint32_t)ubxBuffer[p + 27] << 24));

//     gpsData.lat = (int32_t)(
//       ubxBuffer[p + 28] |
//       ((uint32_t)ubxBuffer[p + 29] << 8) |
//       ((uint32_t)ubxBuffer[p + 30] << 16) |
//       ((uint32_t)ubxBuffer[p + 31] << 24));

//     gpsData.dataReceived = true;
//     return true;
//   }

//   return false;
// }

bool configureNAVPVT() {
  delay(200);
  
  uint8_t cfg[] = {
    0xB5, 0x62,  0x06, 0x01,  0x08, 0x00,
    0x01, 0x07,  0x01, 0x00,  0x00, 0x00,
    0x00, 0x00,  0x00, 0x00
  };
  
  uint8_t ckA = 0, ckB = 0;
  for (int i = 2; i < 14; i++) {
    ckA += cfg[i];
    ckB += ckA;
  }
  cfg[14] = ckA;
  cfg[15] = ckB;
  
  Wire.beginTransmission(UBLOX_ADDR);
  Wire.write(0xFF);
  for (int i = 0; i < 16; i++) {
    Wire.write(cfg[i]);
  }
  uint8_t result = Wire.endTransmission();
  
  delay(300);
  return (result == 0);
}