/***********************************************************************
 *
 *                        254/395 V A L V E S
 *
 *             with the Arduino Leonardo (Arduino Pro Micro)
 *
 *         Using ArduinoJoystickLibrary by Matthew Heironimus
 *        https://github.com/MHeironimus/ArduinoJoystickLibrary
 **********************************************************************/
#include "Joystick.h"
/*--------------------------------------------------------
  "THE BEER-WARE LICENSE" (Revision 42):
  Alex Kostyuk wrote this code. 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.
----------------------------------------------------------*/

Joystick_ Joystick;


void setup()
{
    bits_init();
    Joystick.begin(false);
}

void loop(void)
{
    static int USB_timer = 0;
    discrete_task();

    if(++USB_timer == 120)
    {
        USB_timer = 0;
        Joystick.sendState();
    }
}


//-------------------------------------------
//               Discrete task
//-------------------------------------------
#define BITS_MAXIMUM   14 /*ITEMS_MAX*/
int shift = 0;

#define FLAG_INVERTED   (1<<0)
#define FLAG_ENABLED    (1<<1)

enum
{
    PUSH_BUTTON = 0,
    TOGGLE_SWITCH,
    ROTARY_SWITCH_POS,
    ENCODER_A,
    ENCODER_B,
    SHIFT
};

typedef struct
{
    int counter;
    char signal;
    int  max;
    char flag;
    char mode;
    char pin;
    int pulse_timer;
    int pulse_timer_value;
} BITS_DESCRIPTION;

BITS_DESCRIPTION  bits[BITS_MAXIMUM];

#define input_init(x,y,z,m,t)   do {\
bits[x].counter = 0;\
bits[x].signal = 0;\
bits[x].max = y;\
bits[x].flag = (FLAG_ENABLED | FLAG_INVERTED);\
bits[x].pin = z;\
pinMode(z,INPUT_PULLUP);\
bits[x].mode = m;\
bits[x].pulse_timer_value = t;\
} while(0)

void bits_init(void)
{
    int i;
    memset(bits,0,sizeof(bits));
// buttons
#define DEBOUNCE_DELAY   300
#define PULSE_WIDTH   1000
    input_init(0, DEBOUNCE_DELAY,2,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(1, DEBOUNCE_DELAY,3,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(2, DEBOUNCE_DELAY,4,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(3, DEBOUNCE_DELAY,5,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(4, DEBOUNCE_DELAY,A0, ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(5, DEBOUNCE_DELAY,A1, ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(6, DEBOUNCE_DELAY,A2, ROTARY_SWITCH_POS, PULSE_WIDTH);

    input_init(7, DEBOUNCE_DELAY,6,  PUSH_BUTTON, 0);
    input_init(8, DEBOUNCE_DELAY,7,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(9, DEBOUNCE_DELAY,8,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(10,DEBOUNCE_DELAY,9,  ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(11,DEBOUNCE_DELAY,10, ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(12,DEBOUNCE_DELAY,11, ROTARY_SWITCH_POS, PULSE_WIDTH);
    input_init(13,DEBOUNCE_DELAY,12, ROTARY_SWITCH_POS, PULSE_WIDTH);
}

void discrete_task( void)
{
    int bit,flag;
    int bit_state;
    int i,shift;
    static int pos_254_prev = 0;
    static int pos_395_prev = 0;
    static char ini_flag = 0;


    if(ini_flag == 0)
    {
        for(i=0; i<7; i++)
        {
            if(digitalRead(bits[i].pin) == 0)
                pos_395_prev = i;
        }

        for(; i<14; i++)
        {
            if(digitalRead(bits[i].pin) == 0)
                pos_254_prev = i;
        }

        ini_flag = 1;
    }



    for(i=0; i<BITS_MAXIMUM; i++)
    {
        if(bits[i].pulse_timer)
        {
            if(--bits[i].pulse_timer == 0)
            {
                switch(bits[i].mode)
                {
                case ROTARY_SWITCH_POS:
                    Joystick.releaseButton(i);
                    Joystick.releaseButton(i+16);
                    break;

                }
            }
        }

        if(bits[i].flag & FLAG_ENABLED)
        {
            bit_state = digitalRead(bits[i].pin);

            flag = bits[i].flag;
            bit = (flag & FLAG_INVERTED) ^ bit_state;

            if(bit)
            {
                if(bits[i].counter < bits[i].max)
                {
                    if(++(bits[i].counter) >= bits[i].max)
                    {
                        if(bits[i].signal == 0)
                        {
                            //front __0__/--1--
                            switch(bits[i].mode)
                            {
                            case PUSH_BUTTON:
                                Joystick.pressButton(i);
                                if(i>=0 && i<=6) /* 395 */
                                {
                                    pos_395_prev = i;
                                }
                                if(i>=7 && i<=13) /* 254 */
                                {
                                    pos_254_prev = i;
                                }

                                break;

                            case ROTARY_SWITCH_POS:
                                if(i>=0 && i<=6) /* 395 */
                                {
                                    if(i > pos_395_prev)
                                        shift = 16;
                                    else
                                        shift = 0;
                                    pos_395_prev = i;
                                }
                                if(i>=7 && i<=13) /* 254 */
                                {
                                    if(i > pos_254_prev)
                                        shift = 16;
                                    else
                                        shift = 0;
                                    pos_254_prev = i;
                                }
                                bits[i].pulse_timer = bits[i].pulse_timer_value;
                                Joystick.pressButton(i+shift);
                                break;

                            }
                            bits[i].signal = 1;
                        }

                    }
                }
            }
            else
            {
                if(bits[i].counter > 0)
                {
                    if(--(bits[i].counter) <= 0)
                    {
                        if(bits[i].signal)
                        {
                            // edge  --1--\___0___
                            switch(bits[i].mode)
                            {
                            case PUSH_BUTTON:
                                Joystick.releaseButton(i);
                                break;
                            }
                            bits[i].signal = 0;
                        }
                    }
                }
            }
        }
    }
}