// https://wokwi.com/projects/429040929775531009
// 주석은 코딩을 바꾸는 와중에도 지우지 말것!
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
volatile long encoderPosition = 0;
volatile int lastEncoded = 0;
int menuIndex = 0; // 0:W, 1:V, 2:A
unsigned long lastBlink = 0;
bool blinkState = false;
const unsigned long blinkInterval = 500; // 0.5초
// IRAM_ATTR는 ESP32/ESP8266에서만 필요합니다.
// 아두이노 UNO, Nano 등 AVR 계열에서는 void handleEncoder()로 선언해야 합니다.
void handleEncoder()
{
    int MSB = digitalRead(ENCODER_PIN_A);
    int LSB = digitalRead(ENCODER_PIN_B);
    int encoded = (MSB << 1) | LSB;
    int sum = (lastEncoded << 2) | encoded;
    if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011)
        encoderPosition++;
    if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000)
        encoderPosition--;
    lastEncoded = encoded;
}
void drawMenu()
{
    display.clearDisplay();
    // ----- 왼쪽 공식 그림 -----
    int x_offset = -28;
    int cx = 64 + x_offset, cy = 32, r = 30;
    display.drawCircle(cx, cy, r, SSD1306_WHITE);
    display.drawLine(cx - 29, cy, cx + 29, cy, SSD1306_WHITE);
    display.drawLine(cx, cy, cx, cy + 30, SSD1306_WHITE);
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(cx - 5, cy - 20);
    display.print("V");
    display.setCursor(cx - 15, cy + 5);
    display.print("I");
    display.setCursor(cx + 7, cy + 5);
    display.print("R");
    // ----- 오른쪽 W/V/A 박스 -----
    int box_height = 20;
    int box_width = 24;
    int box_x_offset = -5;
    int text_x_offset = 7;
    int text_y_offset = 2;
    int x = SCREEN_WIDTH - box_width - 4 + box_x_offset;
    int y_top = 2;
    int y_mid = y_top + box_height;
    int y_bot = y_mid + box_height;
    // W 박스
    if (menuIndex == 0 && blinkState)
        display.fillRect(x, y_top, box_width, box_height, SSD1306_WHITE);
    else
        display.drawRect(x, y_top, box_width, box_height, SSD1306_WHITE);
    display.setTextColor((menuIndex == 0 && blinkState) ? SSD1306_BLACK : SSD1306_WHITE);
    display.setCursor(x + text_x_offset, y_top + text_y_offset);
    display.print("W");
    // V 박스
    if (menuIndex == 1 && blinkState)
        display.fillRect(x, y_mid, box_width, box_height, SSD1306_WHITE);
    else
        display.drawRect(x, y_mid, box_width, box_height, SSD1306_WHITE);
    display.setTextColor((menuIndex == 1 && blinkState) ? SSD1306_BLACK : SSD1306_WHITE);
    display.setCursor(x + text_x_offset, y_mid + text_y_offset);
    display.print("V");
    // A 박스
    if (menuIndex == 2 && blinkState)
        display.fillRect(x, y_bot, box_width, box_height, SSD1306_WHITE);
    else
        display.drawRect(x, y_bot, box_width, box_height, SSD1306_WHITE);
    display.setTextColor((menuIndex == 2 && blinkState) ? SSD1306_BLACK : SSD1306_WHITE);
    display.setCursor(x + text_x_offset, y_bot + text_y_offset);
    display.print("A");
    display.display();
    display.setTextColor(SSD1306_WHITE); // 다음 draw를 위해 복원
}
void setup()
{
    pinMode(ENCODER_PIN_A, INPUT_PULLUP);
    pinMode(ENCODER_PIN_B, INPUT_PULLUP);
    int MSB = digitalRead(ENCODER_PIN_A);
    int LSB = digitalRead(ENCODER_PIN_B);
    lastEncoded = (MSB << 1) | LSB;
    attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), handleEncoder, CHANGE);
    attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), handleEncoder, CHANGE);
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.clearDisplay();
    drawMenu();
}
void loop()
{
    static long lastPos = 0;
    if (encoderPosition != lastPos)
    {
        int diff = encoderPosition - lastPos;
        int step = diff / 4; // 엔코더 4틱마다 한 칸 이동
        if (step != 0)
        {
            menuIndex -= step;
            if (menuIndex < 0)
                menuIndex = 0;
            if (menuIndex > 2)
                menuIndex = 2;
            lastPos += step * 4; // 처리한 만큼 lastPos 갱신
            drawMenu();
        }
    }
    if (millis() - lastBlink > 500)
    {
        blinkState = !blinkState;
        lastBlink = millis();
        drawMenu();
    }
}