#ifndef _TILE_BASE_DEFERED_H
#define _TILE_BASE_DEFERED_H

#pragma once
#include <functional>
#include <utility>

namespace tile {

class ScopedDeferred {
public:
    template<typename F>
    explicit ScopedDeferred(F &&f) : action_(std::move(f))
    {}

    ~ScopedDeferred() { action_(); }

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

private:
    std::function<void()> action_;
};

class Deferred {
public:
    Deferred() = default;

    template<typename F>
    explicit Deferred(F &&f) : action_(std::forward<F>(f))
    {}

    Deferred(Deferred &&other) noexcept : action_(std::move(other.action_)) { other.action_ = nullptr; }

    Deferred &operator=(Deferred &&other) noexcept
    {
        if (&other == this) { return *this; }

        Fire();
        action_       = std::move(other.action_);
        other.action_ = nullptr;
        return *this;
    }

    ~Deferred()
    {
        if (action_) { action_(); }
    }

    explicit operator bool() const noexcept { return !!action_; }

    void Fire() noexcept
    {
        if (auto op = std::move(action_)) { op(); }
    }

    void Dimiss() noexcept { action_ = nullptr; }

private:
    std::function<void()> action_;
};

}// namespace tile

#endif// _TILE_BASE_DEFERED_H
