#pragma once

#ifndef MNLib_h
#define MNLib_h

#include <RH_RF95.h>
#include <TemperatureZero.h>
#include <BLAKE2s.h>

#define ON HIGH
#define OFF LOW

#define MODEM_CONFIG RH_RF95::Bw125Cr45Sf128
#define MODEM_POWER 13
#define MODEM_FREQ 868.0
#define HASH_LENGTH 8 // 8 bytes

#define RF_SS_PIN  1
#define RF_IRQ_PIN 0
#define BATMON_PIN A5
#define LOOPEN_PIN 27

#define DIVIDER_mA_per_LSB 2.23 / 4096 / 100 * 1000 // Adding 0.5 LSB and dividing by 4095 would be more accurate (approx. 0.01 % error currently)
#define DIVIDER_V_per_LSB 2.23 / 4096 * (180000 + 51100) / 51100

// The following block is kinda unnecessary
#define A1      A1
#define A2      A2
#define A3      A3
#define GPIO8   8
#define GPIO9   9
#define GPIO4   4
#define GPIO3   3
#define DIGIN38 38
#define DIGIN2  2
#define DIGIN5  5
#define DIGIN11 11
#define MOTOR10 10
#define MOTOR12 12
#define DOUT6   6
#define DOUT7   7

#define N_DEVICES 15
#define CFGMEM 256

#define ERROR_BLINK_HALF_INTERVAL 100 // 5 Hz


#define CONFIGURATION_ADDRESS 0
#define CONFIGURATION_MEMORY_ADDRESS 256
struct MNConfiguration
{
  float modem_frequency;
  int8_t modem_power;

  uint16_t client_address;
  uint16_t server_address;

  uint8_t devices[N_DEVICES];

  uint64_t client_secret_key;
  uint64_t server_secret_key;
} __attribute__ ((packed));
static_assert(sizeof(MNConfiguration) == 40, "MNConfiguration has the wrong size! Please edit this in the configurator too");

struct DeviceBase
{
  uint8_t class_type;
  uint8_t sensor_type;
  uint8_t pin;

  virtual void loop() = 0;
  virtual float getValue() = 0;
  virtual void setValue(float value) = 0;
  virtual void reset() = 0;
  virtual bool doSend() = 0;
  virtual void initialize() = 0;
  virtual void printStatus() = 0;
} __attribute__ ((packed));
static_assert(sizeof(DeviceBase) == 7, "Device size wrong");

struct Device : DeviceBase
{
  void loop() {}
  float getValue()
  {
    return NAN;
  }
  void setValue(float value) {}
  void reset() {} // For counters / ratemeters and such
  bool doSend()
  {
    return false;
  }
  void initialize() {}
  void printStatus()
  {
    SerialUSB.println("This is a dummy device");
  }
} __attribute__ ((packed));
static_assert(sizeof(Device) == 7, "Device size wrong");

struct AnalogInput : Device // 20 Bytes, each device gets about 17.06 bytes on average
{
  float min_threshold;
  float max_threshold;
  float multiplier;
  float offset;
  bool is_current;

  void initialize()
  {
    Device::initialize();
    pinMode(pin, INPUT);
  }
  float getValue()
  {
    int value = analogRead(pin);
    float converted_value;

    if (is_current)
      converted_value = value * DIVIDER_mA_per_LSB;
    else
      converted_value = value * DIVIDER_V_per_LSB;

    if (converted_value < min_threshold || converted_value > max_threshold)
      return NAN;
    else
      return converted_value * multiplier + offset;
  };
  bool doSend() {
    return true;
  }
  void printStatus()
  {
    SerialUSB.print("AnalogInput on pin A");
    SerialUSB.println(pin - 14);
  }
} __attribute__ ((packed));
static_assert(sizeof(AnalogInput) == 24, "AnalogInput size wrong");

enum MessageType
{
  MT_DeviceStatus = 1,
  MT_SensorStatus = 2,
};


extern MNConfiguration configuration;
extern uint8_t configuration_memory[CFGMEM];
extern RH_RF95 radio;
extern TemperatureZero temperature_sensor;
extern BLAKE2s hash_generator;
extern uint32_t message_id;
extern DeviceBase* devices[N_DEVICES];


//void init_mn(bool is_client);
void init_mn();
void test();
void printStatusReport();
void setLoopPower(bool state);
void saveMemory();
void loadMemory();
void refreshConfig();
float batteryVoltage();
void errorBlink(int n); // Quickly blink n times
bool send(uint8_t data[], uint8_t len);
void initializeDevices();
DeviceBase* getDevice(uint8_t pointer);
void loopSensors();
void sendSensorData();
#endif