/*********************************************************************
 *
 * Software License Agreement (BSD License)
 *
 * Sự kiện của mission layer và hàng đợi vận chuyển chúng.
 *
 * Author: DuongTD
 *********************************************************************/
#ifndef MISSION_ADAPTERS_EVENT_H_
#define MISSION_ADAPTERS_EVENT_H_

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <memory>
#include <mutex>
#include <queue>
#include <vector>

#include <mission_adapters/mission_request.h>
#include <mission_adapters/types.h>

namespace mission_adapters
{
    enum class EventType
    {
        SUBMIT_REQUEST,
        NAV_DONE,
        NAV_FAILED,
        PAUSE,
        RESUME,
        CANCEL,
        EMERGENCY,
        CLEAR_EMERGENCY
    };

    struct Event
    {
        EventType type = EventType::SUBMIT_REQUEST;
        uint64_t sequence = 0;   ///< thứ tự phát sinh, EventBus tự gán

        /// Chỉ dùng cho NAV_DONE / NAV_FAILED: mission nào vừa kết thúc.
        MissionId mission_id = kInvalidMissionId;

        /// Chỉ dùng cho SUBMIT_REQUEST: payload chưa chuyển đổi, adapter sẽ xử lý trên thread worker.
        MissionRequest request;
    };

    /**
     * @class EventBus
     * @brief Hàng đợi sự kiện FIFO, cộng một đường out-of-band riêng cho emergency.
     *
     * **FIFO là bắt buộc, không phải lựa chọn phong cách.** Bản trước sắp xếp theo priority toàn
     * phần nên CANCEL (ưu tiên cao) luôn được xử lý trước SUBMIT (ưu tiên thấp) — phát order rồi huỷ
     * ngay thì huỷ chạy trước, order vào hàng đợi sau và robot chạy đúng cái người dùng vừa huỷ.
     * Thứ tự xử lý phải bằng thứ tự phát sinh thì quan hệ nhân quả mới còn.
     *
     * Emergency không đi theo luật đó: nó bật một cờ atomic ngay tại chỗ gọi, nên độ trễ phản ứng
     * không phụ thuộc vào việc hàng đợi đang dài bao nhiêu. Sự kiện EMERGENCY vẫn được đẩy vào hàng
     * đợi để dòng thời gian trong log không bị thủng.
     */
    class EventBus
    {
    public:
        /// @brief Đẩy một sự kiện thường vào cuối hàng đợi.
        void push(Event event);

        /**
         * @brief Đẩy sự kiện emergency: bật cờ out-of-band trước, rồi mới xếp hàng.
         *
         * Bên tiêu thụ phải gọi @ref takeEmergency trước khi xử lý sự kiện tiếp theo.
         */
        void pushEmergency(Event event);

        /// @brief Lấy sự kiện kế tiếp, chờ nếu hàng đợi rỗng. Trả false khi bus đã stop().
        bool pop(Event& event);

        /**
         * @brief Lấy và xoá cờ emergency đang treo.
         * @return true nếu có emergency chưa được xử lý.
         */
        bool takeEmergency();

        /// @brief Có emergency đang treo hay không (không xoá cờ).
        bool emergencyPending() const;

        void stop();
        void reset();

        bool empty() const
        {
            std::lock_guard<std::mutex> lock(mutex_);
            return queue_.empty();
        }

        size_t size() const
        {
            std::lock_guard<std::mutex> lock(mutex_);
            return queue_.size();
        }

        void clear()
        {
            std::lock_guard<std::mutex> lock(mutex_);
            queue_ = {};
        }

    private:
        mutable std::mutex mutex_;
        std::queue<Event> queue_;
        std::condition_variable cv_;
        bool stop_ = false;
        uint64_t next_sequence_ = 0;

        /// Ngoài hàng đợi có chủ đích: emergency không được xếp sau một hàng dài sự kiện thường.
        std::atomic<bool> emergency_pending_{false};
    };

} // namespace mission_adapters

#endif  // MISSION_ADAPTERS_EVENT_H_
