#include <AccelStepper.h>

// Co jaki czas [ms] się uruchomić silnik
// Przykładowo dla 5min powinno być to (5UL * 60UL * 1000UL)
// Dla 10min (10UL * 60UL * 1000UL)
// Dla 12h (720UL * 60UL * 1000UL)
// Ta liczba oznacza minuty
//                     |
//                     |
//                     V
#define MOVE_INTERVAL (10000)

// Ile ms ma obracać się jeszcze silnik po wykryciu krańcówki.
#define STOP_SWITCH_DETECT_EXTRA_TIME 500

// Maksymalna prędkość silnika (kroki na sekundę)
#define MAX_SPEED 1000

// Przyspieszenie silnika podczas ruszania
#define MOTOR_ACCELERATION 1500

// Numery pinów I/O
#define LED_PIN 4
#define BUZZER_PIN 5
#define MANUAL_START_BUTTON 6
#define STOP_SWITCH 7
#define MOTOR_IN_1 8
#define MOTOR_IN_2 9
#define MOTOR_IN_3 10
#define MOTOR_IN_4 11

unsigned long lastMoveTime = 0;
bool isMoving = false;
bool isMovingManual = false;
bool lastStopSwitchState = false;

AccelStepper motor(AccelStepper::HALF4WIRE, MOTOR_IN_1, MOTOR_IN_3, MOTOR_IN_2, MOTOR_IN_4);

bool switchDebounce()
{
    static unsigned long lastSwitchStateChange = 0;
    static bool lastState = false;
    bool newState;

    if (millis() - lastSwitchStateChange < 300UL)
    {
        return lastState;
    }

    newState = digitalRead(STOP_SWITCH);

    if (lastState != newState)
    {
        lastSwitchStateChange = millis();
        lastState = newState;
    }

    return lastState;
}

void startMotor(bool buzzerEnabled)
{
    if (buzzerEnabled)
    {
        for (int i = 0; i < 1; i++)
        {
            digitalWrite(BUZZER_PIN, HIGH);
            delay(100);
            digitalWrite(BUZZER_PIN, LOW);
            delay(100);
        }
        delay(1500);
    }

    lastStopSwitchState = switchDebounce();

    motor.enableOutputs();
    motor.setCurrentPosition(0);
    motor.moveTo(50000);
    digitalWrite(LED_PIN, HIGH);
}

void setup()
{
    motor.setMaxSpeed(MAX_SPEED);
    motor.setAcceleration(MOTOR_ACCELERATION);

    pinMode(STOP_SWITCH, INPUT_PULLUP);
    pinMode(MANUAL_START_BUTTON, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    pinMode(BUZZER_PIN, OUTPUT);

    lastStopSwitchState = digitalRead(STOP_SWITCH);
    digitalWrite(LED_PIN, LOW);
    digitalWrite(BUZZER_PIN, LOW);
}

void loop()
{
    if (!isMoving && (millis() - lastMoveTime >= MOVE_INTERVAL))
    {
        isMoving = true;
        lastMoveTime = millis();

        if (!isMovingManual)
        {
            startMotor(true);
        }
    }

    if (!isMoving && !isMovingManual && !digitalRead(MANUAL_START_BUTTON))
    {
        isMovingManual = true;
        startMotor(false);
    }

    if (isMoving || isMovingManual)
    {
        motor.run();

        if ((lastStopSwitchState && !switchDebounce()) || motor.distanceToGo() == 0)
        {
            unsigned long extraTimeStart = millis();
            while (millis() - extraTimeStart < STOP_SWITCH_DETECT_EXTRA_TIME)
            {
                motor.run();
            }

            isMoving = false;
            isMovingManual = false;
            motor.disableOutputs();
            digitalWrite(LED_PIN, LOW);
        }

        lastStopSwitchState = switchDebounce();
    }
}