#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <lmic.h>
#include <hal/hal.h>

uint8_t coords[9];

static const int RXPin = 0, TXPin = 1; // TXPin not used / dummy
static const uint32_t GPSBaud = 9600;
// The TinyGPS++ object
TinyGPSPlus gps;
// The serial connection to the GPS device

SoftwareSerial ss(RXPin, TXPin);
uint32_t timer = millis();

// LoraWAN Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
// https://github.com/matthijskooijman/arduino-lmic
// -------- LoRa PinMapping FeatherWing Octopus

// THIS WORKS ONLY with the two additional diodes see wiring diagram
const lmic_pinmap lmic_pins = {  
  .nss = 2,                            // Connected to pin D
  .rxtx = LMIC_UNUSED_PIN,             // For placeholder only, Do not connected on RFM92/RFM95
  .rst = LMIC_UNUSED_PIN,              // Needed on RFM92/RFM95? (probably not) D0/GPIO16 
  .dio = {
    15, 15, LMIC_UNUSED_PIN         }
};

// replace with YOUR DEVEUI here use LSB and copy hex to replace 0x00
static const u1_t PROGMEM DEVEUI[8]={
  0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void os_getDevEui (u1_t* buf) { 
  memcpy_P(buf, DEVEUI, 8);
}

// replace with YOUR APPEUI here use LSB and copy hex to replace 0x00
static const u1_t PROGMEM APPEUI[8]={
  0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00 };
void os_getArtEui (u1_t* buf) { 
  memcpy_P(buf, APPEUI, 8);
}

// replace with YOUR APPKEY here use MSB and copy hex to replace 0x00
static const u1_t PROGMEM APPKEY[16]={
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
void os_getDevKey (u1_t* buf) {  
  memcpy_P(buf, APPKEY, 16);
};

volatile int LoRaWAN_Tx_Ready      = 0; // Merker für ACK 

int LoRaWAN_Rx_Payload = 0 ;
// -------- LoRa Event 
void onEvent (ev_t ev) { 
  Serial.print(os_getTime());
  Serial.print(": ");
  switch(ev) {
  case EV_SCAN_TIMEOUT:
    Serial.println(F("EV_SCAN_TIMEOUT"));
    break;
  case EV_BEACON_FOUND:
    Serial.println(F("EV_BEACON_FOUND"));
    break;
  case EV_BEACON_MISSED:
    Serial.println(F("EV_BEACON_MISSED"));
    break;
  case EV_BEACON_TRACKED:
    Serial.println(F("EV_BEACON_TRACKED"));
    break;
  case EV_JOINING:
    Serial.println(F("EV_JOINING"));
    break;
  case EV_JOINED:
    Serial.println(F("EV_JOINED"));
    // Disable link check validation (automatically enabled
    // during join, but not supported by TTN at this time).
    LMIC_setLinkCheckMode(0);
    break;
  case EV_RFU1:
    Serial.println(F("EV_RFU1"));
    break;
  case EV_JOIN_FAILED:
    Serial.println(F("EV_JOIN_FAILED"));
    break;
  case EV_REJOIN_FAILED:
    Serial.println(F("EV_REJOIN_FAILED"));
    break;
    break;
  case EV_TXCOMPLETE:
    Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
    if (LMIC.txrxFlags & TXRX_ACK)
      Serial.println(F("Received ack"));
    if (LMIC.dataLen) {
      Serial.println(F("Received "));
      Serial.println(LMIC.dataLen);
      Serial.println(F(" bytes of payload"));
      LoRaWAN_Rx_Payload = 0; 
      for (int i = 0;i<LMIC.dataLen;i++) { 
        Serial.println(LMIC.frame[i+ LMIC.dataBeg],HEX);
        LoRaWAN_Rx_Payload = 256*LoRaWAN_Rx_Payload+LMIC.frame[i+ LMIC.dataBeg];
      }
    }
    LoRaWAN_Tx_Ready = 1;
    // Schedule next transmission
    //os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
    break;
  case EV_LOST_TSYNC:
    Serial.println(F("EV_LOST_TSYNC"));
    break;
  case EV_RESET:
    Serial.println(F("EV_RESET"));
    break;
  case EV_RXCOMPLETE:
    // data received in ping slot
    Serial.println(F("EV_RXCOMPLETE"));
    break;
  case EV_LINK_DEAD:
    Serial.println(F("EV_LINK_DEAD"));
    break;
  case EV_LINK_ALIVE:
    Serial.println(F("EV_LINK_ALIVE"));
    break;
  default:
    Serial.println(F("Unknown event"));
    break;
  }
}

void setup(){ // Einmalige Initialisierung
  Serial.begin(115200);
  ss.begin(GPSBaud);
  
  // -- Initialisiere LoraWAN 
  os_init();             // LMIC LoraWAN
  LMIC_reset();          // Reset the MAC state 
  LMIC.txpow = 27;       // Maximum TX power 
  LMIC.datarate=DR_SF12; // Long Range
  LMIC.rps = updr2rps(LMIC.datarate);

}

void loop() { // Kontinuierliche Wiederholung 
  
  while (ss.available() > 0)
  
  if (gps.encode(ss.read()))
  {

    if (gps.location.isValid()) {

    Serial.print(gps.altitude.meters(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
    Serial.print(F(","));
    Serial.println(gps.location.isValid(), 6);
        
    
    if (LMIC.opmode & (1 << 7)) { 
      Serial.println(F("OP_TXRXPEND, not sending"));
    } 
    else {
      // Prepare upstream data transmission at the next possible time.
      LoRaWAN_Tx_Ready = 0;    
      int32_t lati = gps.location.lat() * 10000;
      int32_t lon = gps.location.lng() * 10000;
      int32_t altitudeGPS = gps.altitude.meters(); // int16_t
      int8_t hdopGPS = gps.hdop.value(); 

      Serial.println(lati);
      Serial.println(lon);
      Serial.println(altitudeGPS);
      Serial.println(hdopGPS);

      // Pad 2 int32_t to 6 8uint_t, big endian (24 bit each, having 11 meter precision)
      coords[0] = lati;
      coords[1] = lati >> 8;
      coords[2] = lati >> 16;

      coords[3] = lon;
      coords[4] = lon >> 8;
      coords[5] = lon >> 16;

      coords[6] = altitudeGPS;
      coords[7] = altitudeGPS >> 8;

      coords[8] = hdopGPS;
   
      LMIC_setTxData2(1, (uint8_t*) coords, sizeof(coords), 0);
   
      Serial.println(F("Packet queued"));
      while(LoRaWAN_Tx_Ready==0) {
        yield();
        os_runloop_once();
      };  // Warte bis gesendet
    //}
    // Blockende
      Serial.print(F("Waiting "));

      for (int i = 0;i<120;i++) { 
        Serial.print(F("."));
        delay(1000); // for test only - this results in 10 sec transmit, change to much high value to be conform with the duty cycle !!!
      }
      Serial.println(" done.");

     }
    } else {
      Serial.println("no GPS fix");
    }
 }
}
