#pragma once
#include <xs/xs.h>
#include <panda/refcnt.h>

using panda::shared_ptr;
using panda::RefCounted;

static int dcnt = 0;

static AV* clone_array (AV* av, AV* to = NULL) {
    if (!av) return NULL;
    if (!to) to = newAV();
    SV** list = AvARRAY(av);
    for (I32 i = 0; i <= AvFILLp(av); ++i) {
        SV* val = *list++;
        if (!val) continue;
        SvREFCNT_inc(val);
        av_push(to, val);
    }
    return to;
}

static HV* clone_hash (HV* hv, HV* to = NULL) {
    if (!hv) return NULL;
    if (!to) to = newHV();
    HE** list = HvARRAY(hv);
    STRLEN hvmax = HvMAX(hv);
    if (!list) return to;
    for (STRLEN i = 0; i <= hvmax; ++i) {
        for (HE* entry = list[i]; entry; entry = HeNEXT(entry)) {
            SV* val = HeVAL(entry);
            SvREFCNT_inc(val);
            hv_store(to, HeKEY(entry), HeKLEN(entry), val, HeHASH(entry));
        }
    }
    return to;
}

class MyBase {
    public:
    int val;
    MyBase (int arg) : val(arg) {}
    virtual ~MyBase () { dcnt++; }
};

class MyChild : public MyBase {
    public:
    int val2;
    MyChild (int arg1, int arg2) : val2(arg2), MyBase(arg1) {}
    virtual ~MyChild () { dcnt++; }
};

class MyOther {
    public:
    int val;
    MyOther (int arg) : val(arg) {}
    virtual ~MyOther () { dcnt++; }
};

class MixBase {
    public:
    int val;
    MixBase (int arg) : val(arg) {}
    virtual ~MixBase () { dcnt++; }
};

class MixPluginA {
    public:
    int val;
    MixPluginA () : val(0) {}
    virtual ~MixPluginA () { dcnt++; }
};

class MixPluginB {
    public:
    int val;
    MixPluginB () : val(0) {}
    virtual ~MixPluginB () { dcnt++; }
};

class Wrapper {
    public:
    MyBase* obj;
    int xval;
    Wrapper (MyBase* arg) : obj(arg), xval(0) {}
    ~Wrapper () {
        dcnt++;
        delete obj;
    }
};

class MyRefCounted : public RefCounted {
    public:
    int val;
    MyRefCounted (int val) : val(val) { }
    virtual ~MyRefCounted () {
        dcnt++;
    }
};

class MyRefCountedChild : public MyRefCounted {
    public:
    int val2;
    MyRefCountedChild (int val, int val2) : val2(val2), MyRefCounted(val) { }
    virtual ~MyRefCountedChild () {
        dcnt++;
    }
};

class MyClass {
    public:
    int val;
    MyClass (int val) : val(val) { }
    virtual ~MyClass () {
        dcnt++;
    }
};

class MyClassChild : public MyClass {
    public:
    int val2;
    MyClassChild (int val, int val2) : val2(val2), MyClass(val) { }
    virtual ~MyClassChild () {
        dcnt++;
    }
};

typedef panda::shared_ptr<MyRefCounted> MyRefCountedSP;
typedef panda::shared_ptr<MyRefCountedChild> MyRefCountedChildSP;
typedef panda::shared_ptr<MyClass> MyClassSP;
typedef panda::shared_ptr<MyClassChild> MyClassChildSP;
typedef MyRefCounted PTRMyRefCounted;
typedef MyRefCountedChild PTRMyRefCountedChild;
typedef MyRefCountedSP PTRMyRefCountedSP;
typedef MyRefCountedChildSP PTRMyRefCountedChildSP;
typedef MyClassSP PTRMyClassSP;
typedef MyClassChildSP PTRMyClassChildSP;
#ifdef CPP11X
typedef std::shared_ptr<MyClass> MyClassSSP;
typedef std::shared_ptr<MyClassChild> MyClassChildSSP;
typedef MyClassSSP PTRMyClassSSP;
typedef MyClassChildSSP PTRMyClassChildSSP;
#endif

typedef MyBase  MyOPTR;
typedef MyChild MyOPTRChild;
typedef MyBase  Wrapped;
typedef MyBase  MyBaseAV;
typedef MyBase  MyBaseHV;

static MyRefCounted* st_myrefcounted;
static MyRefCountedSP st_myrefcounted_sp;
static MyClassSP st_myclass_sp;
#ifdef CPP11X
static MyClassSSP st_myclass_ssp;
#endif
