/* 
 * // file name harley_led.ino  ( for SERJ )
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 00):
 * <devgate.info эт gmail.com> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   
 * CHINGIZ THE FAIR SUN 
 * ----------------------------------------------------------------------------
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 *  Date create: 2016.12.16  
 *  Date change: 2017.01.13 10:51
 *  Date change: 2017.04.30 22:27 add level key detect
 * ----------------------------------------------------------------------------
 */
const int PIN_LED_left  = 7; 
const int PIN_LED_rigth = 8;
 
#define count_left_led_auto_off   322
#define count_rigth_led_auto_off   430

#define led_ON  0
#define led_OFF 1

typedef enum {
task_off_led_off = 0,
task_executed,
task_reset_off,
task_reset_on ,
task_off_led_on,
task_init,
} te_led_task_status;

typedef enum {
led_off 	= 0,
led_left_exe 	= 1,
led_rigth_exe 	= 2,
led_both_exe_1 	= 3,
led_both_exe_2 	= 4,
led_merry_christmas = 5,

} te_led_exe_status;

//------------------------------------

typedef struct 
{
int pin_number; 
int             time_debounce; 
unsigned long   time_press  ;
unsigned long   time_release;
unsigned long   time_last_press;
unsigned long   time_last_release;

int            press_level_detect;  

 bool            status_press;
 bool            status_release;
 bool            interrupt_press; 
 bool            interrupt_release;
 bool            need_release;
 bool            execute;

}  tdpkey;


typedef struct 
{
int             	pin_number; 
int             	invert_pin_number; 
int             	volume_off;  // may be use PWM 
int             	volume_on;   // may be use PWM 
unsigned long   	time_ON ; 
unsigned long   	time_OFF; 
int             	state; 
unsigned long   	now_timer; 
int 				step_exe;
unsigned long   	disabled_count_interrupt; 

te_led_task_status  status_led;
bool              tobe_executed;  
bool              use_invert_pin;

} td_pled;


typedef enum {
TIMER_EXE       = 0,
TIMER_DISABLED  = 1,
TIMER_RUN       = 2,
}TE_TIMER;

//------------------------------------
tdpkey key_left;
tdpkey key_rigth;

td_pled  pled_left;
td_pled  pled_rigth;

te_led_exe_status LED_STAT;

// #undef DEBUG

char debug_str[100];

//------------------------------------
TE_TIMER programm_count_down_uint32 ( unsigned long *timer , unsigned long shift )
{
    if (*timer > 0 )
    {
      if (*timer > shift )
      {
        *timer -= shift;
        return TIMER_RUN;
      }
       else
      {
         *timer = 0;
         return TIMER_EXE;
      }
    }
  return TIMER_DISABLED;
}


void hall_effect_interrupt ( void )
{
 
   if ( TIMER_EXE == programm_count_down_uint32 (&pled_left.disabled_count_interrupt, 1 ) )
   {
	   set_new_led_status ( led_off );
   }

   if ( TIMER_EXE == programm_count_down_uint32 (&pled_rigth.disabled_count_interrupt, 1 ) )
   {
  	set_new_led_status ( led_off );
   }
   
}

//------------------------------------
void setup() 
{
  // put your setup code here, to run once:
  key_left.time_debounce  = 50;
  key_rigth.time_debounce = 50;
  
  pled_left.volume_off = 1;
  pled_left.volume_on  = 0;
  
  pled_left.pin_number = 7;
  pled_left.status_led =  task_init;
  pled_left.use_invert_pin = false;
  
  pled_rigth.volume_off = 1;
  pled_rigth.volume_on  = 0;
  
  pled_rigth.pin_number = 8;
  pled_rigth.status_led =  task_init;

  pled_rigth.use_invert_pin = false;


  pled_rigth.invert_pin_number = pled_left.pin_number;
  pled_left.invert_pin_number  = pled_rigth.pin_number;
  pled_rigth.use_invert_pin = false;
  pled_left.use_invert_pin = false;

  
  
  key_left.pin_number  = 2;
  key_rigth.pin_number = 4;
  pinMode(key_left.pin_number, INPUT);
  pinMode(key_rigth.pin_number, INPUT);
  Serial.begin(9600);          //  setup serial

  led_task(&pled_left, 0);
  led_task(&pled_rigth,0);
  
   key_rigth.need_release = true;
   key_left.need_release = true;

   key_rigth.press_level_detect = LOW;
   key_left.press_level_detect = LOW;

  
  set_new_led_status (led_off);

  attachInterrupt(0, hall_effect_interrupt, RISING); 
 }
//------------------------------------
void clear_fkey_interrupt ( tdpkey *pkey )
{
pkey->interrupt_release  = false;
pkey->interrupt_press  = false;
pkey->need_release     = true;
}
//------------------------------------
   
void fKey (tdpkey *pkey, int time_elapsed )
{
  

  
  if ( digitalRead( pkey->pin_number) == pkey->press_level_detect )
  { 
   pkey->time_press      	 += time_elapsed; 
  }
  else 
  {  
   pkey->time_release += time_elapsed;
  }
  
  // кнопка была отжата
  if  ( pkey->time_release > pkey->time_debounce )
  {
	if ( pkey->status_release == false ) 
    {
		pkey->time_press =  0;
		pkey->status_release 	= true;
		pkey->status_press 		= false;
		pkey->interrupt_press 	= false;

		if  ( pkey->need_release  == false )
        {
			pkey->interrupt_release = true;
		}
		
		pkey->need_release  = false;
		pkey->time_last_release = pkey->time_release;
        pkey->time_release 		= 0;
#ifdef DEBUG   
		sprintf  ( debug_str,"key_release");  Serial.println  ( debug_str );  
#endif
    }
  }

if ( pkey->time_press > pkey->time_debounce )
{    
	if ( pkey->status_press == false ) 
    {
		pkey->status_press 		= true;

		pkey->need_release   	= false;
		pkey->time_release 		= 0;
		pkey->status_release 	= false;
		pkey->interrupt_press 	= true;
        pkey->interrupt_release = false;
		pkey->time_last_release = pkey->time_release;
        pkey->time_release 		= 0;
#ifdef DEBUG  
		sprintf  ( debug_str,"key_press");  Serial.println  ( debug_str );  
#endif
    }
}
 return;
}

void led_task ( td_pled *pled, int time_elapsed )  
{
	
 TE_TIMER  status; 
	   
 switch ( pled->status_led )
 {
 case task_executed:
 	pled->tobe_executed = true;
 break;

 case task_init:
 	pinMode(pled->pin_number,  OUTPUT);
 	digitalWrite(pled->pin_number, pled->volume_off);
	if ( pled->use_invert_pin )
	{
	digitalWrite ( pled->invert_pin_number, pled->volume_on);
	}		
	pled->tobe_executed = false;
 break;
	 
 case task_off_led_off:
 	digitalWrite(pled->pin_number, pled->volume_off);
	if ( pled->use_invert_pin )
	{
	digitalWrite ( pled->invert_pin_number, pled->volume_on);
	}		
	
	pled->tobe_executed = false;
 break;


 case task_reset_off:
 	pled->step_exe = 1;
 	digitalWrite(pled->pin_number, pled->volume_off);
	if ( pled->use_invert_pin )
	{
		digitalWrite ( pled->invert_pin_number, pled->volume_off);
	}		

	pled->now_timer = pled->time_OFF;
    time_elapsed = 0;
	
	pled->status_led = task_executed;
	pled->tobe_executed = true;
 	break;

 case task_reset_on:
 	pled->step_exe = 0;
 	digitalWrite(pled->pin_number, pled->volume_on);
	if ( pled->use_invert_pin )
	{
		digitalWrite ( pled->invert_pin_number, pled->volume_off);
	}		

	pled->now_timer = pled->time_ON;
    time_elapsed = 0;
	pled->status_led = task_executed;
	pled->tobe_executed = true;
	break;

 case task_off_led_on:
 	digitalWrite(pled->pin_number, pled->volume_on);
	if ( pled->use_invert_pin )
	{
	digitalWrite ( pled->invert_pin_number, pled->volume_off);
	}		
 	pled->tobe_executed = false;
 break;
}

if ( pled->tobe_executed )
{	  

   status = programm_count_down_uint32 (&pled->now_timer, time_elapsed);
   switch ( status )
   {
    case TIMER_EXE:
    // case TIMER_DISABLED:
    switch ( pled->step_exe )
	{
	 // led on 
	 case 0: 
		pled->now_timer = pled->time_ON;
		digitalWrite(pled->pin_number, pled->volume_on);
		pled->step_exe = 1;
		
		if ( pled->use_invert_pin )
		{
		digitalWrite ( pled->invert_pin_number, pled->volume_off);
		}			
		
	 break;

	 default:	
	 case 1: 
		pled->now_timer = pled->time_OFF;
		digitalWrite( pled->pin_number, pled->volume_off);
		pled->step_exe = 0;
		if ( pled->use_invert_pin )
		{
		digitalWrite ( pled->invert_pin_number, pled->volume_on);
		}			

	break;
	}
    break;
   }
}
  
}

 
void set_new_led_status  ( te_led_exe_status new_status )
{
if ( new_status == LED_STAT ) return;
 
#ifdef DEBUG
  sprintf  ( debug_str,"set new state[%d]" , new_status );
  Serial.println  ( debug_str ); 
#endif

switch ( new_status )
{
 default:
 case led_off: 
 pled_left.status_led  = task_off_led_off;
 pled_rigth.status_led  = task_off_led_off;
 pled_left.disabled_count_interrupt = 0;
 pled_rigth.disabled_count_interrupt = 0;

 break;

 case led_left_exe: 
 pled_left.status_led  = task_reset_on;
 pled_left.time_ON = 200;
 pled_left.time_OFF = 800;
 pled_rigth.status_led  = task_off_led_off;

 pled_rigth.disabled_count_interrupt = 0;
 pled_left.disabled_count_interrupt = count_left_led_auto_off;
 break;

 case led_rigth_exe:
 pled_left.status_led  = task_off_led_off;
 pled_rigth.status_led  = task_reset_on;
 pled_rigth.time_ON = 200;
 pled_rigth.time_OFF = 800;
 pled_rigth.disabled_count_interrupt = count_rigth_led_auto_off;
 pled_left.disabled_count_interrupt = 0;

 break;
 
 case led_both_exe_1: 
 pled_left.status_led  = task_reset_on;
 pled_left.time_ON = 200;
 pled_left.time_OFF = 1000;

 pled_rigth.status_led  = task_reset_on;
 pled_rigth.time_ON = 200;
 pled_rigth.time_OFF = 1000;
 pled_left.disabled_count_interrupt = 0;
 pled_rigth.disabled_count_interrupt = 0;
 
 break;

 case led_both_exe_2: 
 pled_left.status_led  = task_reset_on;
 pled_left.time_ON = 100;
 pled_left.time_OFF = 500;

 pled_rigth.status_led  = task_reset_on;
 pled_rigth.time_ON = 100;
 pled_rigth.time_OFF = 500;
 
 pled_left.disabled_count_interrupt = 0;
 pled_rigth.disabled_count_interrupt = 0;
 break;
 
 case led_merry_christmas: 
// pled_left.status_led  = task_reset_on;
// pled_left.time_ON = 200;
// pled_left.time_OFF = 400;
// pled_left.disabled_count_interrupt = 0;

 pled_rigth.status_led  = task_reset_on;
 pled_rigth.time_ON  = 300;
 pled_rigth.time_OFF = 150;
 pled_rigth.disabled_count_interrupt = 0;
 pled_rigth.use_invert_pin = true;

 break;
 
 }

 pled_left.tobe_executed = true;
 pled_rigth.tobe_executed = true;
  

 LED_STAT = new_status;
}

void loop() {
//------------------------------
static  unsigned long sec; 

static  unsigned long old_time; 
        unsigned long new_time; 
        unsigned long _time; 
//------------------------------
  new_time  = millis();
 _time    = new_time - old_time;
 old_time   = new_time;
//------------------------------
 fKey (&key_left , _time );
 fKey (&key_rigth, _time );
//--------------------------------
// to be executed both led 
if ((key_left.interrupt_press )&&( key_rigth.status_press))
{
   set_new_led_status ( led_both_exe_1 );
   clear_fkey_interrupt (&key_rigth);
   clear_fkey_interrupt (&key_left);
}

if ((key_rigth.interrupt_press )&&( key_left.status_press))
{
   set_new_led_status ( led_both_exe_2 );
   clear_fkey_interrupt (&key_rigth);
   clear_fkey_interrupt (&key_left);
}
//-------------------------------------------------------------
if ( key_left.interrupt_release )
{
  key_left.execute = true;
  clear_fkey_interrupt (&key_left);
 }

if ( key_rigth.interrupt_release )
{
  key_rigth.execute = true;
  clear_fkey_interrupt (&key_rigth);
}


// BMW
if ( key_rigth.interrupt_press )
{
 if ( key_rigth.time_press >  1200 )
 {
      set_new_led_status ( led_both_exe_1 );
	  clear_fkey_interrupt (&key_rigth);
 }
}


if ( key_left.interrupt_press )
{
 if ( key_left.time_press >  1200 )
 {
      set_new_led_status ( led_merry_christmas );
	  clear_fkey_interrupt (&key_left);
 }
}

//--------------------
 if ( key_left.execute )
 {
   key_left.execute = false;
   switch ( LED_STAT )
   {
    case 0:   set_new_led_status ( led_left_exe ); break;
    default:  set_new_led_status ( led_off      ); break;
   }   
 }
//--------------------
 if ( key_rigth.execute )
 {
   key_rigth.execute = false;
   switch ( LED_STAT )
   {
    case 0:   set_new_led_status ( led_rigth_exe ); break;
    default:  set_new_led_status ( led_off       ); break;
   }   
}
//--------------------
  if ( pled_left.tobe_executed == true ) 
  {
	led_task (&pled_left, _time); 
  }
//--------------------
  if ( pled_rigth.tobe_executed == true )  
  {
    led_task (&pled_rigth,_time); 
  }
//--------------------
  /*
   switch ( programm_count_down_uint32 (&sec, _time) )
   {
    case TIMER_EXE:
    case TIMER_DISABLED:
	sec = 5000;
	#ifdef DEBUG
	sprintf  ( debug_str,"----" );
	Serial.println  ( debug_str ); 
	#endif
	break;
   }
*/
}