////////////////////////////////////////////////////////////////////////////////////////////////////
//ANDRUINO LIBRARY
//A.Scavuzzo
//www.andruino.it
////////////////////////////////////////////////////////////////////////////////////////////////////

byte lim_check_HiLolimits (unsigned int enable_limits, unsigned int max_lim, unsigned int min_lim, LimitStruct &Limit, unsigned int value);
byte lim_check_digitalAlarm (unsigned int enable_limits, bool alarm, LimitDigStruct &Limit, bool value);

void lim_check_AnaVar_pin_limits(char *push_usr, char *arduino_name, char *module_name, char *lim_type, byte i, unsigned int enable_limits, unsigned int  max, unsigned int  min, LimitStruct & Limit, float value, unsigned int factor);
void lim_check_Dig_pin_limits (char *push_usr, char *arduino_name, char *module_name, char *lim_type, byte i, unsigned int enable_limits, boolean  alarm, LimitDigStruct & Limit, byte value);
void  lim_CheckLimitsSensors(char *push_usr, char *arduino_name);


/////////////////////////////////////////////////////
//Check LIMITS and Send PUSH NOTIFICATIONS
/////////////////////////////////////////////////////
void  lim_CheckLimitsSensors(char *push_usr, char *arduino_name)

{
  char module_name[15];
  //check limits of Analog inputs
  //low/hi
  //THE ANALOG LIMITS ARE SENT MULTIPLIED BY 1024
  if (check_limits_base) {
#if DEBUG_SERIAL_LIMITS ==1
    Serial.println(F("Check limits for Base"));
    Serial.println(F("Analog:"));
#endif
    check_limits_base = false;
    for (int i = 0; i < sizeof(base_analog_pins_used); i++) {
      ArduinoAnalog[i].value = analogRead(i) * ADC_STEP;    //read the Analog input
      lim_check_AnaVar_pin_limits(push_usr, arduino_name, "arduino_io", "ana", base_analog_pins_used[i], ArduinoAnalog[i].enable_limits, ArduinoAnalog[i].max, ArduinoAnalog[i].min, ArduinoAnalog[i].Limit, ArduinoAnalog[i].value, 1024);
    }

    //check limits of Digital inputs
    //low/hi
#if DEBUG_SERIAL_LIMITS ==1
    Serial.println(F("Digital:"));
#endif

    for (int i = 0; i < sizeof(base_digital_pins_used); i++) {   //MAXPIN
      if (ArduinoIO[i].used == 1 && ArduinoIO[i].mode == 0) {  //Used and INPUT (mode=0 -- input, mode=1 --output, mode=3 --pwm)
        read(ArduinoIO[i]);        //read digital inputs
        lim_check_Dig_pin_limits (push_usr, arduino_name, "arduino_io", "dig", base_digital_pins_used[i], ArduinoIO[i].enable_limits, ArduinoIO[i].alarm, ArduinoIO[i].Limit, ArduinoIO[i].state);
      }
    }

    //THE VARIABLE LIMITS ARE SENT MULTIPLIED BY 10
#if DEBUG_SERIAL_LIMITS ==1
    Serial.println(F("Variables:"));
#endif
    for (int i = 0; i < sizeof(base_variable_used); i++) {
      lim_check_AnaVar_pin_limits(push_usr, arduino_name, "arduino_var", "var", base_variable_used[i], Arduino_User_var[i].enable_limits, Arduino_User_var[i].max, Arduino_User_var[i].min, Arduino_User_var[i].Limit, Arduino_User_var[i].value, 10);
    }
  }



#if NRF24L_ENABLE == 1
  //THE NRF VARIABLE LIMITS ARE SENT MULTIPLIED BY 10
  if (check_limits_NRF) {
#if DEBUG_SERIAL_LIMITS ==1
    Serial.println(F("Check limits for NRF"));
#endif
    check_limits_NRF = false;
    for (byte index_nrf = 0; index_nrf < NRF24LMaxModules; index_nrf++) {     //NRF module scan (index_nrf)
      if (SystemNRF24LPins[index_nrf].RNF24LAddr != 0) {                      //if module is used

        ////////////////
        //Analog NRF SCAN
        ///////////////
        //THE ANALOG LIMITS ARE SENT MULTIPLIED BY 1024

        sprintf(module_name, "NRF24L_io_%d", index_nrf);                     //used to identify the NRF module number (NRF24L_var_0, NRF24L_var_1, etc)
        for (int i = 0; i < NRF24LMaxAnalogPin; i++)   {          //NRF variable scan (i)

          if (SystemNRF24LPins[index_nrf].AnaPin[i].used) {            //find is analog is used
            lim_check_AnaVar_pin_limits(push_usr, arduino_name, module_name, "ana", SystemNRF24LPins[index_nrf].AnaPin[i].pin, SystemNRF24LPins[index_nrf].AnaPin[i].enable_limits, SystemNRF24LPins[index_nrf].AnaPin[i].max, SystemNRF24LPins[index_nrf].AnaPin[i].min, SystemNRF24LPins[index_nrf].AnaPin[i].Limit, SystemNRF24LPins[index_nrf].AnaPin[i].value, 1024);
          } //END ANA USED
        }   //END variable SCAN

        ////////////////
        //variable NRF SCAN
        ///////////////
        sprintf(module_name, "NRF24L_var_%d", index_nrf);                     //used to identify the NRF module number (NRF24L_var_0, NRF24L_var_1, etc)
        for (int i = 0; i < SystemNRF24LPins[index_nrf].var_num; i++)   {          //NRF variable scan (i)
          lim_check_AnaVar_pin_limits(push_usr, arduino_name, module_name, "var", i, SystemNRF24LPins[index_nrf].VarPin[i].enable_limits, SystemNRF24LPins[index_nrf].VarPin[i].max, SystemNRF24LPins[index_nrf].VarPin[i].min, SystemNRF24LPins[index_nrf].VarPin[i].Limit, SystemNRF24LPins[index_nrf].VarPin[i].value, 10);
        }   //END variable SCAN

        //////////////
        //IO NRF SCAN
        /////////////
        sprintf(module_name, "NRF24L_io_%d", index_nrf);                     //used to identify the NRF module number (NRF24L_var_0, NRF24L_var_1, etc)
        for (int i = 0; i < NRF24LMaxDigitalPin; i++) {

          if (SystemNRF24LPins[index_nrf].DigPin[i].used && SystemNRF24LPins[index_nrf].DigPin[i].mode == 0) {            //find digital input (in=0, out=1)
            lim_check_Dig_pin_limits (push_usr, arduino_name, module_name, "dig", SystemNRF24LPins[index_nrf].DigPin[i].pin, SystemNRF24LPins[index_nrf].DigPin[i].enable_limits, SystemNRF24LPins[index_nrf].DigPin[i].alarm, SystemNRF24LPins[index_nrf].DigPin[i].Limit, SystemNRF24LPins[index_nrf].DigPin[i].state);
          } //END IO IS INPUT
        } //END IO NRF SCAN
      } //END if module is used
    }  //END SCAN MODULES
  } // END check_limits_NRF

#endif


}
/////////////////////////////
//Check the High/Low limits
//////////////////////////////

//Return 0: no limit is reached or was before rised
//Return 1: high limit is rised
//Return 2: low limit is rised
//Return 3: limit is not more reached (reset out)

byte lim_check_HiLolimits (unsigned int enable_limits, unsigned int max_lim, unsigned int min_lim, LimitStruct &Limit, unsigned int value) {

  unsigned int  hysteresis;

  //check if bit0 or bit1 are enabled (push or out enabled)
  if (enable_limits & 0x3) {        //check if push is enabled (bit0) or out enable are enabled (bit[1])



    boolean sign;
    if (max_lim > min_lim) {
      sign = true;
      hysteresis = (max_lim - min_lim) * 0.2;
    }
    else {
      sign = false;
      hysteresis = (min_lim - max_lim) * 0.2;
    }

#if DEBUG_SERIAL_LIMITS ==1
    Serial.print(F("limit enabled, limit high_cnt:")); Serial.print(Limit.high_cnt); Serial.print(F(", limit low_cnt:")); Serial.print(Limit.low_cnt); Serial.print(F(", limit_events_cnt:")); Serial.print(Limit.events_cnt); Serial.print(F(", limit_max_events:")); Serial.print(Limit.max_events); Serial.print(F(", sign:")); Serial.print(sign); Serial.print(F(", hysteresis:")); Serial.println(hysteresis);
#endif

    ////////////////////////////////////////////////////////
    //MAX LIMIT CHECK (up 128 means high limits is reached)
    //
    if (((value > max_lim) && sign ) || ((value < max_lim) && !sign )) {


      if (Limit.high_cnt != 255)                               //lock the counter to 255 when reach the high limit different times
        Limit.high_cnt++;


      if (Limit.low_cnt > 0)  {                               //if low was set, means that the value is passing from low to high, so the output has to be reset
        Limit.low_cnt = 0;
        Limit.events_cnt = 0;
        return 3;                                                     //3 reset the output PIN
      }

      //notify the limit by push
      //first time, after 10 occurrences for max_events times
      if ((Limit.high_cnt % 20 == 0 || Limit.high_cnt == 1) && Limit.events_cnt < Limit.max_events)  {      //1, 10, 20, 30, 40 till limit_max_events
        Limit.events_cnt++;
        return 1;
      }
      //if max_events is set intentionally to zero
      //Limit.events_cnt =1 -- send push + activate out
      //Limit.events_cnt =2 -- refresh output state
      else if (Limit.max_events == 0) {
        if (Limit.events_cnt > 0)
          Limit.events_cnt = 2;
        else
          Limit.events_cnt = 1;
        return 1;
      }                                                    //SEND PUSH MAX (return value 1)

      ////////////////////////////////////////////////////////
      //MIN LIMIT CHECK
    } else if (((value < min_lim) && sign ) || ((value > min_lim) && !sign ))  {


      if (Limit.low_cnt != 255)                               //lock the counter to 255 when reach the high limit different times
        Limit.low_cnt++;


      if (Limit.high_cnt > 0)  {                               //if low was set, means that the value is passing from low to high, so the output has to be reset
        Limit.high_cnt = 0;
        Limit.events_cnt = 0;
        return 3;                                                     //3 reset the output PIN
      }
      //notify the limit by push
      if ((Limit.low_cnt % 20 == 0 || Limit.low_cnt == 1) && Limit.events_cnt  < Limit.max_events)  {      //1, 10, 20, 30, 40 till limit_max_events
        Limit.events_cnt ++;
        return 2;                                                       //SEND PUSH MAX (return value 1)
      }
      //if max_events is set intentionally to zero
      //Limit.events_cnt =1 -- send push + activate out
      //Limit.events_cnt =2 -- refresh output state
      else if (Limit.max_events == 0) {
        if (Limit.events_cnt > 0)
          Limit.events_cnt = 2;
        else
          Limit.events_cnt = 1;
        return 2;
      }

      //no limit is reached or we are exiting from a violation
    }
    else if (((value < (max_lim - hysteresis)) && (value > (min_lim + hysteresis)) && sign ) || ((value > (max_lim + hysteresis)) && (value < (min_lim - hysteresis)) && !sign )) {

      if (Limit.high_cnt > 0 || Limit.low_cnt > 0) {    //reset the OUT pin (return 3) if the limits were set but not yet reached
        Limit.high_cnt = 0;
        Limit.low_cnt = 0;
        Limit.events_cnt = 0;
        return 3;                                                       //3 reset the output PIN
      }
    }
    else {

      /*    if (Limit.high_cnt >0 || Limit.low_cnt >0) {      //reset the OUT pin (return 3) if the limits were set but not yet reached
            Limit.high_cnt = 0;
            Limit.low_cnt = 0;
            Limit.events_cnt = 0;
            return 3;                                                       //3 reset the output PIN
          }
      */
    }


  } else {
    Limit.high_cnt = 0;
    Limit.low_cnt = 0;
    Limit.events_cnt = 0;
  }

  return 0;
}



//////////////////////////////
//Check the High/Low limits
//////////////////////////////

//Return 0: no limit is reached
//Return 1: limit is reached
//Return 3: limit is not more reached (reset out)

byte lim_check_digitalAlarm (unsigned int enable_limits, bool alarm, LimitDigStruct &Limit, bool value) {

  //check if bit0 or bit1 are enabled (push or out enabled)
  if (enable_limits & 0x3) {                                                    //>0 means that the limits are enabled but not reached

#if DEBUG_SERIAL_LIMITS ==1
    Serial.print(F("Dig limit count:")); Serial.print(Limit.cnt); Serial.print(F(", limit_max_events:")); Serial.print(Limit.max_events); Serial.print(F(", events_cnt:")); Serial.println(Limit.events_cnt);
#endif


    if (value == alarm) {

      if (Limit.cnt != 255)                                           //lock the counter to 254 when reach the high limit 128 times
        Limit.cnt++;                                                  //increment the limit reach number

      //limit_max_events
      if ((Limit.cnt % 20 == 0 || Limit.cnt == 1) && Limit.events_cnt < Limit.max_events)  {
        Limit.events_cnt++;
        return 1;                                                 //SEND PUSH MAX (return value 1) and select output
      }
      //if max_events is set intentionally to zero
      //Limit.events_cnt =1 -- send push + activate out
      //Limit.events_cnt =2 -- refresh output state
      else if (Limit.max_events == 0) {
        if (Limit.events_cnt > 0)
          Limit.events_cnt = 2;
        else
          Limit.events_cnt = 1;
        return 1;
      }

    } else {
      Limit.events_cnt = 0;
      if (Limit.cnt > 0) {                                        //means that there was an overflow before but now no (so reset the output)
        Limit.cnt = 0;
        return 3;
      } else
        Limit.cnt = 0;                                          //reset the limit reached counter if no limits are reached
    }
  }
  else {
    Limit.cnt = 0;
    Limit.events_cnt = 0;
  }

  return 0;
}


//module_name = arduino_io or NRF24L_io_1 o NRF24L_var_0
//lim_type = "ana" o "var"
//factor = 10(var) or 1024(ana)
void lim_check_AnaVar_pin_limits(char *push_usr, char *arduino_name, char *module_name, char *lim_type, byte i, unsigned int enable_limits, unsigned int  max, unsigned int  min, LimitStruct & Limit, float value, unsigned int factor) {


  byte OutOfLimitsEdge;
  unsigned  int value_x_factor;

  value_x_factor = value * factor;

#if DEBUG_SERIAL_LIMITS ==1
  //Sensor: ana[3], Lim enabled:1, MAX lim:870, Value: 0.72, factor: 1024, value_x_factor:741, MIN lim:765
  Serial.print(F("module_name: ")); Serial.print(module_name); Serial.print(F(", Sensor: ")); Serial.print(lim_type); Serial.print(F("[")); Serial.print(i); Serial.print(F("]")); Serial.print(F(", Lim enabled:")); Serial.print(enable_limits); Serial.print(F(", MAX lim:")); Serial.print(max); Serial.print(F(", Value: ")); Serial.print(value, 2); Serial.print(F(", factor: ")); Serial.print(factor); Serial.print(F(", value_x_factor:")); Serial.print(value_x_factor); Serial.print(F(", MIN lim:")); Serial.println(min);
#endif
  OutOfLimitsEdge = lim_check_HiLolimits(enable_limits, max, min, Limit, value_x_factor);

#if DEBUG_SERIAL_LIMITS ==1
  //  Serial.print(F("OutOfLimitsEdge: "));Serial.println(OutOfLimitsEdge);
#endif

  //ACTIVATE OUTPUT IF LIMIT IS REACHED
  //RESET the OUTPUT is the limit is not yet reached
  if (enable_limits & 0x2 && OutOfLimitsEdge > 0) {                 //if BIT1=1, output enabled
    byte out_port = (enable_limits >> 2) & 0x3F;                    //bit 7:2 output enabled number
    //find out_port on ArduinoIO array
    short index_array = json_SearchDigitalPin(sizeof(base_digital_pins_used), ArduinoIO, out_port, OUTPUT);
    if (index_array >= 0) {
      if ((OutOfLimitsEdge == 2 && (!(enable_limits & 0x100)))        //bit 8 polarity [8]=0, low
          || (OutOfLimitsEdge == 1 && (enable_limits & 0x100)))       //bit 8 polarity [8]=1, high
        writeDig(ArduinoIO[index_array], true);                //activate output
      else if (OutOfLimitsEdge == 3)                                  //reset output
        writeDig(ArduinoIO[index_array], false);               //disactivate output
    }

#if DEBUG_SERIAL_LIMITS ==1
    bool plarity = enable_limits >> 8;
    Serial.print(F("ANA/VAR LIMIT output trigger activation by:")); Serial.print(lim_type); Serial.print(i); Serial.print(F(", enable_limits: ")); Serial.print(enable_limits, BIN); Serial.print(F(", polarity:")); Serial.print(plarity); Serial.print(F(", output activated: out")); Serial.println(out_port);
#endif

  }

  //SENS PUSH IF LIMIT IS REACHED
  //if max_events == 0, push only the first time
  if ((OutOfLimitsEdge == 1 || OutOfLimitsEdge == 2) && (enable_limits & 0x1) && ((Limit.max_events == 0 && Limit.events_cnt == 1) || Limit.max_events > 0)) {                      //BIT8=alarm polarity, BIT1=1, output enabled, BIT0=1, push enabled
#if DEBUG_SERIAL_LIMITS ==1
    Serial.print(F("Sending PUSH, OutOfLimitsEdge: ")); Serial.println(OutOfLimitsEdge);
#endif

    //push_SendPush(char* www_site, char *push_usr, char *arduino_name, char *type, char *mode, char *group, byte port, byte lim, float value)
    push_SendPush(andruino_it_ip_address, push_usr, arduino_name, "lim", lim_type, module_name , i , OutOfLimitsEdge, value, false);                  //SEND PUSH MAX/MIN andrea_push?type=limits&mode=Ana&port=1&lim=hi&value=0.11
  } //END PUSH SEND
}



//module_name = arduino_io or NRF24L_io_1 o NRF24L_var_0
//lim_type = "ana" or "var"

void lim_check_Dig_pin_limits (char *push_usr, char *arduino_name, char *module_name, char *lim_type, byte i, unsigned int enable_limits, boolean  alarm, LimitDigStruct & Limit, byte value) {


  byte OutOfLimitsEdge;

#if DEBUG_SERIAL_LIMITS ==1
  //Sensor: ana[3], Lim enabled:1, MAX lim:870, Value: 0.72, factor: 1024, value_x_factor:741, MIN lim:765
  Serial.print(F("module_name: ")); Serial.print(module_name); Serial.print(F(", Sensor: ")); Serial.print(lim_type); Serial.print(F("[")); Serial.print(i); Serial.print(F("]")); Serial.print(F(", Lim enabled:")); Serial.print(enable_limits); Serial.print(F(", alarm:")); Serial.print(alarm); Serial.print(F(", Value: ")); Serial.println(value);
#endif


  //OutOfLimitsEdge RISE when the limit is riched and after max_events times
  OutOfLimitsEdge = lim_check_digitalAlarm(enable_limits, alarm, Limit, value);


  //ACTIVATE OUTPUT IF LIMIT IS REACHED
  if (enable_limits & 0x2 && OutOfLimitsEdge > 0) {                             //BIT8=alarm polarity, BIT1=1, output enabled, BIT0=1, push enabled

    byte out_port = (enable_limits >> 2) & 0x3F;                                //bit 7:2 output enabled number

    //find out_port on ArduinoIO array
    short index_array = json_SearchDigitalPin(sizeof(base_digital_pins_used), ArduinoIO, out_port, OUTPUT);
    if (index_array >= 0) {
      if (OutOfLimitsEdge == 1)
        writeDig(ArduinoIO[index_array], true);                           //activate output
      else if (OutOfLimitsEdge == 3)
        writeDig(ArduinoIO[index_array], false);                          //deactivate output
    }

#if DEBUG_SERIAL_LIMITS ==1
    bool plarity = enable_limits >> 8;
    Serial.print(F("DIG LIMIT output trigger activation by dig")); Serial.print(i); Serial.print(F(" enable_limits: ")); Serial.print(enable_limits, BIN); Serial.print(F(", polarity:")); Serial.print(plarity); Serial.print(F(", output activated: out")); Serial.println(out_port);
#endif

  }


  //SENS PUSH IF LIMIT IS REACHED
  //if max_events == 0, push only the first time
  if (OutOfLimitsEdge == 1 && (enable_limits & 0x1) && ((Limit.max_events == 0 && Limit.events_cnt == 1) || Limit.max_events > 0)) {                      //BIT8=alarm polarity, BIT1=1, output enabled, BIT0=1, push enabled
#if DEBUG_SERIAL_LIMITS ==1
    Serial.print(F("DIG LIMIT push notification: ")); Serial.println(OutOfLimitsEdge);
#endif
    //

    push_SendPush(andruino_it_ip_address, push_usr, arduino_name, "lim", lim_type, module_name, i, OutOfLimitsEdge, value, false);              //SEND PUSH MAX/MIN andrea_push?type=limits&mode=Ana&port=1&lim=hi&value=0.11
  }
}
