#pragma once
#include <xs/Io.h>
#include <xs/Sub.h>
#include <xs/Hash.h>
#include <xs/Array.h>
#include <xs/Scalar.h>
namespace xs {
using xs::my_perl;
struct Glob : Scalar {
static Glob create (const Stash& stash, panda::string_view name, U32 flags = 0);
Glob (std::nullptr_t = nullptr) {}
Glob (SV* sv, bool policy = INCREMENT) : Scalar(sv, policy) { _validate(); }
Glob (GV* sv, bool policy = INCREMENT) : Scalar(sv, policy) {}
Glob (const Glob& oth) : Scalar(oth) {}
Glob (Glob&& oth) : Scalar(std::move(oth)) {}
Glob (const Scalar& oth) : Scalar(oth) { _validate(); }
Glob (Scalar&& oth) : Scalar(std::move(oth)) { _validate(); }
Glob (const Sv& oth) : Scalar(oth) { _validate(); }
Glob (Sv&& oth) : Scalar(std::move(oth)) { _validate(); }
Glob (const Simple&) = delete;
Glob (const Ref&) = delete;
Glob (const Array&) = delete;
Glob (const Hash&) = delete;
Glob (const Sub&) = delete;
Glob (const Io&) = delete;
Glob& operator= (SV* val) { Scalar::operator=(val); _validate(); return *this; }
Glob& operator= (GV* val) { Scalar::operator=(val); return *this; }
Glob& operator= (const Glob& oth) { Scalar::operator=(oth); return *this; }
Glob& operator= (Glob&& oth) { Scalar::operator=(std::move(oth)); return *this; }
Glob& operator= (const Scalar& oth) { Scalar::operator=(oth); _validate(); return *this; }
Glob& operator= (Scalar&& oth) { Scalar::operator=(std::move(oth)); _validate(); return *this; }
Glob& operator= (const Sv& oth) { return operator=(oth.get()); }
Glob& operator= (Sv&& oth) { Sv::operator=(std::move(oth)); _validate(); return *this; }
Glob& operator= (const Simple&) = delete;
Glob& operator= (const Ref&) = delete;
Glob& operator= (const Array&) = delete;
Glob& operator= (const Hash&) = delete;
Glob& operator= (const Sub&) = delete;
Glob& operator= (const Io&) = delete;
void set (SV* val) { Sv::operator=(val); }
operator AV* () const = delete;
operator HV* () const = delete;
operator CV* () const = delete;
operator IO* () const = delete;
operator GV* () const { return (GV*)sv; }
GV* operator->() const { return (GV*)sv; }
template <typename T = SV> panda::enable_if_one_of_t<T,SV,GV>* get () const { return (T*)sv; }
template <typename T> panda::enable_if_one_of_t<T,Scalar,Array,Hash,Sub,Io> slot () const;
Scalar scalar () const { return sv ? GvSV((GV*)sv) : nullptr; }
Array array () const { return sv ? GvAV((GV*)sv) : nullptr; }
Hash hash () const { return sv ? GvHV((GV*)sv) : nullptr; }
Sub sub () const { return sv ? GvCV((GV*)sv) : nullptr; }
Io io () const { return sv ? GvIO((GV*)sv) : nullptr; }
void slot (SV*);
void slot (AV*);
void slot (HV*);
void slot (CV*);
void slot (IO*);
void slot (const Scalar&);
void slot (const Sv& v) { slot(v.get()); }
void slot (const Array& v) { slot(v.get<AV>()); }
void slot (const Hash& v) { slot(v.get<HV>()); }
void slot (const Sub& v) { slot(v.get<CV>()); }
void slot (const Io& v) { slot(v.get<IO>()); }
void slot (GV*) = delete;
void slot (const Glob&) = delete;
void scalar (const Scalar& val) { slot(val); }
void array (const Array& val) { slot(val); }
void hash (const Hash& val) { slot(val); }
void sub (const Sub& val) { slot(val); }
void io (const Io& val) { slot(val); }
Sv get_const () const { return gv_const_sv((GV*)sv); }
panda::string_view name () const { return panda::string_view(GvNAME((GV*)sv), GvNAMELEN((GV*)sv)); }
panda::string_view effective_name () const { return panda::string_view(GvENAME((GV*)sv), GvENAMELEN((GV*)sv)); }
Stash stash () const;
Stash effective_stash () const;
private:
void _validate () {
if (!sv) return;
if (type() == SVt_PVGV) return;
if (SvROK(sv)) {
SV* val = SvRV(sv);
if (SvTYPE(val) == SVt_PVGV) {
Sv::operator=(val);
return;
}
}
if (is_undef()) return reset();
reset();
throw std::invalid_argument("SV is not a Glob or Glob reference");
}
};
template <> inline Scalar Glob::slot<Scalar> () const { return scalar(); }
template <> inline Array Glob::slot<Array> () const { return array(); }
template <> inline Hash Glob::slot<Hash> () const { return hash(); }
template <> inline Sub Glob::slot<Sub> () const { return sub(); }
template <> inline Io Glob::slot<Io> () const { return io(); }
}