#ifndef DEVICE_CTRL_BEAN_H
#define DEVICE_CTRL_BEAN_H
#include <future>
#include <shared_mutex>
#include <string>
#include <unordered_set>
#include "common/utils/protobuf_util.h"

#define BEAN_Uninitialized 0
#define BEAN_Initializing 1
#define BEAN_Available 2
#define BEAN_Destroying 3
#define BEAN_Terminated 4

namespace zsy
{
    class Bean
    {
    protected:
        std::atomic_uint8_t state;
        mutable std::shared_mutex stateMtx;
        mutable std::condition_variable_any stateChangeCV;

        void reportUninitialized() noexcept;

        void reportInitializing() noexcept;

        void reportAvailable() noexcept;

        void reportDestroying() noexcept;

        void reportTerminated() noexcept;

    public:
        virtual std::string name() const noexcept = 0;

        virtual std::string sn() const noexcept = 0;

        uint8_t getState() const noexcept;

        bool canUse() const noexcept;

        bool waitForState(uint8_t state, std::chrono::milliseconds timeout = std::chrono::milliseconds(10000)) const noexcept;

        bool waitStarted(std::chrono::milliseconds timeout = std::chrono::milliseconds(10000)) const noexcept;

        bool waitStopped(std::chrono::milliseconds timeout = std::chrono::milliseconds(10000)) const noexcept;

        template<typename P>
            requires std::invocable<P, const std::atomic_uint8_t &>
        bool waitForState(P &&predicate, std::chrono::milliseconds timeout = std::chrono::milliseconds(10000)) const noexcept
        {
            std::shared_lock lck(stateMtx);
            return stateChangeCV.wait_for(lck, timeout, [this, predicate = std::forward<P>(predicate)]
            {
                return predicate(this->state);
            });
        }

        Bean();

        Bean(const Bean &) = delete;

        Bean &operator=(const Bean &) = delete;

        Bean(Bean &&) noexcept = delete;

        Bean &operator=(Bean &&) noexcept = delete;

        virtual ~Bean() = default;
    };

    template<typename T>
    concept BeanConcept =
            std::derived_from<T, Bean> &&
            requires
            {
                { T::dependencies() } -> std::same_as<std::unordered_set<std::string> >;
            };
} // zsy

#endif //DEVICE_CTRL_BEAN_H