#pragma once
#include <memory>
#include "exception.hpp"
#include "method.hpp"
#include "pop.hpp"
#include "push.hpp"
namespace wrenbind17 {
    class Variable {
    public:
        Variable() {
        }
        Variable(const std::shared_ptr<Handle>& handle) : handle(handle) {
        }
        ~Variable() {
            reset();
        }
        Method func(const std::string& signature) {
            if (const auto ptr = handle->getVmWeak().lock()) {
                auto* h = wrenMakeCallHandle(ptr.get(), signature.c_str());
                return Method(handle, std::make_shared<Handle>(ptr, h));
            } else {
                throw RuntimeError("Invalid handle");
            }
        }
        Handle& getHandle() {
            return *handle;
        }
        const Handle& getHandle() const {
            return *handle;
        }
        operator bool() const {
            return handle.operator bool();
        }
        void reset() {
            handle.reset();
        }
    private:
        std::shared_ptr<Handle> handle;
    };
    template <> inline Variable detail::getSlot<Variable>(WrenVM* vm, const int idx) {
        validate<WrenType::WREN_TYPE_UNKNOWN>(vm, idx);
        return Variable(std::make_shared<Handle>(getSharedVm(vm), wrenGetSlotHandle(vm, idx)));
    }
    template <> inline bool detail::is<Variable>(WrenVM* vm, const int idx) {
        return wrenGetSlotType(vm, idx) == WREN_TYPE_UNKNOWN;
    }
    template <> inline void detail::PushHelper<Variable>::f(WrenVM* vm, int idx, const Variable& value) {
        wrenSetSlotHandle(value.getHandle().getVm(), idx, value.getHandle().getHandle());
    }
    template <> inline void detail::PushHelper<Variable>::f(WrenVM* vm, int idx, Variable&& value) {
        wrenSetSlotHandle(value.getHandle().getVm(), idx, value.getHandle().getHandle());
    }
    template <> inline void detail::PushHelper<const Variable>::f(WrenVM* vm, int idx, const Variable value) {
        wrenSetSlotHandle(value.getHandle().getVm(), idx, value.getHandle().getHandle());
    }
    template <> inline void detail::PushHelper<const Variable&>::f(WrenVM* vm, int idx, const Variable& value) {
        wrenSetSlotHandle(value.getHandle().getVm(), idx, value.getHandle().getHandle());
    }
    template <> inline void detail::PushHelper<Variable&>::f(WrenVM* vm, int idx, Variable& value) {
        wrenSetSlotHandle(value.getHandle().getVm(), idx, value.getHandle().getHandle());
    }
} // namespace wrenbind17