/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of JITCraft.
 *
 * JITCraft is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 * 
 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * JITCraft. If not, see http://www.gnu.org/licenses/.
 */
#ifndef _include_jitcraft_rooting_h_
#define _include_jitcraft_rooting_h_

#include <stdlib.h>

namespace ke {

class Zone;
class Object;

template <typename T> class Local;
template <typename T> class ScopedRoot;

struct NullAddress
{
    static void * const Sentinel;
};

/*
 * Internal handles work differently from API handles. An API handle is a
 * root, whereas as an internal handle points to a rooted location. This
 * subtle difference makes handles easy for embedders, but somewhat
 * cumbersome internally.
 */
template <typename T>
class Handle
{
    T * const *thingp_;

  private:
    Handle()
      : thingp_(reinterpret_cast<T * const *>(&NullAddress::Sentinel))
    {
    }

  public:
    Handle(const Handle &other) {
        thingp_ = other.address();
    }
    template <typename S> Handle(const Handle<S> &handle) {
        typeCheck<S>();
        thingp_ = reinterpret_cast<T * const *>(handle.address());
    }
    template <typename S> inline Handle(const Local<S> &local);
    template <typename S> inline Handle(const ScopedRoot<S> &root);

    static inline Handle Null() {
        return Handle();
    }

  public:
    T *operator ->() {
        return *thingp_;
    }
    T *operator *() {
        return *thingp_;
    }
    operator T *() { return *thingp_; }
    operator bool() const {
        return thingp_ && *thingp_;
    }
    T * const *address() const {
        return thingp_;
    }

    bool operator ==(const Handle<T> &other) const {
        return (!thingp_ && !other.thingp_) ||
               *thingp_ == *other.thingp_;
    }
    bool operator !=(const Handle<T> &other) const {
        return (!thingp_ != !other.thingp_) ||
               *thingp_ != *other.thingp_;
    }

  private:
    template <typename S>
    void typeCheck() {
#ifndef NDEBUG
        T *v1 = NULL;
        S *v2 = NULL;
        v1 = v2;
        (void)v1;
#endif
    }
};

template <typename T>
class Local
{
    friend class Zone;

    Zone *zone_;
    T *thing_;
    Local<Object> *prev_;

    Local<Object> *prev() {
        return prev_;
    }
    Local<Object> *toRaw() {
        return reinterpret_cast<Local<Object>*>(this);
    }

  public:
    // These are implemented in Zone.h.
    inline explicit Local(Zone *zone, T *thing = NULL);
    inline ~Local();

    const Local &operator =(T *thing) {
        thing_ = thing;
        return *this;
    }
    T *operator ->() const {
        return operator *();
    }
    T *operator *() const {
        return thing_;
    }
    operator bool () const {
        return !!thing_;
    }
    bool operator !=(const Local &other) {
        return thing_ != other.thing_;
    }

    T **address() {
        return (T **)&thing_;
    }
    T * const *address() const {
        return (T * const *)&thing_;
    }
    T **ref() {
        return &thing_;
    }
    operator T *() {
        return thing_;
    }

    Local &operator =(const Local &other) {
        thing_ = other.thing_;
        return *this;
    }

  private:
    // Disallow heap allocation.
    void *operator new(size_t size);
    void *operator new[](size_t size);

    // Disallow copy construction.
    Local(const Local<T> &other);
};

// A ScopedRoot is a special kind of handle for storing references on the
// heap in bulk.
template <typename T>
class ScopedRoot
{
    T **thingp_;

  private:
    // This is implemented in Zone.h.
    inline T **make(T *other);

    void assign(T *other) {
        *thingp_ = other;
    }

  public:
    ScopedRoot();

    // This is implemented in Zone.h.
    inline ScopedRoot(T *thing);

    ScopedRoot &operator =(const ScopedRoot<T> &other) {
        assign(*other);
        return *this;
    }
    ScopedRoot &operator =(T *other) {
        assign(other);
        return *this;
    }
    T *operator ->() const {
        return operator *();
    }
    T *operator *() const {
        return *thingp_;
    }
    operator T *() const {
        return *thingp_;
    }
    operator bool () const {
        return !!*thingp_;
    }
    T **address() const {
        return thingp_;
    }
};

template <typename T> template <typename S> inline
Handle<T>::Handle(const Local<S> &local)
{
    typeCheck<S>();
    thingp_ = reinterpret_cast<T * const *>(local.address());
}

template <typename T> template <typename S> inline
Handle<T>::Handle(const ScopedRoot<S> &root)
{
    typeCheck<S>();
    thingp_ = reinterpret_cast<T * const *>(root.address());
}

}

#endif // _include_jitcraft_rooting_h_
