#ifndef TILE_BASE_INTERNAL_MOVE_ON_COPY_H
#define TILE_BASE_INTERNAL_MOVE_ON_COPY_H

#pragma once
#include "tile/base/internal/logging.h"
#include "tile/base/internal/meta.h"
#include "tile/base/make_unique.h"
#include <memory>

namespace tile {
template<typename T>
class MoveOnCopy {
public:
    MoveOnCopy() = delete;

    MoveOnCopy(T &&x) : value_(make_unique<T>(std::move(x))) {}

    MoveOnCopy(T &other) : value_{std::move(other)} { other.value_ = nullptr; }

    MoveOnCopy(const MoveOnCopy &other) : value_(std::move(other.value_)) { other.value_ = nullptr; }

    // MoveOnCopy &operator=(const MoveOnCopy &other) = delete;
    MoveOnCopy(MoveOnCopy &other) : value_{std::move(other.value_)} { other.value_ = nullptr; }

    MoveOnCopy(MoveOnCopy &&other) : value_{std::move(other.value_)} { other.value_ = nullptr; }

    MoveOnCopy &operator=(MoveOnCopy &other)
    {
        if (this != &other) {
            value_       = std::move(other.value_);
            other.value_ = nullptr;
        }
        return *this;
    }

    MoveOnCopy &operator=(MoveOnCopy &&other)
    {
        if (this != &other) {
            value_       = std::move(other.value_);
            other.value_ = nullptr;
        }
        return *this;
    }

    T &Ref() const
    {
        TILE_CHECK(value_ != nullptr);
        return *value_;
    }

    T &&Move() const { return std::move(Ref()); }

    T *operator->() { return &Ref(); }

    operator bool() const noexcept { return value_ != nullptr; }

    bool operator!() const noexcept { return !value_; }

private:
    mutable std::unique_ptr<T> value_;
};

template<typename T>
auto
MakeMoveOnCopy(T &&value) -> MoveOnCopy<remove_reference_t<remove_cvref_t<T>>>
{
    return MoveOnCopy<remove_reference_t<remove_cvref_t<T>>>{std::move(value)};
}
}// namespace tile

#endif// TILE_BASE_INTERNAL_MOVE_ON_COPY_H
