#pragma once
#include <Arduino.h>

/// Неблокирующий звуковой паттерн. Частоты и тайминги ниже (kSirenHighHz,
/// kSirenLowHz, kSirenSwitchMs, kBeepTriplePulseMs) — методический выбор для
/// наглядности на уроке, а не значения из даташита.
enum class Pattern : uint8_t {
    None,
    Siren,      ///< Чередование двух частот (kSirenHighHz/kSirenLowHz), пока не остановят turnOff()
    BeepTriple  ///< Три коротких писка подтверждения, затем сам выключается
};

/**
 * @brief Драйвер пассивного бузера (пьезоизлучателя без встроенного генератора).
 *
 * В отличие от активного бузера, пассивный не умеет пищать сам по себе — на
 * управляющий пин нужно подавать переменный сигнал нужной частоты. За это отвечает
 * стандартная функция Arduino Core `tone(pin, frequency)`. Все неблокирующие сигналы
 * (`turnOn()`, `playSiren()`, `playBeepTriple()`) реализованы через машину состояний
 * в update() и не используют delay().
 *
 * Исключение — beepBlocking(): она намеренно блокирующая и предназначена только для
 * коротких сигналов на старте программы (например, "устройство готово"), когда ещё
 * нечему мешать. Для сигналов во время работы основной программы используйте
 * неблокирующие методы.
 *
 * @note В этом драйвере нет isAlive(). Бузер — исполнительное устройство без канала
 * обратной связи (только tone()/digitalWrite на пин), поэтому обрыв провода или
 * неисправность самого излучателя программно не детектируются — это физическое
 * ограничение схемы, а не упущение драйвера.
 */
class PassiveBuzzer {
public:
    /**
     * @param pin Пин управления бузером (по умолчанию GPIO 15, см. WIRING.md).
     * Рекомендуется подключать через транзистор/MOSFET — см. WIRING.md.
     */
    explicit PassiveBuzzer(uint8_t pin = 15);

    /** @brief Настройка пина на выход и гарантированная тишина. Вызвать один раз в setup(). */
    void begin();

    /**
     * @brief Издать одиночный писк заданной частоты и длительности (БЛОКИРУЮЩИЙ вызов).
     * Использовать только для коротких сигналов на старте программы, см. класс-комментарий.
     * @param frequency Частота тона в Гц. 0 игнорируется (тишина) — tone(pin, 0) не имеет смысла.
     * @param durationMs Длительность в миллисекундах. 0 игнорируется.
     */
    void beepBlocking(uint16_t frequency, uint32_t durationMs);

    /**
     * @brief Включить непрерывный тон заданной частоты (неблокирующий вызов).
     * @param frequency Частота тона в Гц. 0 трактуется как выключение звука (эквивалент turnOff()).
     */
    void turnOn(uint16_t frequency);

    /** @brief Полностью выключить звук и сбросить текущий паттерн. */
    void turnOff();

    /** @brief Запустить неблокирующий паттерн сирены (чередование двух частот). */
    void playSiren();

    /** @brief Запустить неблокирующий паттерн из трёх коротких писков подтверждения. */
    void playBeepTriple();

    /** @brief Обновление машины состояний. Обязательно вызывать на каждой итерации loop(). */
    void update();

    /** @brief true, пока бузер издаёт звук (непрерывный тон или паттерн). */
    bool isActive() const { return _active; }

private:
    static constexpr uint16_t kSirenHighHz = 1200;      ///< Сирена: верхняя частота
    static constexpr uint16_t kSirenLowHz = 800;         ///< Сирена: нижняя частота
    static constexpr uint16_t kSirenSwitchMs = 150;      ///< Сирена: интервал переключения частоты
    static constexpr uint16_t kBeepTripleFreqHz = 2000;  ///< BeepTriple: частота писка
    static constexpr uint16_t kBeepTriplePulseMs = 100;  ///< BeepTriple: писк-пауза-писк-пауза-писк по 100мс

    uint8_t _pin;
    bool _active = false;
    Pattern _currentPattern = Pattern::None;

    unsigned long _lastUpdate = 0;
    uint8_t _step = 0;
};
