#include "TBeamSupremeLoRaInterface.h"

#include <Arduino.h>
#include <Destination.h>
#include <Log.h>
#include <Packet.h>
#include <Reticulum.h>
#include <Transport.h>
#include <Type.h>
#include <Utilities/OS.h>
#include <microStore/Adapters/UniversalFileSystem.h>
#include <microStore/FileSystem.h>

#ifndef NODE_LABEL
#define NODE_LABEL "?"
#endif

static RNS::Reticulum reticulum({RNS::Type::NONE});
static RNS::Interface lora_interface({RNS::Type::NONE});
static RNS::Destination inbound_destination({RNS::Type::NONE});
static RNS::Destination outbound_destination({RNS::Type::NONE});
static TBeamSupremeLoRaInterface* lora_impl = nullptr;

static void print_config() {
  Serial.printf("Node=%s\r\n", NODE_LABEL);
  Serial.printf("Pins: CS=%d DIO1=%d RST=%d BUSY=%d SCK=%d MISO=%d MOSI=%d\r\n",
                (int)LORA_CS, (int)LORA_DIO1, (int)LORA_RESET, (int)LORA_BUSY,
                (int)LORA_SCK, (int)LORA_MISO, (int)LORA_MOSI);
  Serial.printf("LoRa: freq=%.1f BW=%.1f SF=%d CR=%d sync=0x%02x txp=%d\r\n",
                (double)LORA_FREQ_MHZ, (double)LORA_BW_KHZ, (int)LORA_SF,
                (int)LORA_CR, (int)LORA_SYNC_WORD, (int)LORA_TX_POWER_DBM);
}

static void on_packet(const RNS::Bytes& data, const RNS::Packet& packet) {
  (void)packet;
  float rssi = lora_impl ? lora_impl->last_rssi() : 0.0f;
  float snr = lora_impl ? lora_impl->last_snr() : 0.0f;
  Serial.printf("RX RNS: %s | RSSI=%.1f SNR=%.1f\r\n",
                data.toString().c_str(), rssi, snr);
}

static void setup_reticulum() {
  microStore::FileSystem filesystem{microStore::Adapters::UniversalFileSystem()};
  filesystem.init();
  RNS::Utilities::OS::register_filesystem(filesystem);

  lora_impl = new TBeamSupremeLoRaInterface();
  lora_interface = lora_impl;
  lora_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
  RNS::Transport::register_interface(lora_interface);
  lora_interface.start();

  reticulum = RNS::Reticulum();
  reticulum.transport_enabled(false);
  reticulum.probe_destination_enabled(false);
  reticulum.start();

  inbound_destination = RNS::Destination({RNS::Type::NONE},
                                         RNS::Type::Destination::IN,
                                         RNS::Type::Destination::PLAIN,
                                         "microreticulum",
                                         "pingpong");
  inbound_destination.set_packet_callback(on_packet);

  outbound_destination = RNS::Destination({RNS::Type::NONE},
                                          RNS::Type::Destination::OUT,
                                          RNS::Type::Destination::PLAIN,
                                          "microreticulum",
                                          "pingpong");
}

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 5000) {
    delay(100);
  }
  delay(250);

  Serial.println();
  Serial.println("Exercise 201: microReticulum LoRa ping-pong");
  print_config();

  RNS::loglevel(RNS::LOG_NOTICE);
  setup_reticulum();
  Serial.println("microReticulum ready");
}

void loop() {
  reticulum.loop();

  static uint32_t next_tx_ms = 0;
  static uint32_t iter = 0;
  uint32_t now = millis();

  if (next_tx_ms == 0) {
    uint32_t offset = (NODE_LABEL[0] == 'A') ? 500 : 1500;
    next_tx_ms = now + offset;
  }

  if ((int32_t)(now - next_tx_ms) >= 0) {
    next_tx_ms = now + 2000;

    String message = String(NODE_LABEL) + " says hi. iter=" + String(iter++);
    Serial.printf("TX RNS: %s\r\n", message.c_str());

    RNS::Packet packet(outbound_destination,
                       RNS::bytesFromString(message.c_str()),
                       RNS::Type::Packet::DATA,
                       RNS::Type::Packet::CONTEXT_NONE,
                       RNS::Type::Transport::BROADCAST,
                       RNS::Type::Packet::HEADER_1,
                       {RNS::Bytes::NONE},
                       false);
    packet.send();
  }

  delay(5);
}

int _write(int file, char* ptr, int len) {
  (void)file;
  int wrote = Serial.write(ptr, len);
  Serial.flush();
  return wrote;
}
