// 준비중인 시뮬레이터 링크 : https://wokwi.com/projects/446010214971010049
/*
 * 74HC165 Switch Input Reader - Reading Book Style
 *
 * Wiring Connections:
 * ┌─────────────────┬──────────┬─────────────────────────┐
 * │ 74HC165         │ Pin      │ Arduino Connection      │
 * ├─────────────────┼──────────┼─────────────────────────┤
 * │ Q7 (Serial Out) │ 9        │ D2                      │
 * │ CP (Clock)      │ 2        │ D3                     │
 * │ PL (Load)       │ 1        │ D4                     │
 * │ CE (Enable)     │ 15       │ GND (Always Active)     │
 * │ VCC             │ 16       │ 5V                      │
 * │ GND             │ 8        │ GND                     │
 * └─────────────────┴──────────┴─────────────────────────┘
 *
 * Switch Connections: D0~D7 -> 10kΩ Pull-up -> 5V (외부 저항 필수!)
 *                     Switch -> GND (Active Low)
 *
 * ⚠️ 주의: Arduino 내장 Pull-up(INPUT_PULLUP)은 사용 불가!
 *          74HC165는 독립된 IC이므로 외부 Pull-up 저항 필수
 */
const int PIN_DATA = 2;
const int PIN_CLOCK = 3;
const int PIN_LOAD = 4;
const unsigned long READ_INTERVAL = 100;
const int TOTAL_SWITCHES = 8;
unsigned long previous_millis = 0;
byte switch_data = 0;
byte previous_switch_data = 0xFF;
void setup()
{
    configure_hardware_pins();
    configure_initial_pin_states();
    configure_serial_communication();
    display_welcome_message();
}
void loop()
{
    if (is_time_to_read_switches())
    {
        update_current_time_stamp();
        perform_switch_reading_sequence();
    }
}
void configure_hardware_pins()
{
    pinMode(PIN_DATA, INPUT);
    pinMode(PIN_CLOCK, OUTPUT);
    pinMode(PIN_LOAD, OUTPUT);
}
void configure_initial_pin_states()
{
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_LOAD, HIGH);
}
void configure_serial_communication()
{
    Serial.begin(9600);
}
void display_welcome_message()
{
    Serial.println("74HC165 Switch Input Reader - Reading Book Style");
    Serial.println("Monitoring 8 switches");
    Serial.println("--------------------------------------------------");
}
bool is_time_to_read_switches()
{
    unsigned long current_millis = millis();
    return (current_millis - previous_millis >= READ_INTERVAL);
}
void update_current_time_stamp()
{
    previous_millis = millis();
}
void perform_switch_reading_sequence()
{
    load_parallel_data_into_register();
    read_serial_data_from_register();
    process_switch_data_if_changed();
}
void load_parallel_data_into_register()
{
    set_load_pin_low();
    wait_for_data_stabilization();
    set_load_pin_high();
}
void set_load_pin_low()
{
    digitalWrite(PIN_LOAD, LOW);
}
void wait_for_data_stabilization()
{
    delayMicroseconds(5);
}
void set_load_pin_high()
{
    digitalWrite(PIN_LOAD, HIGH);
}
void read_serial_data_from_register()
{
    switch_data = 0;
    // 74HC165는 클록 전에 비트가 준비되어 있음 (D7 먼저 출력됨)
    for (int i = 7; i >= 0; i--)
    {
        int bit = digitalRead(PIN_DATA);
        switch_data |= (bit << i);
        digitalWrite(PIN_CLOCK, HIGH);
        digitalWrite(PIN_CLOCK, LOW);
    }
}
void process_switch_data_if_changed()
{
    if (has_any_switch_state_changed())
    {
        display_all_switch_states();
        remember_current_switch_states();
    }
}
bool has_any_switch_state_changed()
{
    return (switch_data != previous_switch_data);
}
void display_all_switch_states()
{
    print_switch_states_header();
    print_each_switch_status();
    print_raw_binary_data();
    print_line_ending();
}
void print_switch_states_header()
{
    Serial.print("Switch States: ");
}
void print_each_switch_status()
{
    for (int i = 0; i < TOTAL_SWITCHES; i++)
    {
        print_single_switch_status(i);
    }
}
void print_single_switch_status(int switch_index)
{
    print_switch_label(switch_index);
    print_switch_state(switch_index);
}
void print_switch_label(int switch_index)
{
    Serial.print("SW");
    Serial.print(switch_index);
    Serial.print(":");
}
void print_switch_state(int switch_index)
{
    bool is_switch_pressed = is_switch_pressed_at_index(switch_index);
    if (is_switch_pressed)
    {
        print_switch_on_state();
    }
    else
    {
        print_switch_off_state();
    }
}
bool is_switch_pressed_at_index(int index)
{
    byte bit_value = (switch_data >> index) & 0x01;
    return !bit_value;
}
void print_switch_on_state()
{
    Serial.print("ON ");
}
void print_switch_off_state()
{
    Serial.print("OFF ");
}
void print_raw_binary_data()
{
    Serial.print(" | Raw: 0b");
    // 8비트 형식으로 출력 (앞에 0 표시)
    for (int i = 7; i >= 0; i--)
    {
        Serial.print((switch_data >> i) & 0x01);
    }
    Serial.print(" (0x");
    Serial.print(switch_data, HEX);
    Serial.print(")");
}
void print_line_ending()
{
    Serial.println();
}
void remember_current_switch_states()
{
    previous_switch_data = switch_data;
}