/**
 * @file     : pending_task_safety_flag
 * @created  : Wednesday Feb 14, 2024 17:55:39 CST
 * @license  : MIT
 **/

#ifndef SLED_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H
#define SLED_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H

#pragma once
#include "sled/meta/type_traits.h"
#include "sled/ref_counted_base.h"
#include "sled/scoped_refptr.h"
#include "sled/synchronization/sequence_checker.h"
#include <functional>

namespace sled {

class PendingTaskSafetyFlag final : public sled::RefCountedNonVirtual<PendingTaskSafetyFlag> {
public:
    static sled::scoped_refptr<PendingTaskSafetyFlag> Create();
    static sled::scoped_refptr<PendingTaskSafetyFlag> CreateDetached();
    static sled::scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive();

    ~PendingTaskSafetyFlag() = default;
    void SetNotAlive();
    void SetAlive();
    bool alive() const;

protected:
    explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {}

private:
    static sled::scoped_refptr<PendingTaskSafetyFlag> CreateInternal(bool alive);
    bool alive_ = true;
    SequenceChecker main_sequence_;
};

class ScopedTaskSafety final {
public:
    ScopedTaskSafety() = default;

    explicit ScopedTaskSafety(scoped_refptr<PendingTaskSafetyFlag> flag) : flag_(std::move(flag)) {}

    ~ScopedTaskSafety() { flag_->SetNotAlive(); }

    void reset(scoped_refptr<PendingTaskSafetyFlag> new_flag = PendingTaskSafetyFlag::Create())
    {
        flag_->SetNotAlive();
        flag_ = std::move(new_flag);
    }

private:
    scoped_refptr<PendingTaskSafetyFlag> flag_;
};

template<typename F, typename... Args, typename = EnableIfT<!std::is_void<InvokeResultT<F, Args...>>::value>>
std::function<InvokeResultT<F, Args...>(Args &&...)>
SafeTask(scoped_refptr<PendingTaskSafetyFlag> flag, F &&f)
{
    return [flag, f](Args &&...args) mutable -> InvokeResultT<F, Args...> {
        if (flag->alive()) { return f(std::forward<Args>(args)...); }
        return {};
    };
}

template<typename F, typename... Args, typename = EnableIfT<std::is_void<InvokeResultT<F, Args...>>::value>>
std::function<void(Args &&...)>
SafeTask(scoped_refptr<PendingTaskSafetyFlag> flag, F &&f)
{
    return [flag, f](Args &&...args) mutable -> void {
        if (flag->alive()) { f(std::forward<Args>(args)...); }
    };
}

}// namespace sled

#endif// SLED_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H
