#include <Servo.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <math.h>
constexpr int SCREEN_WIDTH = 128;
constexpr int SCREEN_HEIGHT = 64;
constexpr int OLED_RESET = -1;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Servo myServo;
constexpr int SERVO_PIN = 9;
constexpr int START_ANGLE = 0;
constexpr int END_ANGLE = 180;
constexpr int WAIT_TIME = 1000;     // 1초 대기
constexpr int MOVE_DURATION = 6000; // 이동 시간 (6초)
constexpr int CENTER_X = 64;   // 축 중심 X
constexpr int CENTER_Y = 48;   // 축 중심 Y
constexpr int ARM_LENGTH = 30; // 암 길이
constexpr int ARM_WIDTH = 8;   // 암 밑변 길이
constexpr int BODY_WIDTH = 30;
constexpr int BODY_HEIGHT = 12;
constexpr int BODY_OFFSET_Y = 10;
constexpr int SHAFT_RADIUS = 5;
int currentAngle = START_ANGLE;
unsigned long lastUpdate = 0;
unsigned long stateStartTime = 0;
constexpr int steps = END_ANGLE - START_ANGLE;
constexpr int delayPerStep = MOVE_DURATION / steps;
// 상태머신
enum State {
    WAIT_AT_0,
    MOVE_TO_180,
    WAIT_AT_180,
    MOVE_TO_0
};
State state = WAIT_AT_0;
void setup() {
    myServo.attach(SERVO_PIN);
    myServo.write(currentAngle);
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.clearDisplay();
    display.display();
    drawServo(currentAngle);
    stateStartTime = millis();
}
void drawMotorBody() {
    display.fillRect(CENTER_X - BODY_WIDTH / 2, CENTER_Y + BODY_OFFSET_Y, BODY_WIDTH, BODY_HEIGHT, SSD1306_WHITE);
}
void drawShaft() {
    display.fillCircle(CENTER_X, CENTER_Y, SHAFT_RADIUS, SSD1306_WHITE);
}
void drawArm(int angle) {
    float rad = radians(angle + 180); // 0도는 왼쪽(180도) 방향에서 시작
    float width = ARM_WIDTH;
    float height = ARM_LENGTH;
    int tipX = CENTER_X + height * cos(rad);
    int tipY = CENTER_Y + height * sin(rad);
    float sideRad1 = rad + radians(90);
    float sideRad2 = rad - radians(90);
    int baseX1 = CENTER_X + (width / 2) * cos(sideRad1);
    int baseY1 = CENTER_Y + (width / 2) * sin(sideRad1);
    int baseX2 = CENTER_X + (width / 2) * cos(sideRad2);
    int baseY2 = CENTER_Y + (width / 2) * sin(sideRad2);
    display.fillTriangle(tipX, tipY, baseX1, baseY1, baseX2, baseY2, SSD1306_WHITE);
}
void drawAngleText(int angle) {
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.print("Angle: ");
    display.print(angle);
    display.print(" deg");
}
void drawServo(int angle) {
    display.clearDisplay();
    drawMotorBody();
    drawShaft();
    drawArm(angle);
    drawAngleText(angle);
    display.display();
}
void handleWaitAt0(unsigned long now) {
    if (now - stateStartTime >= WAIT_TIME) {
        state = MOVE_TO_180;
        lastUpdate = now;
    }
}
void handleMoveTo180(unsigned long now) {
    if (now - lastUpdate >= delayPerStep) {
        lastUpdate = now;
        if (currentAngle < END_ANGLE) {
            currentAngle++;
            myServo.write(currentAngle);
            drawServo(currentAngle);
        } else {
            state = WAIT_AT_180;
            stateStartTime = now;
        }
    }
}
void handleWaitAt180(unsigned long now) {
    if (now - stateStartTime >= WAIT_TIME) {
        state = MOVE_TO_0;
        lastUpdate = now;
    }
}
void handleMoveTo0(unsigned long now) {
    if (now - lastUpdate >= delayPerStep) {
        lastUpdate = now;
        if (currentAngle > START_ANGLE) {
            currentAngle--;
            myServo.write(currentAngle);
            drawServo(currentAngle);
        } else {
            state = WAIT_AT_0;
            stateStartTime = now;
        }
    }
}
void loop() {
    unsigned long now = millis();
    switch (state) {
        case WAIT_AT_0:
            handleWaitAt0(now);
            break;
        case MOVE_TO_180:
            handleMoveTo180(now);
            break;
        case WAIT_AT_180:
            handleWaitAt180(now);
            break;
        case MOVE_TO_0:
            handleMoveTo0(now);
            break;
    }
}