#include <xs/Ref.h>
#include <xs/Glob.h>
#include <xs/Stash.h>
#include <xs/Simple.h>

namespace xs {

Glob Glob::create (const Stash& stash, panda::string_view name, U32 flags) {
    GV* gv = (GV*)newSV(0);
    flags |= GV_ADDMULTI; // suppress 'used only once' warning
    gv_init_pvn(gv, stash, name.data(), name.length(), flags);
    return gv;
}

Stash Glob::stash           () const { return GvSTASH((GV*)sv); }
Stash Glob::effective_stash () const { return GvESTASH((GV*)sv); }

static inline void _set_slot (SV** where, SV* what) {
    auto old = *where;
    *where = what;
    SvREFCNT_inc_simple_void(what);
    SvREFCNT_dec(old);
}

static inline void _set_slot (GV* where, CV* what) {
    auto old = GvCV(where);
    GvCV_set(where, what);
    SvREFCNT_inc_simple_void(what);
    SvREFCNT_dec(old);
}

void Glob::slot (SV* val) {
    GV* gv = (GV*)sv;
    if (!val || SvTYPE(val) <= SVt_PVMG) _set_slot(&GvSV(gv), val);
    else switch (SvTYPE(val)) {
        case SVt_PVCV: _set_slot(gv, (CV*)val);          break;
        case SVt_PVHV: _set_slot((SV**)&GvHV(gv),  val); break;
        case SVt_PVAV: _set_slot((SV**)&GvAV(gv),  val); break;
        case SVt_PVIO: _set_slot((SV**)&GvIOp(gv), val); break;
        default: throw std::invalid_argument("can set unsupported type to a typeglob");
    }
}

void Glob::slot (AV* val)           { _set_slot((SV**)&GvAV((GV*)sv), (SV*)val); }
void Glob::slot (HV* val)           { _set_slot((SV**)&GvHV((GV*)sv), (SV*)val); }
void Glob::slot (CV* val)           { _set_slot((GV*)sv, val); }
void Glob::slot (IO* val)           { _set_slot((SV**)&GvIOp((GV*)sv), (SV*)val); }
void Glob::slot (const Scalar& val) { _set_slot(&GvSV((GV*)sv), val); }

}