#pragma once

#include "coro_cond_var.h"

namespace NSrvKernel {

/**
 * Analogue of TSystemEvent for coroutines. Represents condition variable along with some flag.
 *
 * Notice that after first notify() this event will be marked as done permanently.
 * To be able to use this event again, call Reset() method.
 **/
class TCoroManualEvent {
public:
    /**
     * Suspends current coroutine until deadline or event occurrence.
     **/
    [[nodiscard]] int wait_until(TContExecutor* const exec, TInstant deadline) noexcept;

    /**
     * Suspends current coroutine for timeout or until event occurrence.
     **/
    [[nodiscard]] int wait_for(TContExecutor* const exec, TDuration timeout) noexcept {
        return wait_until(exec, timeout.ToDeadLine());
    }

    /**
     * Suspends current coroutine until event occurrence.
     **/
    [[nodiscard]] int wait(TContExecutor* const exec) noexcept {
        return wait_until(exec, TInstant::Max());
    }

    /**
     * Sets event as triggered and wakes up waiting coroutine if presented.
     **/
    void notify() noexcept {
        Done_ = true;
        CV_.notify();
    }

    /**
     * Resets event state to make it possible to use this event again.
     */
    void Reset() noexcept {
        Done_ = false;
    }

private:
    bool Done_ = false;
    TCoroSingleCondVar CV_;
};

/**
 * Analogue of TSystemEvent for coroutines. Represents condition variable along with some flag.
 *
 * As opposite to TCoroManualEvent, this type of event doesn't have Reset() method.
 * But after every successful wait event will be reset automaticaly.
 **/
class TCoroAutoEvent {
public:
    /**
     * Suspends current coroutine until deadline or event occurrence.
     **/
    [[nodiscard]] int wait_until(TContExecutor* const exec, TInstant deadline) noexcept {
        int ret = Event_.wait_until(exec, deadline);
        Event_.Reset();
        return ret;
    }

    /**
     * Suspends current coroutine for timeout or until event occurrence.
     **/
    [[nodiscard]] int wait_for(TContExecutor* const exec, TDuration timeout) noexcept {
        return wait_until(exec, timeout.ToDeadLine());
    }

    /**
     * Suspends current coroutine until event occurrence.
     **/
    [[nodiscard]] int wait(TContExecutor* const exec) noexcept {
        return wait_until(exec, TInstant::Max());
    }

    /**
     * Sets event as triggered and wakes up waiting coroutine if presented.
     **/
    void notify() noexcept {
        Event_.notify();
    }

private:
    TCoroManualEvent Event_;
};

}  // namespace NSrvKernel
