#include <SPI.h>
#include <LoRa.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <FastLED.h>

#define output_active 3000 // How long is output activated in ms
#define led_fire 2000
#define buzzer_fire 1000
#define buzzer_arm 2000
#define buzzer_disarm 500

byte output_pins[] = {8, 7, 6, 5, 4, 3};

OneWire ds(A1);  // DS18B20 pin

CRGB led[1];

#define BUZZER A3
#define LED A5
#define battery A6
byte unit = EEPROM.read(0);
byte LoRa_power = EEPROM.read(1);
byte LoRa_SF = EEPROM.read(2);
boolean hold_up_timer = false;
unsigned long hold_up_timer_from;
unsigned long set_hold_up_timer[] = {0, 0, 0, 0, 0, 0};
unsigned long glow[6];
unsigned long command_time;
byte alias[6];
int one_by_one;
int one_by_one_from;
int hold_up;
boolean time_running = false;
unsigned long set_time = 0;
unsigned long timer = 0;
byte time_h = 0;
byte time_m = 0;
byte time_s = 0;
byte n;
byte command = 0;
boolean na[6];
boolean armed = false;
unsigned long blinking;
boolean led_on = false;
unsigned long led_fire_stamp;

unsigned long refresh_bat_lvl;
int bat_lvl;


void setup() {
  FastLED.addLeds<WS2812B, LED, GRB>(led, 1);
  
  for(int i = 0; i < 6; i++) {
    pinMode(output_pins[i], OUTPUT);
    digitalWrite(output_pins[i], LOW);
  }

  if((LoRa_power > 20) || (LoRa_power < 2)) {
    LoRa_power = 17;
    EEPROM.write(1, LoRa_power);
  }
  if((LoRa_SF > 12) || (LoRa_SF < 6)) {
    LoRa_SF = 7;
    EEPROM.write(2, LoRa_SF);
  }

  if(!LoRa.begin(433E6)) {
    while(1);
  }
  LoRa.setTxPower(LoRa_power);
  LoRa.setSpreadingFactor(LoRa_SF);

  hold_up = 500 * unit;
  one_by_one_from = (6 * (unit - 1)) + 1;

  for(byte i = 0; i < 6; i ++) {
    alias[i] = one_by_one_from + i;
  }

  led[0] = CRGB::Black;
  FastLED.show();
  tone(BUZZER, 5000, 100);

  readTemp();
  analogRead(battery);

  // Serial communication -------------------------
  Serial.begin(9600);

  Serial.print("Unit number set to ");
  Serial.println(unit);

  Serial.print("LoRa power set to ");
  Serial.print(LoRa_power);
  Serial.println(" dBm");
  
  Serial.print("LoRa spreading factor set to ");
  Serial.println(LoRa_SF);
}

void loop() {
  if(LoRa.parsePacket() > 0) {
    String msg = LoRa.readString();

    if(msg.startsWith("Ping")) {
      one_by_one = getFirstNumber(msg);
      if(one_by_one == unit) {
        command_time = millis() + 500;
      }
      else if(one_by_one == 0) {
        command_time = millis() + hold_up;
      }
      command = 1;
    }

    // go into READY mode -------------------------------------------------------------------------
    else if(msg.startsWith("Arm!")) {
      armed = true;

      tone(BUZZER, 5000, buzzer_arm);

      command_time = millis() + hold_up;
      command = 9;
    }

    // go into SAFE mode --------------------------------------------------------------------------
    else if(msg.startsWith("Disarm!")) {
      armed = false;

      // stop countdown ---------------------------------------------------------------------------
      if(time_running) {
        time_running = false;
        set_time = set_time - (millis() - timer);
      }
      
      tone(BUZZER, 5000, buzzer_disarm);

      command_time = millis() + hold_up;
      command = 9;
    }

    else if(msg == "Fire_ALL") {
      if(armed) {
        hold_up_timer_from = millis();
        hold_up_timer = true;
        n = 0;
        for(int i = 0; i < 6; i++) {
          na[i] = false;
        }
      }
    }

    else if(msg.startsWith("Fire")) {
      if(armed) {
        one_by_one = getFirstNumber(msg);

        for(byte i = 0; i < 6; i ++) {
          if(one_by_one == alias[i]) {
            tone(BUZZER, 5000, buzzer_fire);
            led_on = true;
            led[0] = CRGB::White;
            FastLED.show();
            led_fire_stamp = millis() + led_fire;
            blinking = millis() + 100;
            
            digitalWrite(output_pins[i], HIGH);
            glow[i] = millis() + output_active;
            
            command_time = millis() + 500;
            command = 100;
          }
        }
      }
    }

    else if(msg.startsWith("Start")) {
      if(armed) {
        timer = millis();
        time_running = true;
      }
    }

    else if(msg.startsWith("Time")) {
      set_time = getSecondNumber(msg);
    }

    else if(msg.startsWith("Delay")) {
      one_by_one = getFirstNumber(msg);
      if((one_by_one >= one_by_one_from) && (one_by_one <= (one_by_one_from + 5))) {
        set_hold_up_timer[one_by_one - one_by_one_from] = getSecondNumber(msg);
        delay(500);
        resendDelay(one_by_one, set_hold_up_timer[one_by_one - one_by_one_from]);
      }
    }

    else if(msg.startsWith("Alias")) {
      one_by_one = getFirstNumber(msg);

      if((one_by_one >= one_by_one_from) && (one_by_one <= (one_by_one_from + 5))) {
        alias[one_by_one - one_by_one_from] = getSecondNumber(msg);
        delay(500);
        resendAlias(one_by_one, alias[one_by_one - one_by_one_from]);
      }
    }

    else if(msg.startsWith("Power")) {
      one_by_one = getFirstNumber(msg);
      if((one_by_one == unit) || (one_by_one == 0)) {
        LoRa_power = getSecondNumber(msg);
        EEPROM.write(1, LoRa_power);
        LoRa.setTxPower(LoRa_power);
      }
    }

    else if(msg.startsWith("SF")) {
      one_by_one = getFirstNumber(msg);
      LoRa_SF = getSecondNumber(msg);
      
      if(one_by_one == unit) {
        delay(500);
        sendLoRaSetting();
        delay(500);
        EEPROM.write(2, LoRa_SF);
        LoRa.setSpreadingFactor(LoRa_SF);
      }
      else if(one_by_one == 0) {
        command_time = millis() + hold_up;
        command = 5;
      }
    }

    else if(msg.startsWith("/")) {
      one_by_one = getFirstNumber(msg);
      msg.remove(msg.indexOf('/'), 1);
      msg.remove(msg.indexOf('('), msg.length() - msg.indexOf('('));

      // Time Info
      if(msg == "c") {
        if(one_by_one == unit) {
          command_time = millis() + 500;
        }

        else if(one_by_one == 0) {
          command_time = millis() + hold_up;
        }
        command = 2;
      }

      // Load Time
      else if(msg == "d") {
        if(one_by_one == unit) {
          if(time_running) {
            delay(500);
            LoRa.beginPacket();
            LoRa.print("Time_T[");
            LoRa.print(set_time - (millis() - timer));
            LoRa.print("]");
            LoRa.endPacket();
          }
          else {
            delay(500);
            LoRa.beginPacket();
            LoRa.print("Time_F[");
            LoRa.print(set_time);
            LoRa.print("]");
            LoRa.endPacket();
          }
        }
      }

      // Delay Info
      else if(msg == "e") {
        if((one_by_one >= one_by_one_from) && (one_by_one <= (one_by_one_from + 5))) {
          command_time = millis() + 500;
        }
        if(one_by_one == 0) {
          command_time = millis() + 1000 * (6 *  (unit - 1));
        }
        command = 3;
      }

      // LoRa Setting Info ------------------------------------------------------------------------
      else if(msg == "f") {
        if(one_by_one == unit) {
          command_time = millis() + 500;
        }
        else if(one_by_one == 0) {
          command_time = millis() + hold_up;
        }
        command = 4;
      }

      // Alias Info -------------------------------------------------------------------------------
      else if(msg == "g") {
        if((one_by_one >= one_by_one_from) && (one_by_one <= (one_by_one_from + 5))) {
          command_time = millis() + 500;
        }
        else if(one_by_one == 0) {
          command_time = millis() + 1000 * (6 *  (unit - 1));
        }
        command = 6;
      }

      // Temperature Info -------------------------------------------------------------------------
      else if(msg == "h") {
        if(one_by_one == unit) {
          command_time = millis() + 500;
        }
        else if(one_by_one == 0) {
          command_time = millis() + hold_up;
        }
        command = 7;
      }

      // Battery Info -----------------------------------------------------------------------------
      else if(msg == "i") {
        if(one_by_one == unit) {
          command_time = millis() + 500;
        }
        else if(one_by_one == 0) {
          command_time = millis() + hold_up;
        }
        command = 8;
      }

      // Test LED ---------------------------------------------------------------------------------
      else if(msg == "j") {
        if((one_by_one == unit) || (one_by_one == 0)) {
          blinking = millis() + 1000;
          led_on = true;
          led[0] = CRGB::White;
          FastLED.show();
        }
      }

      // Test Buzzer ------------------------------------------------------------------------------
      else if(msg == "k") {
        if((one_by_one == unit) || (one_by_one == 0)) {
          tone(BUZZER, 5000, 1000);
        }
      }

      else if(msg == "l") {
        if(one_by_one == unit) {
          command_time = millis() + 500;
        }
        else if(one_by_one == 0) {
          command_time = millis() + hold_up;
        }
        command = 9;
      }

      // LED & Buzzer Test -------------------------------------------------------------------------
      else if(msg == "m") {
        if((one_by_one == unit) || (one_by_one == 0)) {
          blinking = millis() + 1000;
          led_on = true;
          led[0] = CRGB::White;
          FastLED.show();

          tone(BUZZER, 5000, 1000);
        }
      }
    }
  }

  // Setting via serial communication -------------------------------------------------------------
  if(Serial.available() > 0) {
    String msg = Serial.readString();

    if(msg.startsWith("Unit")) {
      unit = getFirstNumber(msg);
      EEPROM.write(0, unit);
      Serial.print("Unit number set to ");
      Serial.println(unit);
    }

    else if(msg.startsWith("Power")) {
      LoRa_power = getFirstNumber(msg);
      EEPROM.write(1, LoRa_power);
      LoRa.setTxPower(LoRa_power);
      Serial.print("LoRa power set to ");
      Serial.print(LoRa_power);
      Serial.println(" dBm");
    }

    else if(msg.startsWith("SF")) {
      LoRa_SF = getFirstNumber(msg);
      EEPROM.write(2, LoRa_SF);
      LoRa.setSpreadingFactor(LoRa_SF);
      Serial.print("LoRa spreading factor set to ");
      Serial.println(LoRa_SF);
    }
  }

  if(time_running) {
    unsigned long time = set_time - (millis() - timer);

    if(time <= 0) {
      time_running = false;

      if(armed) {
        hold_up_timer_from = millis();
        hold_up_timer = true;
        n = 0;
        for(int i = 0; i < 6; i++) {
          na[i] = false;
        }
      }
    }
  }


  if(hold_up_timer && armed) {
    for(int i = 0; i < 6; i++) {
      if((millis() > (hold_up_timer_from + set_hold_up_timer[i])) && (na[i] == false)) {
        tone(BUZZER, 5000, buzzer_fire);
        led_on = true;
        led[0] = CRGB::White;
        FastLED.show();
        led_fire_stamp = millis() + led_fire;
        blinking = millis() + 100;
        
        digitalWrite(output_pins[i], HIGH);
        glow[i] = millis() + output_active;
        n ++;
        na[i] = true;
      }
    }
    if(n == 6) {
      hold_up_timer = false;
    }
  }

  // Light blinking -------------------------------------------------------------------------------
  if(millis() > blinking) {
    if(led_on) {
      led_on = false;
      led[0] = CRGB::Black;
      FastLED.show();

      if(millis() < led_fire_stamp) {
        blinking = millis() + 100;
      }
      else {
        blinking = millis() + 750;
      }
    }
    else {
      led_on = true;

      if(millis() < led_fire_stamp) {
        led[0] = CRGB::White;
        FastLED.show();
        blinking = millis() + 100;
      }
      else if(armed) {
        led[0] = CRGB::Red;
        FastLED.show();
        blinking = millis() + 250;
      }
      else {
        led[0] = CRGB::Green;
        FastLED.show();
        blinking = millis() + 250;
      }
    }
  }

  // Deactivating outputs -------------------------------------------------------------------------
  for(int i = 0; i < 6; i++) {
    if(millis() > glow[i]) {
      digitalWrite(output_pins[i], LOW);
    }
  }

  // read battery voltage every 30 s and save to bat_lvl ------------------------------------------
  if(millis() > refresh_bat_lvl) {
    refresh_bat_lvl = millis() + 30000;
    bat_lvl = max(map(analogRead(battery), 600, 790, 0, 100), 0);
    bat_lvl = min(bat_lvl, 100);

    if((bat_lvl < 30) && (!armed)) {
      blinking = millis() + 1000;
      led_on = true;
      led[0] = CRGB::Blue;
      FastLED.show();

      tone(BUZZER, 5000, 1000);
    }

    // debugging
    /*Serial.print("ADC battery level: ");
    Serial.print(analogRead(battery));
    Serial.print(", ");
    Serial.print(bat_lvl);
    Serial.println(" %");*/
  }

  waitingCommands();
}
