#include "Sv.h"
#include "Sub.h"
#include "Simple.h"
#include <ostream>

namespace xs {

static Sv::payload_marker_t _default_marker;
Sv::payload_marker_t* Sv::default_marker() { return &_default_marker; }

const Sv Sv::undef(&PL_sv_undef);
const Sv Sv::yes(&PL_sv_yes);
const Sv Sv::no(&PL_sv_no);

MAGIC* Sv::payload_attach (void* ptr, SV* obj, const payload_marker_t* marker) {
    upgrade(SVt_PVMG);
    MAGIC* mg;
    Newx(mg, 1, MAGIC);
    mg->mg_moremagic = SvMAGIC(sv);
    SvMAGIC_set(sv, mg);
    mg->mg_virtual = const_cast<payload_marker_t*>(marker);
    mg->mg_type = PERL_MAGIC_ext;
    mg->mg_len = 0;
    mg->mg_ptr = (char*)ptr;
    mg->mg_private = 0;

    if (obj) {
        mg->mg_obj = SvREFCNT_inc_simple_NN(obj);
        mg->mg_flags = MGf_REFCOUNTED;
    } else {
        mg->mg_obj = NULL;
        mg->mg_flags = 0;
    }

    #ifdef USE_ITHREADS
      if (marker->svt_dup) mg->mg_flags |= MGf_DUP;
    #endif

    return mg;
}

void Sv::__at_perl_destroy () {
    const_cast<Sv*>(&undef)->reset();
    const_cast<Sv*>(&yes)->reset();
    const_cast<Sv*>(&no)->reset();
}

std::ostream& operator<< (std::ostream& os, const Sv& sv) {
    SV* v = sv;
    if (!v) return os << (void*)nullptr;
    switch (sv.type()) {
        case SVt_NULL: return os << "<undef>";
        case SVt_PVAV: return os << "array(" << (void*)v << ')';
        case SVt_PVHV: return os << "hash(" << (void*)v << ')';
        case SVt_PVFM: return os << "format(" << (void*)v << ')';
        default:
            STRLEN len;
            auto s = SvPV(v, len);
            return os.write(s, len);
    }
}

Sv eval (const panda::string& code) {
    Sv ret = eval_pv(code.c_str(), 0);

    auto errsv = GvSV(PL_errgv);
    if (SvTRUE(errsv)) {
        auto exc = Sv::noinc(errsv);
        GvSV(PL_errgv) = newSVpvs("");
        throw PerlRuntimeException(exc);
    }

    return ret;
}

}