#ifndef _V8Context_h_
#define _V8Context_h_

#include <v8.h>

#include <vector>
#include <map>
#include <string>

#ifdef __cplusplus
extern "C" {
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#include "ppport.h"
}
#endif
#undef New
#undef Null
#undef do_open
#undef do_close

using namespace v8;
using namespace std;

typedef map<string, Persistent<Object> > ObjectMap;

class SimpleObjectData {
public:
    Handle<Object> object;
    long ptr;

    SimpleObjectData(Handle<Object> object_, long ptr_)
        : object(object_)
        , ptr(ptr_)
    { }
};

class SvMap {
    typedef multimap<int, SimpleObjectData*> sv_map;
    sv_map objects;

public:
    SvMap () { }

    ~SvMap() {
        for (sv_map::iterator it = objects.begin(); it != objects.end(); it++)
            delete it->second;
    }

    void add(Handle<Object> object, long ptr);
    SV* find(Handle<Object> object);
};

typedef map<int, Handle<Value> > HandleMap;

class V8Context;

class ObjectData {
public:
    V8Context* context;
    Persistent<Object> object;
    SV* sv;
    long ptr;

    ObjectData() {};
    ObjectData(V8Context* context_, Handle<Object> object_, SV* sv);
    virtual ~ObjectData();
};

class V8ObjectData : public ObjectData {
public:
    V8ObjectData(V8Context* context_, Handle<Object> object_, SV* sv_);

    static MGVTBL vtable;
    static int svt_free(pTHX_ SV*, MAGIC*);
};

class PerlObjectData : public ObjectData {
    size_t bytes;

public:
    PerlObjectData(V8Context* context_, Handle<Object> object_, SV* sv_);
    virtual ~PerlObjectData();

    virtual size_t size();
    void add_size(size_t bytes_);

    static void destroy(Persistent<Value> object, void *data);
};

typedef map<int, ObjectData*> ObjectDataMap;

class V8Context {
    public:
        V8Context(
            int time_limit = 0,
            const char* flags = NULL,
            bool enable_blessing = false,
            const char* bless_prefix = NULL
        );
        ~V8Context();

        void bind(const char*, SV*);
        void bind_ro(const char*, SV*);
        SV* eval(SV* source, SV* origin = NULL);
        bool idle_notification();
        int adjust_amount_of_external_allocated_memory(int bytes);
        void set_flags_from_string(char *str);
        void name_global(const char *str);

        Handle<Value> sv2v8(SV*);
        SV*           v82sv(Handle<Value>);

        Persistent<Context> context;

        void register_object(ObjectData* data);
        void remove_object(ObjectData* data);

        Persistent<Function> make_function;

        bool enable_wantarray;

    private:
        Handle<Value>    sv2v8(SV*, HandleMap& seen);
        SV*              v82sv(Handle<Value>, SvMap& seen);

        Handle<Value>    rv2v8(SV*, HandleMap& seen);
        Handle<Array>    av2array(AV*, HandleMap& seen, long ptr);
        Handle<Object>   hv2object(HV*, HandleMap& seen, long ptr);
        Handle<Object>   cv2function(CV*);
        Handle<String>   sv2v8str(SV* sv);
        Handle<Object>   blessed2object(SV *sv);

        SV* array2sv(Handle<Array>, SvMap& seen);
        SV* object2sv(Handle<Object>, SvMap& seen);
        SV* object2blessed(Handle<Object>);
        SV* function2sv(Handle<Function>);

        Persistent<String> string_wrap;

        void fill_prototype(Handle<Object> prototype, HV* stash);
        Handle<Object> get_prototype(SV* sv);

        ObjectMap prototypes;

        ObjectDataMap seen_perl;
        SV* seen_v8(Handle<Object> object);

        int time_limit_;
        string bless_prefix;
        bool enable_blessing;
        static int number;
};

#endif
