/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2004-2012 David Anderson
 *
 * This file is part of SourcePawn.
 *
 * SourcePawn 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.
 * 
 * SourcePawn 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
 * SourcePawn. If not, see http://www.gnu.org/licenses/.
 */
#ifndef _include_sourcepawn3_api_h_
#define _include_sourcepawn3_api_h_

#include <assert.h>
#include <stddef.h>
#include "../include/keima_util.h"

namespace ke {

class Zone;
class Object;
class HandleScope;
class HeapRoot;

}

namespace Ke {

class Function;

/*
 * A Handle is a safe pointer that holds an object reference, such that it
 * can be scanned and potentially moved by the garbage collector.
 */
template <typename T>
class Handle
{
  protected:
    template <typename S> void typeCheck() {
        while (false) {
            T *v1 = NULL;
            S *v2 = NULL;
            v1 = v2;
            (void)v1;
        }
    }

  public:
    Handle()
      : obj_(NULL)
    {
    }

    template <typename S> Handle(Handle<S> other)
      : obj_(reinterpret_cast<S *>(*other))
    {
        typeCheck<S>();
    }
 
    explicit Handle(T *obj)
      : obj_(obj)
    {
    }

    T *operator ->() const {
        return obj_;
    }
    T *operator *() const {
        return obj_;
    }
    operator bool () const {
        return !!obj_;
    }

  protected:
    T *obj_;
};

/*
 * A local variable that roots an object. Locals are owned by the nearest
 * HandleScope, and as soon as that HandleScope dies, all its Locals are
 * no longer available.
 */
template <typename T>
class Local : public Handle<T>
{
  public:
    Local()
      : Handle<T>()
    {
    }

    template <typename S> Local(Local<S> other)
      : Handle<T>(reinterpret_cast<T *>(*other))
    {
        this->template typeCheck<S>();
    }

    template <typename S> Local(S *other)
      : Handle<T>(other)
    {
    }
};

/*
 * A non-local variable that outlives HandleScopes. A Root is reference-
 * counted. When its reference count reaches 0, the Root is removed.
 */
template <typename T>
class Root : public Handle<T>
{
  public:
    Root<T>()
      : Handle<T>()
    {
    }

    Root<T>(const Root<T> &other)
      : Handle<T>(reinterpret_cast<T *>(*other))
    {
        incref();
    }

    explicit Root(ke::HeapRoot *obj)
      : Handle<T>(reinterpret_cast<T *>(obj))
    {
incref();
    }

    static inline Root<T> New(Handle<T> other);

    ~Root() {
        decref();
    }

    Root<T> &operator =(const Root<T> &other) {
        decref();
        this->obj_ = other.obj_;
        incref();
        return *this;
    }

  private:
    void incref();
    void decref();
};

/*
 * A HandleScope is where Local roots are allcoated. When the HandleScope
 * is destroyed, all Handles created under it are also destroyed. HandleScopes
 * may only be allocated on the stack, in LIFO order.
 */
class HandleScope
{
  protected:
    ke::Zone *zone_;
    ke::Object **position_;
    HandleScope *prev_;

    HandleScope(ke::Zone *zone, ke::Object **position, HandleScope *prev)
      : zone_(zone),
        position_(position),
        prev_(prev)
    {
    }

  public:
    HandleScope();
    ~HandleScope();

    static ke::Object **CreateHandle(ke::Object *obj);

  private:
    // Disallow silly stuff.
    HandleScope(const HandleScope &);
    void operator =(const HandleScope &);
    void *operator new (size_t size);
    void operator delete(void *, size_t);
};

class Parameter;

// Error messages are pumped through this interface.
class IMessageReporter
{
  public:
    virtual void onOutOfMemory() {
    }
    virtual void onUncaughtException(const char *message) {
    }
    virtual void beginCompilationErrors(const char *file) {
    }
    virtual void reportCompilationError(const char *message, unsigned line, unsigned col) {
    }
    virtual void endCompilationErrors() {
    }
};

// For the purposes of error reporting, very basic information can be queried
// about the stack frame. This structure may be used to retrieve such
// information. During iteration, it is illegal to re-enter any scripted code
// or call any Keima functions that may cause garbage collection.
class IStackFrameIterator
{
  public:
    virtual ~IStackFrameIterator() { }

    // Returns true if there are more frames, false otherwise.
    virtual bool done() = 0;

    // Returns the name of the current function. This may be NULL if the
    // function is anonymous.
    virtual const char *function() = 0;

    // Returns the file of the current function. This will return an invalid
    // path for C++ functions (for example, "<native>").
    virtual const char *source() = 0;

    // Returns the line number currently executing on this frame. For C++
    // functions, this is always 0.
    virtual unsigned lineNumber() = 0;

    // Advance to the previous frame in the call stack.
    virtual void prev() = 0;
};

// A string is an immutable UTF8 buffer.
class KE_EXPORT String
{
  public:
    // Returns the length of the string, in bytes.
    unsigned length();

    // Returns the character buffer of the string, or NULL on failure. If the
    // string is interned, chars() is guaranteed to succeed.
    const char *chars();
};

// A function is an executable unit of code.
class KE_EXPORT Function
{
  public:
    // Returns the name of a function, which may be NULL. If not NULL, the
    // string is guaranteed to be interned.
    Local<String> name();
};

// A native is an ISourcePawnEngine-compatible function provided by C++.
class KE_EXPORT Native
{
  public:
    // Returns the name of a native, which is never NULL. The string is
    // guaranteed to be interned.
    Local<String> name();

    // Returns the function pointer of the native.
    void *function();

    // Sets the function pointer of the native.
    void bind(void *function);
};

// A module represents a single compiled file, and contains functions and
// global variables for that file (as well as exported types).
class KE_EXPORT Module
{
  public:
    // Returns publics that are based on forwards.
    unsigned numPublics();
    Local<Function> getPublic(unsigned index);

    // Returns natives.
    unsigned numNatives();
    Local<Native> getNative(unsigned index);

    // Sets the native identity pointer, which is passed as the first argument
    // to natives.
    void setNativeIdentity(void *ptr);
};

class Keima
{
  public:
    /*
     * Initializes the Keima engine. This must be called exactly once before any
     * other Keima functions are called. After this, it is safe to use Keima
     * on the calling thread.
     */
    static bool Initialize();

    /*
     * Shuts down the Keima engine and frees data structures on all threads.
     */
    static void Shutdown();

    /*
     * Searches the call stack and returns true if the given module has any
     * active code running. This is an O(n) algorithm.
     */
    static bool IsInCallStack(Handle<Module> module);

    /*
     * Reclaim as much memory as possible. This pauses the calling thread
     * until garbage collection completes.
     */
    static void CollectGarbage();

    /*
     * Clear any pending exception.
     */
    static void ClearPendingException();

    /*
     * Type checks a function against given parameters.
     */
    static bool CheckInvokeTypes(Handle<Function> fun, const Parameter *params, unsigned nparams);

    /*
     * Executes a function with the given parameters. This interface is
     * intended for compatibility with SourceMod's ISourcePawnEngine2 and
     * should not be used otherwise, since it is intended to support only
     * a small subset of the core language.
     */
    static bool Invoke(Handle<Function> fun, const Parameter *params, unsigned nparams, int *result);

    /*
     * Sets the global message handler through which errors will be reported
     */
    static void InstallMessageReporter(IMessageReporter *reporter);

    static IStackFrameIterator *NewStackFrameIterator();
};

/*
 * Suppresses exceptions. This is useful for calling API functions. When this
 * in scope, errors will be reported, but the exception will be cleared after.
 */
class AutoClearExceptions
{
  public:
    ~AutoClearExceptions()
    {
        Keima::ClearPendingException();
    }
};

// Argument-passing interface for Invoke().
class Parameter
{
  public:
    enum Type {
        Int,
        Float,
        IntRef,
        FloatRef,
        Array,
        CharArray
    };

    Parameter()
    {
    }

    Parameter(int i)
      : type_(Int)
    {
        u.i = i;
    }

    Parameter(int *i, bool writeback)
      : type_(IntRef),
        writeback_(writeback)
    {
        u.ip = i;
    }

    Parameter(float f)
      : type_(Float)
    {
        u.f = f;
    }

    Parameter(float *f, bool writeback)
      : type_(FloatRef),
        writeback_(writeback)
    {
        u.fp = f;
    }

    Parameter(Type type, void *buffer, size_t length, bool writeback)
      : type_(type),
        writeback_(writeback),
        length_(length)
    {
        assert(type == Array || type == CharArray);
        u.buffer = buffer;
    }

    Type type() const {
        return type_;
    }

    int i32() const {
        assert(type() == Int);
        return u.i;
    }
    float f() const {
        assert(type() == Float);
        return u.f;
    }
    int *i32p() const {
        assert(type() == IntRef);
        return u.ip;
    }
    float *fp() const {
        assert(type() == FloatRef);
        return u.fp;
    }
    bool writeback() const {
        assert(type() >= IntRef && type() <= CharArray);
        return writeback_;
    }
    size_t length() const {
        return length_;
    }
    void *buffer() const {
        assert(type() == Array || type() == CharArray);
        return u.buffer;
    }

  private:
    Type type_;
    bool writeback_;
    size_t length_;

    union {
        int i;
        float f;
        int *ip;
        float *fp;
        void *buffer;
    } u;
};

/*
 * Compiles a file and returns a Module.
 */
extern Local<Module> Compile(const char *path);

class Internals
{
    template <typename T> friend class Root;

  private:
    static ke::HeapRoot *NewHeapRoot(ke::Object **objp);
    static void IncRef(ke::HeapRoot *root);
    static void DecRef(ke::HeapRoot *root);
};

template <typename T> Root<T>
Root<T>::New(Handle<T> other)
{
    if (!other)
        return Root<T>();
    ke::Object **ptr = reinterpret_cast<ke::Object **>(*other);
    return Root<T>(Internals::NewHeapRoot(ptr));
}

template <typename T> void
Root<T>::incref()
{
    if (!*this)
        return;
    ke::HeapRoot *ptr = reinterpret_cast<ke::HeapRoot *>(**this);
    Internals::IncRef(ptr);
}

template <typename T> void
Root<T>::decref()
{
    if (!*this)
        return;
    ke::HeapRoot *ptr = reinterpret_cast<ke::HeapRoot *>(**this);
    Internals::DecRef(ptr);
}

}

#endif // _include_sourcepawn3_api_h_
