//Arduino forum 02_21_2022 blh64 Faraday https://forum.arduino.cc/t/onebutton-library-doubleclick/959465/30
//My_Working_OneButton_Harley_Fog_Lights_With_Police_Fine_Tuneup_1.uno
//button 4 blinks the fog lights

//Button 5 has 3 jobs short click, long press, double click
//Button 5 will have 3 jobs, a short press will toggle on or off the high beam fog lights, as you have coded.
//Button 5 the first long press will turn off all the fog lights, and a second long press will turn on just the low beam fog lights, as you have coded.
//while all the fog lights are off , no other button presses will work
//Button 5 a first double click, will turn on the police lights, and a second double click will turn off the police lights and just light the low beam fog lights, the high beams will be off.
//The low beam fog lights are always on as default.

//Declarations
#include "OneButton.h"
//===================================================================================================================================================



//=======================================================================================================================================================
//BEGIN run_police_lights_2 code
//=======================================================================================================================================================
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
// CONSTANT DEFINITION


constexpr byte LedPinsStages[] {6,7,8,9};  // portPin o---|220|---|LED|---GND



//LED pins
constexpr byte LedPinsStage1[] {6,7};         // portPin o---|220|---|LED|---GND
constexpr byte LedPinsStage3[] {8,9};  

// portPin o---|220|---|LED|---GND

//How many flashes
constexpr  int numberOfFlashs1 {6};
constexpr  int numberOfFlashs3 {6};

//set the times
constexpr unsigned long delayTimeStage1 {50};
constexpr unsigned long delayTimeStage2 {100};
constexpr unsigned long delayTimeStage3 {50};



#define OutPutTest
unsigned long OutPutTestTime {500};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct TIMER {              // has the following members
unsigned long duration;   // memory for interval time
unsigned long stamp;      // memory for actual time
bool onOff;               // control for start/stop
};
TIMER stage1 {delayTimeStage1, 0, false};
TIMER stage2 {delayTimeStage2, 0, false};
TIMER stage3 {delayTimeStage3, 0, false};

bool timerEvent (TIMER &timer) {
return (currentTime - timer.stamp >= timer.duration && timer.onOff);
}
void startTimer(TIMER &timer) {
timer.stamp = currentTime;
timer.onOff = true;
}
void resetTimer(TIMER &timer) {
timer.stamp = currentTime;
}
void stoppTimer(TIMER &timer) {
timer.onOff = false;
}
void toogleLedPin(int pin) {
digitalWrite(pin, !digitalRead(pin));
}

//=======================================================================================================================================================
//END run_police_lights_2 code
//=======================================================================================================================================================
// Setup a new OneButton push buttons on pin 4 and 5

/*
Button 4 - toggle blinking on/off
Button 5 - toggle HIGH fog ligts on, long click - toggle all lights on/off
*/

//OneButton blink_button(4, true); //original
//OneButton on_off_button(5, true); //original

OneButton button_blink_the_fog_lights(4, true, true);
OneButton switch_turn_on_the_high_beam_fog_lights(5, true, true);


//define
#define DISABLED  false
#define ENABLED  true
#define LEDon  HIGH
#define LEDoff  LOW

unsigned long currentMillis;
unsigned long previousMillis;
unsigned long on_off_long_press_start_time;

const unsigned long blinkInterval = 100;
//this is not needed on a long press because of switch_turn_on_the_high_beam_fog_lights.setPressTicks(2500); // a long press will be detected after 2500 ms
const unsigned long longPressInterval = 2500;

//LED's
const int fog_light_low_beam = 6;
const int fog_light_low_beam_indicator = 7;
const int fog_light_high_beam = 8;
const int fog_light_high_beam_indicator = 9;

//bool
bool blinking = DISABLED;
bool inLongPress = false;
bool all_the_fog_lights_are_off = DISABLED;

//enum
//enum { LIGHTS_OFF, LIGHTS_LOW_BEAM, LIGHTS_BOTH };//original
enum { LIGHTS_OFF, LIGHTS_LOW_BEAM, LIGHTS_BOTH, POLICE_LIGHTS };
int lightState;


//Police lights
bool start_police_lights_now = DISABLED;
bool Short_Button_Press_Allowed = ENABLED;

const long onDuration_1 = 250;// OFF time for LED//orig 100
const long offDuration_1 = 200;// ON time for LED //orig 500 lower this number for a faster blink

const long onDuration_2 = 250;// OFF time for LED// orig 100
const long offDuration_2 = 200;// ON time for LED//orig 500 lower this number for a faster blink


int LEDState_1 = HIGH;// initial state of LED
int LEDState_2 = LOW;// initial state of LED

long rememberTime_1 = 0;// this is used by the code
long rememberTime_2 = 0;
//end Police

//END Declarations

void setup()
{

//=======================================================================================================================================================
//BEGIN run_police_lights_2 code
//=======================================================================================================================================================   
//  https://www.learncpp.com/cpp-tutorial/for-each-loops/
for (auto Output_ : LedPinsStages) pinMode(Output_, OUTPUT);
#ifdef OutPutTest
// check outputs
//for (auto Output_ : LedPinsStages) digitalWrite(Output_, HIGH), delay(OutPutTestTime);//original
//BEGIN my add edit

////this does NOT light the 4 LED's, on startup, when Output is set to LOW, uncomment it if you wish, and then "comment out"  for (auto Output_ : LedPinsStages) digitalWrite(Output_, HIGH), delay(OutPutTestTime);
//for (auto Output_ : LedPinsStages) digitalWrite(Output_, LOW), delay(OutPutTestTime);

//this lights the 4 LED's, on startup, when Output is set to HIGH, comment it out is you wish and then "uncomment" /for (auto Output_ : LedPinsStages) digitalWrite(Output_, LOW), delay(OutPutTestTime);
for (auto Output_ : LedPinsStages) digitalWrite(Output_, HIGH), delay(OutPutTestTime);

//END my add edit
for (auto Output_ : LedPinsStages) digitalWrite(Output_, LOW), delay(OutPutTestTime);
#endif
startTimer(stage1);
//=======================================================================================================================================================
//END run_police_lights_2 code
//=======================================================================================================================================================   
 
 pinMode(fog_light_low_beam, OUTPUT);
 pinMode(fog_light_low_beam_indicator, OUTPUT);
 pinMode(fog_light_high_beam, OUTPUT);
 pinMode(fog_light_high_beam_indicator, OUTPUT);

 digitalWrite(fog_light_high_beam, LEDoff);
 digitalWrite(fog_light_high_beam_indicator, LEDoff);
 digitalWrite(fog_light_low_beam, LEDon);
 digitalWrite(fog_light_low_beam_indicator, LEDon);
 
 //turns on the low beeams default
 lightState = LIGHTS_LOW_BEAM;

 // Setup the Serial port. see http://arduino.cc/en/Serial/IfSerial
 Serial.begin(9600);
 while (!Serial) 
 {
    ; // wait for serial port to connect. Needed for Leonardo only
 }

 Serial.println("\nAway we go!\n");

 //link your buttos here
 button_blink_the_fog_lights.attachClick(blink_click);//original this is for a "latched switch" if you want one

 //begin my add or edit
 button_blink_the_fog_lights.attachLongPressStart(blink_click);//this is for a momentary-on switch the "blinking" variable gets switched on and off in the blink_click function,  blinking = !blinking;
 button_blink_the_fog_lights.attachLongPressStop(blink_click);//this is for a momentary-on switch the "blinking" variable gets switched on and off in the blink_click function, blinking = !blinking;
 //
 button_blink_the_fog_lights.setDebounceTicks(50);
 button_blink_the_fog_lights.setPressTicks(10);//set faster click response
 //end my add or edit

 //original
 switch_turn_on_the_high_beam_fog_lights.attachClick(on_off_click);//original
 switch_turn_on_the_high_beam_fog_lights.attachLongPressStart(on_off_long_press_start);//original
 switch_turn_on_the_high_beam_fog_lights.attachDuringLongPress(on_off_long_press);//original
 switch_turn_on_the_high_beam_fog_lights.attachLongPressStop(on_off_long_press_stop);//original

 //begin my add or edit
 switch_turn_on_the_high_beam_fog_lights.setDebounceTicks(50);
 switch_turn_on_the_high_beam_fog_lights.setClickTicks(150);//this 150ms will allow the doubleclick to work, any "lower" and doubleclick will not be detected
 switch_turn_on_the_high_beam_fog_lights.setPressTicks(2500); // a long press will be detected after 2500 ms
 switch_turn_on_the_high_beam_fog_lights.attachDoubleClick(doubleclick); //will use this for police lights
 //end my add or edit

} //setup


void loop()
{
 //don't forget to set start time with millis()
 currentMillis = millis();
 
 //currentTime = millis(); used for run_police_lights_2
 currentTime = millis();
 
 
 // keep watching the push buttons:
 button_blink_the_fog_lights.tick();
 switch_turn_on_the_high_beam_fog_lights.tick();

 if (blinking)
 {
    //call the function
    blink_the_fog_lights();
 }

if(start_police_lights_now)//toggled in doubleclick
{
   lightState = POLICE_LIGHTS;//set lightState to POLICE_LIGHTS
   blinking = DISABLED;
   Short_Button_Press_Allowed = DISABLED;//don't allow short clicks
   //call the function
   //run_police_lights();
   run_police_lights_2();
} 
else//police lights are off
{
   Short_Button_Press_Allowed = ENABLED;// allow short clicks
}

}//loop

//SHORT PRESS BUTTON 4 toggle on and off the high beam fog lights
void on_off_click()//button 5 short press hereh "LIGHTS_BOTH" is doing 1 job here for a "short press" high beams are OFF low beams are ON
{
 // if the high beam fog lights are off, turn them on
 // if the high beam fog lights are on, turn them off

 if(Short_Button_Press_Allowed)
 {
    Serial.println("switch_turn_on_the_high_beam_fog_lights click.");
    switch ( lightState )
    {
       case POLICE_LIGHTS:// short press do noting
       break;
    
       case LIGHTS_OFF:  // all lights are off so ignore
       break;
         
       //this is called initally from void setup() lightState = LIGHTS_LOW_BEAM;
       case LIGHTS_LOW_BEAM: // low beam fog lights are on so switch to just high beam fog lights
          digitalWrite(fog_light_high_beam, LEDon);
          digitalWrite(fog_light_high_beam_indicator, LEDon);
          digitalWrite(fog_light_low_beam, LEDon);
          digitalWrite(fog_light_low_beam_indicator, LEDon);
          lightState = LIGHTS_BOTH;
          Serial.println("high ON");
          break;

       case LIGHTS_BOTH: // both low and high beam fog lights are on so switch high off, "LIGHTS_BOTH" is doing 1 job here for the second "short press" high beams are OFF low beams are ON
          digitalWrite(fog_light_high_beam, LEDoff);
          digitalWrite(fog_light_high_beam_indicator, LEDoff);
          digitalWrite(fog_light_low_beam, LEDon);
          digitalWrite(fog_light_low_beam_indicator, LEDon);
          lightState = LIGHTS_LOW_BEAM;
          Serial.println("high OFF");
          break;
    }//switch case
 }//if
}// end on_off_click()

//LONG PRESS BUTTON 5 on the first long press turn off all the fog lights then on the second long press turn back on just the low beam fog lights
void on_off_long_press()//button 5 long press here or doubleclick
{
 // this gets called while a long press is in progress
 // if enough time has elapsed, toggle the state
 //using millis() here
 unsigned long duration = millis() - on_off_long_press_start_time;

 if ( duration >= longPressInterval && inLongPress == true )
 {
    // if the high beam fog lights are off, turn them on
    // if the high beam fog lights are on, turn them off

    Serial.println("Button 5 on_off_longPress.");
    //lightState can be used in other functions
    switch (lightState) 
    {
       
       case LIGHTS_OFF:  // all lights are off so turn on low beam fog lights
          Serial.println("long press button 5 High Beam Fog Lights Off will call LIGHTS_BOTH on the next press");
          digitalWrite(fog_light_high_beam, LEDoff);
          digitalWrite(fog_light_high_beam_indicator, LEDoff);
          digitalWrite(fog_light_low_beam, LEDon);
          digitalWrite(fog_light_low_beam_indicator, LEDon);
          all_the_fog_lights_are_off = DISABLED;
          lightState = LIGHTS_LOW_BEAM;
          Serial.println("low ON");
          break;

       case LIGHTS_LOW_BEAM: // low beam fog lights are on so switch everything off
          
  
       case LIGHTS_BOTH: // "LIGHTS_BOTH" is doing another job here for a "long press", both low and high beam fog lights are off
          Serial.println("long press button 5 ALL Fog Lights Off will call LIGHTS_OFF on the next press");
          digitalWrite(fog_light_high_beam, LEDoff);
          digitalWrite(fog_light_high_beam_indicator, LEDoff);
          digitalWrite(fog_light_low_beam, LEDoff);
          digitalWrite(fog_light_low_beam_indicator, LEDoff);
          all_the_fog_lights_are_off = ENABLED;
          lightState = LIGHTS_OFF;
          Serial.println("all OFF");
          break;
       
       case POLICE_LIGHTS:
          lightState = POLICE_LIGHTS;
          break;
          
    }//switch case
    
    inLongPress = false;//this was set true in void on_off_long_press_start(), and set false also in void on_off_long_press_stop()
 }//if
}//on_off_long_press_stop()

void on_off_long_press_start()
{
 // record time of the long press
 //begin my add edit
 //on_off_long_press_start_time = millis();//don't need millis here because of switch_turn_on_the_high_beam_fog_lights.setPressTicks(2500); // a long press will be detected after 2500 ms
 //end my add edit
 inLongPress = true;
}  //on_off_long_press_start

void on_off_long_press_stop()
{
 inLongPress = false;
}


void blink_click()
{

 //this blinking bool variable, toggles "blinking" back and forth on each run of the blink_click() function
 blinking = !blinking;

 // if we are done blinking, make sure we leave the lights on
 if ( blinking == DISABLED )//if blinking is DISABLED do nothing
 {

    Serial.println("blinking");
    Serial.println(blinking);
   if ( lightState == LIGHTS_BOTH )
   {
      Serial.println("blinking");
      Serial.println(blinking);
      digitalWrite(fog_light_high_beam, LEDon);
      digitalWrite(fog_light_high_beam_indicator, LEDon);
   }//if

     //if the value of "lightState" equals the value of "LIGHTS_HIGH_BEAM" "OR" the value of "lightState" equals the value of "LIGHTS_BOTH"
     if ( lightState == LIGHTS_LOW_BEAM || lightState == LIGHTS_BOTH )
     {
        // toggle low beams
        digitalWrite(fog_light_low_beam, LEDon);
        digitalWrite(fog_light_low_beam_indicator, LEDon);
     }//if
 }//blinking DISABLED
}//blink_click()


void blink_the_fog_lights()
{
 //if lighrState = LIGHTS_OFF bail out, do not blink
 if ( lightState == LIGHTS_OFF )
 {
    // lights are off so nothing to do
    return;
 }//if
 //using millis() here
 if (currentMillis - previousMillis >= blinkInterval)
 {
    // enough time passed yet?
    previousMillis = currentMillis; // sets the time we wait "from"
    if ( lightState == LIGHTS_BOTH )
    {
       // toggle high beams
       digitalWrite(fog_light_high_beam, !digitalRead(fog_light_high_beam)); // shortcut to toggle the LED
       digitalWrite(fog_light_high_beam_indicator, !digitalRead(fog_light_high_beam_indicator)); // shortcut to toggle the LED
    }//if

    //if the value of "lightState" equals the value of "LIGHTS_LOW_BEAM" "OR" the value of "lightState" equals the value of "LIGHTS_BOTH"
    if ( lightState == LIGHTS_LOW_BEAM || lightState == LIGHTS_BOTH )
    {
       // toggle low beams
       digitalWrite(fog_light_low_beam, !digitalRead(fog_light_low_beam)); // shortcut to toggle the LED
       digitalWrite(fog_light_low_beam_indicator, !digitalRead(fog_light_low_beam_indicator)); // shortcut to toggle the LED
    }//if
 }//if
}//blink_the_fog_lights()

// this function will be called when the button was pressed 2 times in a short timeframe.
void doubleclick()
{
 if (!all_the_fog_lights_are_off)//all the fog lights are NOT off, so continue to run the police lights
 {
    if (!start_police_lights_now)//police lights are on
    {
       start_police_lights_now = ENABLED;
       Short_Button_Press_Allowed = DISABLED;
    }
    else//police lights are off
    {
       start_police_lights_now = DISABLED; 
       Short_Button_Press_Allowed = ENABLED;
       if (lightState = POLICE_LIGHTS)
       {
          digitalWrite(fog_light_high_beam, LEDoff);
          digitalWrite(fog_light_high_beam_indicator, LEDoff);
          digitalWrite(fog_light_low_beam, LEDon);
          digitalWrite(fog_light_low_beam_indicator, LEDon); 
          lightState = LIGHTS_LOW_BEAM;//go back to short press LIGHTS_LOW_BEAM
       } //if
    }//else
 } //all_the_fog_lights_are_off
} //end  doubleclick


//Police
void run_police_lights()
{
 if(start_police_lights_now)
 {  
    Serial.println("the police lights are on"); 
    //  LED blink with millis()

    if( LEDState_1 == HIGH )
    {
       if( (millis() - rememberTime_1) >= onDuration_1)
       {   
          LEDState_1 = LOW;// change the state of LED
          rememberTime_1 = millis();// remember Current millis() time
       }//if
 }
 else
 {   
    if( (millis()- rememberTime_1) >= offDuration_1)
    {     
       LEDState_1 = HIGH;// change the state of LED
       rememberTime_1 = millis();// remember Current millis() time
    }//if else
 }//if 

 //  LED blink with millis()
 digitalWrite(fog_light_low_beam,LEDState_1);// turn the LED ON or OFF
 digitalWrite(fog_light_low_beam_indicator,LEDState_1);// turn the LED ON or OFF

 if( LEDState_2 ==LOW )
 {
    if( (millis() - rememberTime_2) >= onDuration_2)
    {   
       LEDState_2 = HIGH;// change the state of LED
       rememberTime_2 = millis();// remember Current millis() time
    }
 
    }
    else
    {   
       if( (millis()- rememberTime_2) >= offDuration_2)
       {     
          LEDState_2 = LOW;// change the state of LED
          rememberTime_2 = millis();// remember Current millis() time
       }//if
    }//if else

    //  LED blink with millis()
    digitalWrite(fog_light_high_beam,LEDState_2);// turn the LED ON or OFF
    digitalWrite(fog_light_high_beam_indicator,LEDState_2);// turn the LED ON or OFF
 }
 else
 {
    if (!start_police_lights_now)
    {
       digitalWrite(fog_light_high_beam,LEDoff);
       digitalWrite(fog_light_high_beam_indicator,LEDoff);
       Short_Button_Press_Allowed = ENABLED;
    }//if
 }//if else

} //end void


void run_police_lights_2()
{
 
 if(start_police_lights_now)
 {  
    if (timerEvent(stage1))
    {
       for (auto LedPinStage1 : LedPinsStage1) toogleLedPin(LedPinStage1);
       resetTimer(stage1);
       static int counter=0;
       if(!(++counter%=numberOfFlashs1<<1)) startTimer(stage2),stoppTimer(stage1);
       }
          if (timerEvent(stage2))
          {
             startTimer(stage3),stoppTimer(stage2);
          }
          if (timerEvent(stage3))
          {
             for (auto LedPinStage3 : LedPinsStage3) toogleLedPin(LedPinStage3);
             resetTimer(stage3);
             static int counter=0;
             if(!(++counter%=numberOfFlashs3<<1)) startTimer(stage1),stoppTimer(stage3);
             } 
             }
             else
             {
                if (!start_police_lights_now)
                {
                   digitalWrite(fog_light_high_beam,LEDoff);
                   digitalWrite(fog_light_high_beam_indicator,LEDoff);
                   Short_Button_Press_Allowed = ENABLED;
                }//if
             }//else
 
}//run_police_lights_2