/*
 * 74HC595 LED Chase Effect - FSM Style
 *
 * Wiring Connections:
 * ┌─────────────────┬──────────┬─────────────────────────┐
 * │ 74HC595         │ Pin      │ Arduino Connection      │
 * ├─────────────────┼──────────┼─────────────────────────┤
 * │ DS (Data)       │ 14       │ D8                      │
 * │ SHCP (Clock)    │ 11       │ D12                     │
 * │ STCP (Latch)    │ 12       │ D11                     │
 * │ MR (Reset)      │ 10       │ 5V                      │
 * │ OE (Enable)     │ 13       │ GND                     │
 * │ VCC             │ 16       │ 5V                      │
 * │ GND             │ 8        │ GND                     │
 * └─────────────────┴──────────┴─────────────────────────┘
 *
 * LED Connections: Q0~Q7 -> 560Ω -> LED -> GND
 */
const int PIN_DATA = 8;
const int PIN_CLOCK = 12;
const int PIN_LATCH = 11;
const unsigned long INTERVAL_TIME = 500;
const int TOTAL_LEDS = 8;
enum class State
{
    LED_ON,
    LED_OFF
};
State current_state = State::LED_OFF;
unsigned long previous_millis = 0;
int current_led = 0;
void setup()
{
    initialize_hardware();
    initialize_state();
}
void loop()
{
    if (is_time_to_transition())
    {
        update_time_stamp();
        process_current_state();
    }
}
void initialize_hardware()
{
    pinMode(PIN_DATA, OUTPUT);
    pinMode(PIN_CLOCK, OUTPUT);
    pinMode(PIN_LATCH, OUTPUT);
}
void initialize_state()
{
    current_state = State::LED_OFF;
    current_led = 0;
    turn_off_all_leds();
}
bool is_time_to_transition()
{
    unsigned long current_millis = millis();
    return (current_millis - previous_millis >= INTERVAL_TIME);
}
void update_time_stamp()
{
    previous_millis = millis();
}
void process_current_state()
{
    switch (current_state)
    {
    case State::LED_ON:
        handle_led_on_state();
        break;
    case State::LED_OFF:
        handle_led_off_state();
        break;
    }
}
void handle_led_on_state()
{
    turn_off_all_leds();
    transition_to_led_off_state();
    move_to_next_led();
}
void handle_led_off_state()
{
    turn_on_current_led();
    transition_to_led_on_state();
}
void transition_to_led_on_state()
{
    current_state = State::LED_ON;
}
void transition_to_led_off_state()
{
    current_state = State::LED_OFF;
}
void turn_on_current_led()
{
    byte pattern = 1 << current_led;
    update_shift_register(pattern);
}
void turn_off_all_leds()
{
    update_shift_register(0x00);
}
void move_to_next_led()
{
    current_led++;
    if (current_led >= TOTAL_LEDS)
    {
        current_led = 0;
    }
}
void update_shift_register(byte pattern)
{
    digitalWrite(PIN_LATCH, LOW);
    shiftOut(PIN_DATA, PIN_CLOCK, MSBFIRST, pattern);
    digitalWrite(PIN_LATCH, HIGH);
}