#include <ArduinoJson.h>
#include "Variables.h"
#include <Arduino.h>

bool group_switch_gauge_measurement = true; // Measure flag

struct group_pin_defs{
  uint8_t solenoid_pin;
  uint8_t flow_meter_pin;
};

typedef struct{
  const int id;
  const struct group_pin_defs mix_pin_defs;
  const struct group_pin_defs alcohol_pin_defs;
  float mix;
  float alcohol;
  int current_threshold;
  int current_threshold_alcohol;
  bool alcohol_switch;
  bool mix_switch;
  bool mix_reset_routine;
  bool alcohol_reset_routine;
}group_vars_t;

/****************GROUP 1 VARIABLES**************************/
group_vars_t group1_vars = {
  .id = 1,
  .mix_pin_defs = {.solenoid_pin = G1_MIX_SOLENOID_PIN, .flow_meter_pin = G1_MIX_FLOW_METER_PIN},
  .alcohol_pin_defs = {.solenoid_pin = G1_ALCOHOL_SOLENOID_PIN, .flow_meter_pin = G1_ALCOHOL_FLOW_METER_PIN},
  .mix = 0,
  .alcohol = 0,
  .current_threshold = 50,
  .current_threshold_alcohol = 20,
  .alcohol_switch = false,
  .mix_switch = false,
  .mix_reset_routine = true,
  .alcohol_reset_routine = true,
};

/****************GROUP 2 VARIABLES**************************/
group_vars_t group2_vars = {
  .id = 2,
  .mix_pin_defs = {.solenoid_pin = G2_MIX_SOLENOID_PIN, .flow_meter_pin = G2_MIX_FLOW_METER_PIN},
  .alcohol_pin_defs = {.solenoid_pin = G2_ALCOHOL_SOLENOID_PIN, .flow_meter_pin = G2_ALCOHOL_FLOW_METER_PIN},
  .mix = 0,
  .alcohol = 0,
  .current_threshold = 50,
  .current_threshold_alcohol = 20,
  .alcohol_switch = false,
  .mix_switch = false,
  .mix_reset_routine = true,
  .alcohol_reset_routine = true,
};

/****************GROUP 3 VARIABLES**************************/
group_vars_t group3_vars = {
  .id = 3,
  .mix_pin_defs = {.solenoid_pin = G3_MIX_SOLENOID_PIN, .flow_meter_pin = G3_MIX_FLOW_METER_PIN},
  .alcohol_pin_defs = {.solenoid_pin = G3_ALCOHOL_SOLENOID_PIN, .flow_meter_pin = G3_ALCOHOL_FLOW_METER_PIN},
  .mix = 0,
  .alcohol = 0,
  .current_threshold = 50,
  .current_threshold_alcohol = 20,
  .alcohol_switch = false,
  .mix_switch = false,
  .mix_reset_routine = true,
  .alcohol_reset_routine = true,
};

/****************GROUP 4 VARIABLES**************************/
group_vars_t group4_vars = {
  .id = 4,
  .mix_pin_defs = {.solenoid_pin = G4_MIX_SOLENOID_PIN, .flow_meter_pin = G4_MIX_FLOW_METER_PIN},
  .alcohol_pin_defs = {.solenoid_pin = G4_ALCOHOL_SOLENOID_PIN, .flow_meter_pin = G4_ALCOHOL_FLOW_METER_PIN},
  .mix = 0,
  .alcohol = 0,
  .current_threshold = 50,
  .current_threshold_alcohol = 20,
  .alcohol_switch = false,
  .mix_switch = false,
  .mix_reset_routine = true,
  .alcohol_reset_routine = true,
};

StaticJsonDocument<1500> doc;
DynamicJsonDocument outgoing_json(1500);

// Initialise inputs
void init_inputs()
{
  pinMode(G1_MIX_FLOW_METER_PIN, INPUT);
  pinMode(G2_MIX_FLOW_METER_PIN, INPUT);
  pinMode(G3_MIX_FLOW_METER_PIN, INPUT);
  pinMode(G4_MIX_FLOW_METER_PIN, INPUT);

  pinMode(G1_ALCOHOL_FLOW_METER_PIN, INPUT);
  pinMode(G2_ALCOHOL_FLOW_METER_PIN, INPUT);
  pinMode(G3_ALCOHOL_FLOW_METER_PIN, INPUT);
  pinMode(G4_ALCOHOL_FLOW_METER_PIN, INPUT);

  attachInterrupt(digitalPinToInterrupt(G1_MIX_FLOW_METER_PIN), flow_meter1_mix_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G2_MIX_FLOW_METER_PIN), flow_meter2_mix_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G3_MIX_FLOW_METER_PIN), flow_meter3_mix_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G4_MIX_FLOW_METER_PIN), flow_meter4_mix_isr, RISING);

  attachInterrupt(digitalPinToInterrupt(G1_ALCOHOL_FLOW_METER_PIN), flow_meter1_alcohol_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G2_ALCOHOL_FLOW_METER_PIN), flow_meter2_alcohol_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G3_ALCOHOL_FLOW_METER_PIN), flow_meter3_alcohol_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(G4_ALCOHOL_FLOW_METER_PIN), flow_meter4_alcohol_isr, RISING);
}

// Initialise outputs
void init_outputs()
{
  pinMode(G1_MIX_SOLENOID_PIN, OUTPUT);
  pinMode(G2_MIX_SOLENOID_PIN, OUTPUT);
  pinMode(G3_MIX_SOLENOID_PIN, OUTPUT);
  pinMode(G4_MIX_SOLENOID_PIN, OUTPUT);

  pinMode(G1_ALCOHOL_SOLENOID_PIN, OUTPUT);
  pinMode(G2_ALCOHOL_SOLENOID_PIN, OUTPUT);
  pinMode(G3_ALCOHOL_SOLENOID_PIN, OUTPUT);
  pinMode(G4_ALCOHOL_SOLENOID_PIN, OUTPUT);

  digitalWrite(G1_MIX_SOLENOID_PIN, LOW);  // turn the LED on (HIGH is the voltage level)
  digitalWrite(G2_MIX_SOLENOID_PIN, LOW);
  digitalWrite(G3_MIX_SOLENOID_PIN, LOW);
  digitalWrite(G4_MIX_SOLENOID_PIN, LOW);

  digitalWrite(G1_ALCOHOL_SOLENOID_PIN, LOW);  // turn the LED on (HIGH is the voltage level)
  digitalWrite(G2_ALCOHOL_SOLENOID_PIN, LOW);
  digitalWrite(G3_ALCOHOL_SOLENOID_PIN, LOW);
  digitalWrite(G4_ALCOHOL_SOLENOID_PIN, LOW);
}


// Set solenoid to high/low
void switch_solenoid(uint8_t pin, uint8_t state)
{
  digitalWrite(pin, state);
}

void check_mix(group_vars_t *group, int max)
{
  DynamicJsonDocument outgoing_json_data(200);

  if(group->mix >= max){
    group->mix_switch = false;
    switch_solenoid(group->mix_pin_defs.solenoid_pin, LOW);

    if(group->alcohol < group->current_threshold_alcohol){
      group->alcohol_switch = true;
      switch_solenoid(group->alcohol_pin_defs.solenoid_pin, HIGH);
    }
        
    String json_string;
    outgoing_json_data["status"] = String("MIX_OUT_" + String(group->id));
    serializeJson(outgoing_json_data, json_string);
    Serial.println(json_string);
  }
  else if(group->mix >= group->current_threshold){
    group->current_threshold += MIX_THRESHOLD_STEP;

    if(!(group->alcohol_switch) && group->alcohol < ALCOHOL_MAX_THRESHOLD){
      switch_solenoid(group->alcohol_pin_defs.solenoid_pin, HIGH);
      group->alcohol_switch = true;
    }
  }
}

void check_alcohol(group_vars_t *group, int max)
{
  DynamicJsonDocument outgoing_json_data(200);

  if(group->alcohol >= max){
    group->alcohol_switch = false;
    switch_solenoid(group->alcohol_pin_defs.solenoid_pin, LOW);

    if(group->mix < group->current_threshold){
      group->mix_switch = true;
      switch_solenoid(group->mix_pin_defs.solenoid_pin, HIGH);
    }

    String json_string;
    outgoing_json_data["status"] = String("ALCOHOL_OUT_" + String(group->id));
    serializeJson(outgoing_json_data, json_string);
    Serial.println(json_string);
  }
  else if(group->alcohol >= group->current_threshold_alcohol){
    group->current_threshold_alcohol += ALCOHOL_THRESHOLD_STEP;

    if(!(group->mix_switch) && group->mix < MIX_MAX_THRESHOLD){
      group->mix_switch = true;
      switch_solenoid(group->mix_pin_defs.solenoid_pin, HIGH);
    }
  }
}

void check_mix_reset_routine(group_vars_t *group)
{
  if(group->mix_reset_routine){
    group->mix_switch = true;
    switch_solenoid(group->mix_pin_defs.solenoid_pin, HIGH);

    group->mix = 0;
    group->mix_reset_routine = false;
    group->current_threshold = MIX_THRESHOLD_STEP;

    switch(group->id){
      case 1:{
        pulse_1_mix = 0;
        break;
      }
      case 2:{
        pulse_2_mix = 0;
        break;
      }
      case 3:{
        pulse_3_mix = 0;
        break;
      }
      case 4:{
        pulse_4_mix = 0;
        break;
      }
      default:{
        break;
      }
    }
  }
}

void check_alcohol_reset_routine(group_vars_t *group)
{
  if(group->alcohol_reset_routine){
    
    group->alcohol_switch = false;
    switch_solenoid(group->alcohol_pin_defs.solenoid_pin, HIGH);

    group->alcohol = 0;
    group->alcohol_reset_routine = false;
    group->current_threshold_alcohol = ALCOHOL_THRESHOLD_STEP;  

    switch(group->id){
      case 1:{
        pulse_1_alcohol = 0;
        break;
      }
      case 2:{
        pulse_2_alcohol = 0;
        break;
      }
      case 3:{
        pulse_3_alcohol = 0;
        break;
      }
      case 4:{
        pulse_4_alcohol = 0;
        break;
      }
      default:{
        break;
      }
    }    
  }
}



void update_status(group_vars_t *group)
{
  String json_string;

  if((group->mix >= MIX_MAX_THRESHOLD) && (group->alcohol >= ALCOHOL_MAX_THRESHOLD)){
    // Ignore status if limit is exceeded for both alcohol and mix
    return;
  }
  
  outgoing_json["status"] = String("mixing-" + String(group->id));
  outgoing_json["mix"] = group->mix;
  outgoing_json["alcohol"] = group->alcohol;
  outgoing_json["currentThresh_mix"] = group->current_threshold;
  outgoing_json["currentThresh_alcohol"] = group->current_threshold_alcohol;
  outgoing_json["alcohol_reset_routine"] = group->alcohol_reset_routine;
  outgoing_json["pulse_mix"] = pulse_1_mix;
  outgoing_json["pulse_alcohol"] = pulse_1_alcohol;


  serializeJson(outgoing_json, json_string);
  Serial.println(json_string);

  //flow_meter1_mix_isr();
}

void measurement_task(void *pvParameters) {
  // StaticJsonDocument<200> doc_data;
  // DynamicJsonDocument outgoing_json_data(200);

  while (1) {
    if (group_switch_gauge_measurement) {

      update_status(&group1_vars);
      // update_status(&group2_vars);
      // update_status(&group3_vars);
      // update_status(&group4_vars);

      check_alcohol_reset_routine(&group1_vars);
      check_alcohol_reset_routine(&group2_vars);
      check_alcohol_reset_routine(&group3_vars);
      check_alcohol_reset_routine(&group4_vars);

      check_mix_reset_routine(&group1_vars);
      check_mix_reset_routine(&group2_vars);
      check_mix_reset_routine(&group3_vars);
      check_mix_reset_routine(&group4_vars);

      check_mix(&group1_vars, MIX_MAX_THRESHOLD);
      check_mix(&group2_vars, MIX_MAX_THRESHOLD);
      check_mix(&group3_vars, MIX_MAX_THRESHOLD);
      check_mix(&group4_vars, MIX_MAX_THRESHOLD);

      check_alcohol(&group1_vars, ALCOHOL_MAX_THRESHOLD);
      check_alcohol(&group2_vars, ALCOHOL_MAX_THRESHOLD);
      check_alcohol(&group3_vars, ALCOHOL_MAX_THRESHOLD);
      check_alcohol(&group4_vars, ALCOHOL_MAX_THRESHOLD);


      flow_meter1_mix_isr();
      flow_meter1_alcohol_isr();
      // flow_meter1_alcohol_isr();
    }

    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

void jsonReceiveTask(void *pvParameters) {
  Serial.begin(115200);

  while (1) {
    // Task to receive and process incoming JSON data from the serial port

    if (Serial.available() > 0) {
      String incomingChar = Serial.readString();
      DeserializationError error = deserializeJson(doc, incomingChar);
      String json_string;

      if (error) {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.f_str());
        continue;
      }

      if (doc["status"] == "intialization") {
        calibration_multiplier_flow_1_keg = doc["G1_calibration_factor_mix"];
        calibration_multiplier_flow_2_keg = doc["G2_calibration_factor_mix"];
        calibration_multiplier_flow_3_keg = doc["G3_calibration_factor_mix"];
        calibration_multiplier_flow_4_keg = doc["G4_calibration_factor_mix"];

        calibration_multiplier_flow_1_alcohol = doc["G1_calibration_factor_alcohol"];
        calibration_multiplier_flow_2_alcohol = doc["G2_calibration_factor_alcohol"];
        calibration_multiplier_flow_3_alcohol = doc["G3_calibration_factor_alcohol"];
        calibration_multiplier_flow_4_alcohol = doc["G4_calibration_factor_alcohol"];

        g1_mix_max_vol = doc["g1_mix_max_vol"];
        g1_mix_interval_vol = doc["g1_mix_interval_vol"];
        g1_alcohol_max_vol = doc["g1_alcohol_max_vol"];
        g1_alcohol_interval_vol = doc["g1_alcohol_interval_vol"];

        g2_mix_max_vol = doc["g2_mix_max_vol"];
        g2_mix_interval_vol = doc["g2_mix_interval_vol"];
        g2_alcohol_max_vol = doc["g2_alcohol_max_vol"];
        g2_alcohol_interval_vol = doc["g2_alcohol_interval_vol"];

        g3_mix_max_vol = doc["g3_mix_max_vol"];
        g3_mix_interval_vol = doc["g3_mix_interval_vol"];
        g3_alcohol_max_vol = doc["g3_alcohol_max_vol"];
        g3_alcohol_interval_vol = doc["g3_alcohol_interval_vol"];

        g4_mix_max_vol = doc["g4_mix_max_vol"];
        g4_mix_interval_vol = doc["g4_mix_interval_vol"];
        g4_alcohol_max_vol = doc["g3_alcohol_max_vol"];
        g3_alcohol_interval_vol = doc["g4_alcohol_interval_vol"];
        // currentThreshold=g1_mix_interval_vol;

        outgoing_json["status"] = "initialized";
        outgoing_json["G1_calibration_factor_mix"] = calibration_multiplier_flow_1_keg;
        outgoing_json["G2_calibration_factor_mix"] = calibration_multiplier_flow_2_keg;
        outgoing_json["G3_calibration_factor_mix"] = calibration_multiplier_flow_3_keg;
        outgoing_json["G4_calibration_factor_mix"] = calibration_multiplier_flow_4_keg;

        outgoing_json["G1_calibration_factor_alcohol"] = calibration_multiplier_flow_1_alcohol;
        outgoing_json["G2_calibration_factor_alcohol"] = calibration_multiplier_flow_2_alcohol;
        outgoing_json["G3_calibration_factor_alcohol"] = calibration_multiplier_flow_3_alcohol;
        outgoing_json["G4_calibration_factor_alcohol"] = calibration_multiplier_flow_4_alcohol;
        // outgoing_json["current_threshold_G1"] = currentThreshold;

        serializeJson(outgoing_json, json_string);
        Serial.println(json_string);
      }
      else if (doc["status"] == "start_gauge_measurement") {
        group_switch_gauge_measurement = true;

        // Force a reset
        group1_vars.alcohol_reset_routine = true;
        group1_vars.mix_reset_routine = true;

        group2_vars.alcohol_reset_routine = true;
        group2_vars.mix_reset_routine = true;

        group3_vars.alcohol_reset_routine = true;
        group4_vars.mix_reset_routine = true;

        group4_vars.alcohol_reset_routine = true;
        group4_vars.mix_reset_routine = true;
      }
      else if (doc["status"] == "G1_RESET_ALCOHOL") {
        group1_vars.alcohol_reset_routine = true;
      }
      else if (doc["status"] == "G1_RESET_MIX") {
        group1_vars.mix_reset_routine = true;
      }
      else if (doc["status"] == "G2_RESET_ALCOHOL") {
        group2_vars.alcohol_reset_routine = true;

        outgoing_json["status"] = "RESETED_ALCOHOL";
        serializeJson(outgoing_json, json_string);
        Serial.println(json_string);
      }
      else if (doc["status"] == "G2_RESET_MIX") {
        group2_vars.mix_reset_routine = true;
      }
      else if (doc["status"] == "G3_RESET_ALCOHOL") {
        group3_vars.alcohol_reset_routine = true;
      }
      else if (doc["status"] == "G3_RESET_MIX") {
        group3_vars.mix_reset_routine = true;
      }
      else if (doc["status"] == "G4_RESET_ALCOHOL") {
        group4_vars.alcohol_reset_routine = true;
      }
      else if (doc["status"] == "G4_RESET_MIX") {
        group4_vars.mix_reset_routine = true;
      }
    }

    vTaskDelay(pdMS_TO_TICKS(500));  // Adjust the delay as needed
  }
}

void setup() {

  init_inputs();
  init_outputs();

  xTaskCreatePinnedToCore(measurement_task, "Task1", 4096, NULL, 0, NULL, 0);
  xTaskCreatePinnedToCore(jsonReceiveTask, "JSONRecv", 2048, NULL, 1, NULL, 1);
}

float measure(float calibration_multiplier, int pulse) {
  Serial.print("pulse:-");
  Serial.println(pulse);
  return (calibration_multiplier * pulse);
}


void flow_meter1_mix_isr()
{
  pulse_1_mix++;
  Serial.println(pulse_1_mix);

  group1_vars.mix = measure(calibration_multiplier_flow_1_keg, pulse_1_mix);
  if(group1_vars.mix >= group1_vars.current_threshold){
    group1_vars.mix_switch = false;
    switch_solenoid(group1_vars.mix_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter2_mix_isr()
{
  pulse_2_mix++;

  group2_vars.mix = measure(calibration_multiplier_flow_2_keg, pulse_2_mix);
  if(group2_vars.mix >= group2_vars.current_threshold){
    group2_vars.mix_switch = false;
    switch_solenoid(group2_vars.mix_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter3_mix_isr()
{
  pulse_3_mix++;

  group3_vars.mix = measure(calibration_multiplier_flow_3_keg, pulse_3_mix);
  if(group3_vars.mix >= group3_vars.current_threshold){
    group3_vars.mix_switch = false;
    switch_solenoid(group3_vars.mix_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter4_mix_isr()
{
  pulse_4_mix++;

  group4_vars.mix = measure(calibration_multiplier_flow_4_keg, pulse_4_mix);
  if(group4_vars.mix >= group4_vars.current_threshold){
    group4_vars.mix_switch = false;
    switch_solenoid(group4_vars.mix_pin_defs.solenoid_pin, LOW);
  }
}


void flow_meter1_alcohol_isr()
{
  pulse_1_alcohol++;

  group1_vars.alcohol = measure(calibration_multiplier_flow_1_alcohol, pulse_1_alcohol);
  if(group1_vars.alcohol >= group1_vars.current_threshold_alcohol){
    group1_vars.alcohol_switch = false;
    switch_solenoid(group1_vars.alcohol_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter2_alcohol_isr()
{
  pulse_2_alcohol++;

  group2_vars.alcohol = measure(calibration_multiplier_flow_2_alcohol, pulse_2_alcohol);
  if(group2_vars.alcohol >= group2_vars.current_threshold_alcohol){
    group2_vars.alcohol_switch = false;
    switch_solenoid(group2_vars.alcohol_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter3_alcohol_isr()
{
  pulse_3_alcohol++;

  group3_vars.alcohol = measure(calibration_multiplier_flow_3_alcohol, pulse_3_alcohol);
  if(group3_vars.alcohol >= group3_vars.current_threshold_alcohol){
    group3_vars.alcohol_switch = false;
    switch_solenoid(group3_vars.alcohol_pin_defs.solenoid_pin, LOW);
  }
}

void flow_meter4_alcohol_isr()
{
  pulse_4_alcohol++;

  group4_vars.alcohol = measure(calibration_multiplier_flow_4_alcohol, pulse_4_alcohol);
  if(group4_vars.alcohol >= group4_vars.current_threshold_alcohol){
    group4_vars.alcohol_switch = false;
    switch_solenoid(group4_vars.alcohol_pin_defs.solenoid_pin, LOW);
  }
}

void loop() {
  // Main loop code here
}