/* Uncomment the following FLTK_ABI_VERSION line to enable ABI breaking fixes
 * in the current patch release of FLTK. ** Use for static builds only! **
 * For more info on this macro, see: http://fltk.org/cmp.php#FLTK_ABI_VERSION
 */
#define FLTK_ABI_VERSION 10303

#include "include/Fl_pm.h"

{%for include in includes sorted:key %}
#include <{{include.key}}>{%endfor%}

HV * Fl_stash,  // For inserting stuff directly into Fl's namespace
   * Fl_export, // For inserting stuff directly into Fl's exports//
   * Fl_cache;  // For caching weak refs to widgets and other objects

void _cache(void * ptr, const char * cls) {
    _cache( (const char *) &ptr, cls );
}

void _cache(const char * ptr, const char * cls) {
    dTHX;
#if FLTK_DEBUG
    warn("Caching %s as %s...", ptr, cls);
#endif
    if (Fl_cache == NULL)
        Fl_cache =
#if FLTK_DEBUG
                gv_stashpv( "Fl::DEBUG::ClassCache", GV_ADD | GV_ADDMULTI );
#else
                newHV();
#endif

    int  _len = strlen(cls);
    SV * _cls = newSVpv(cls, _len);
    int  len_ = strlen(ptr);

    SV ** tag = hv_store( Fl_cache, ptr, len_, _cls, 0 );
#if FLTK_DEBUG
    if (SvOK(* tag) )
        warn("okay");
    else
        warn("failed");
#endif
}

const char * _cache(void * ptr) {
    return _cache( (const char *) &ptr );
}

const char * _cache(const char * ptr) {
    dTHX;
    if (! hv_exists( Fl_cache, ptr, strlen(ptr))) {
#if FLTK_DEBUG
        warn ("Cache miss on %s", ptr);
#endif
        return NULL;
    }
#if FLTK_DEBUG
    warn("Getting cache of %s...", ptr);
#endif
    int  _len = strlen(ptr);
    SV ** tag = hv_fetch( Fl_cache, ptr, _len, FALSE );
    if (tag && SvOK(* tag))
        return (const char *)SvPV_nolen(*tag);
#if FLTK_DEBUG
    warn("...failed.", ptr);
#endif
    return NULL;
}

void _delete_cache(void * ptr) {
    _delete_cache( (const char *) &ptr );
}

void _delete_cache(const char * ptr) {
    dTHX;
    if (! hv_exists( Fl_cache, ptr, strlen(ptr))) {
#if FLTK_DEBUG
        warn ("Cache miss on %s", ptr);
#endif
        return;
    }
#if FLTK_DEBUG
    warn("Deleting cache of %s...", ptr);
#endif
    int  _len = strlen(ptr);
    hv_delete( Fl_cache, ptr, _len, FALSE | G_DISCARD);
    return;
}

/*
SV * cache(const char * ptr, SV * obj) {
    dTHX;
 //warn("Exporting %s to %s", what, _tag);
    SV ** tag = hv_fetch( Fl_cache, ptr, strlen(ptr), TRUE );
    if (tag && SvOK(* tag) && SvROK(* tag ) && (SvTYPE(SvRV(*tag))) == SVt_PVAV)
        av_push((AV*)SvRV(*tag), newSVpv(what, 0));
    else {
        SV * av;
        av = (SV*) newAV( );
        av_push((AV*)av, newSVpv(what, 0));
        tag = hv_store( Fl_export, _tag, strlen(_tag), newRV_noinc(av), 0 );
    }
}
*/
void register_constant( const char * name, SV * value ) {
    dTHX;
    newCONSTSUB( Fl_stash, (char *) name, value );
}

void register_constant( const char * package, const char * name, SV * value ) {
    dTHX;
    HV * _stash  = gv_stashpv( package, TRUE );
    newCONSTSUB( _stash, (char *) name, value );
}

void export_function (const char * what, const char * _tag ) {
    dTHX;
    //warn("Exporting %s to %s", what, _tag);
    SV ** tag = hv_fetch( Fl_export, _tag, strlen(_tag), TRUE );
    if (tag && SvOK(* tag) && SvROK(* tag ) && (SvTYPE(SvRV(*tag))) == SVt_PVAV)
        av_push((AV*)SvRV(*tag), newSVpv(what, 0));
    else {
        SV * av;
        av = (SV*) newAV( );
        av_push((AV*)av, newSVpv(what, 0));
        tag = hv_store( Fl_export, _tag, strlen(_tag), newRV_noinc(av), 0 );
    }
}

void export_constant( const char * name, const char * _tag, double val ) {
    dTHX;
    SV * value;
    value = newSVnv(val);
    register_constant(name, value);
    export_function(name, _tag);
}

void set_isa(const char * klass, const char * parent) {
    dTHX;
    HV * parent_stash = gv_stashpv( parent, GV_ADD | GV_ADDMULTI );
    av_push( get_av( form( "%s::ISA", klass ), TRUE ),
             newSVpv( parent, 0 ) );
    // TODO: make this spider up the list and make deeper connections?
}

void _cb_w ( Fl_Widget * widget, void * CODE ) {
    ((Callback * ) CODE)->trigger(widget);
    return;
}

#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif

/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) STATIC XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) STATIC XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

STATIC void
croak(const char *const params);
STATIC void
croak(const char *const params) {
    PERL_ARGS_ASSERT_CROAK_XS_USAGE;
    Perl_croak_nocontext("%s", params);
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

{%for class in xsubs%}
/* package {{class.package}}; */
#define PKG_{{ class.package | replace: "::", "_" | upcase }}
// #include <FL/{%if class.widget_type%}{{class.widget_type}}{%else%}{{class.package | replace:'::','_'}}{%endif%}.H>
{%for method in class.methods %}
    {%assign function = method.name%}
{%capture cpp.function.name%}{%
    if class.package contains '::'%}XS_{{ class.package|replace:':','_' }}_{{ method.name }}{%
        else%}XS_{{ method.name | replace:':','_' }}{%
    endif%}{%endcapture%}
XS_EUPXS({{cpp.function.name}});
XS_EUPXS({{cpp.function.name}}) {
    dVAR; dXSARGS;{%for definition in method.definitions %}
{%
    assign size.req  = definition.required | size %}{%
    assign size.opt  = definition.optional | size %}{%
    unless class.package == 'Fl' %}{%
        assign is_method = 1 %}{% else %}{%
        assign is_method = 0 %}{%
    endunless%}
    {%if is_method%}{%assign size.req = size.req | plus:1 %}{%endif%}
 // is_method   = {{is_method}}
 // size.req    = {{size.req}}
 // st.position = {{st.position}}

    {%unless forloop.first%}else {%endunless%}if (items {%if size.opt%}>{%else%}={%endif%}= {{ size.req }}{% if size.opt %} && items < {{ size.req | plus: size.opt | plus: is_method }}{% endif %}{%if definition.andif %} && {{definition.andif}}{% endif %}) {
        {%if definition.required %}
        // Required
        {% for arg in definition.required %}
        {%if is_method%}{%assign st.position = forloop.index%}{%else%}{%assign st.position = forloop.index0%}{%endif%}
        // st.position = {{st.position}}
        // .index       = {{forloop.index}}
        // .index0      = {{forloop.index0}}
        // {{arg | join:' '}}
        {%case arg.0%}
        {%when 'uchar'                          %}{{arg | join: ' '}} = (unsigned char)SvUV(ST({{st.position}}));
        {%when 'const char *'                   %}{{arg | join: ' '}} = ({{arg.0}})SvPV_nolen(ST({{st.position}}));
        {%when 'SV *'                           %}{{arg | join: ' '}} = (SV *)ST({{st.position}});
        {%when 'CV *'                           %}{{arg | join: ' '}}; 	STMT_START {
                HV *st; GV *gvp;
                SV * const xsub_tmp_sv = ST({{st.position}});
                SvGETMAGIC(xsub_tmp_sv);
                {{arg.1}} = sv_2cv(xsub_tmp_sv, &st, &gvp, 0);
                if (!{{arg.1}})
                    croak("We were expecting {{arg.1}} to be a CODE reference!");
        } STMT_END;
        {%when 'AV *'                           %}{{arg | join: ' '}}; STMT_START {
            SV* const xsub_tmp_sv = ST({{st.position}});
            SvGETMAGIC(xsub_tmp_sv);
            if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVAV)
                {{arg.0}} = (AV*)SvRV(xsub_tmp_sv);
            else
                croak("{{arg.0}} is not an ARRAY reference");
            }
        } STMT_END;
        {%when 'HV *'               %}{{arg | join: ' '}}; STMT_START {
            SV* const xsub_tmp_sv = ST({{st.position}});
            SvGETMAGIC(xsub_tmp_sv);
            if (SvROK(xsub_tmp_sv) && SvTYPE(SvRV(xsub_tmp_sv)) == SVt_PVHV)
                {{arg.0}} = (HV*)SvRV(xsub_tmp_sv);
            else
                croak("{{arg.0}} is not an HASH reference");
            }
        } STMT_END;
        {%when 'Fl_Widget *' %}CTX * /*{{arg.0 | split: ' ' | first | replace: '_', '__'}}*/ {{arg.1}};
        if ( sv_isobject( ST({{st.position}}) ) || ( SvTYPE( SvRV( ST({{st.position}}) ) ) == SVt_PVMG ) ) {
            IV tmp = SvIV((SV*)SvRV(ST({{st.position}})));
            {{arg.1}} = INT2PTR(CTX * /*{{ arg.0 | split:' ' | first | replace:'_', '__'}}*/, tmp);
        }
        else
            croak("Expected object. Got a non-object?!");
        {%when 'Fl_Menu_Item *' %}CTX * /*{{arg.0 | replace: '_', | replace_first: 'Fl', 'Fl__' | replace: '\*',}}*/ {{arg.1}};
        if ( sv_isobject( ST({{st.position}}) ) || ( SvTYPE( SvRV( ST({{st.position}}) ) ) == SVt_PVMG ) ) {
            IV tmp = SvIV((SV*)SvRV(ST({{st.position}})));
            {{arg.1}} = INT2PTR(CTX */*{{ arg.0 | split:' ' | first | replace: '_', | replace_first: 'Fl', 'Fl__'}}*/, tmp);
        }
        else
            croak("Expected object. Got a non-object?!");
        {%when 'int'         %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'short'       %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'float'       %}{{arg | join: ' '}} = ({{arg.0}})SvNV(ST({{st.position}}));
        {%when 'double'      %}{{arg | join: ' '}} = ({{arg.0}})SvNV(ST({{st.position}}));
        {%when 'FILE'        %}{{arg | join: ' '}} = PerlIO_findFILE(IoIFP(sv_2io(ST({{st.position}}))));
        {%when 'PerlIO *'    %}{{arg | join: ' '}} = IoIFP(sv_2io(ST({{st.position}})));
        {%when 'Fl_Fontsize' %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Font'     %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Color'    %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Align'    %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Char'     %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_When'     %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Labeltype'  %}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%when 'Fl_Boxtype'%}{{arg | join: ' '}} = ({{arg.0}})SvIV(ST({{st.position}}));
        {%endcase%}{% endfor %}{% endif %}
    {%if size.opt %}
        // Optional{% for arg in definition.optional %}
        {%assign position = forloop.index0 %}
        // position  = {{position}}            | {{position}} + {{size.req}} = {{ position | plus: size.req}} {{ 99 | plus : 30000000 }}
        // .index       = {{forloop.index}}
        // .index0      = {{forloop.index0}}
        // size.req     = {{size.req }}
        // {{size.req | plus: position | minus:1}}
        // {{arg}}

        {{arg.0}} {{arg.1}}; // {{size.req}} | {{position}}
        if (items > {{size.req | plus: position}}) // OY! {{ size.req | plus: position | minus: 1 }}
            {{arg.1}} = {%case arg.0%}
            {%when 'bool'           %}({{arg.0}}) SvTRUE(ST({{    size.req | plus: position }}));
            {%when 'uchar'          %}(unsigned char) SvUV(ST({{  size.req | plus: position }}));
            {%when 'unsigned int'   %}(unsigned int) SvUV(ST({{   size.req | plus: position }}));
            {%when 'const char *'   %}({{arg.0}}) SvPV_nolen(ST({{size.req | plus: position }}));
            {%when 'int'            %}({{arg.0}}) SvIV(ST({{      size.req | plus: position }}));
            {%when 'short'          %}({{arg.0}}) SvIV(ST({{      size.req | plus: position }}));
            {%when 'double'         %}({{arg.0}}) SvNV(ST({{      size.req | plus: position }}));
            {%when 'float'          %}({{arg.0}}) SvNV(ST({{      size.req | plus: position }}));
            {%when 'FILE'           %} PerlIO_findFILE(IoIFP(sv_2io(ST({{size.req | plus: position }}))));
            {%when 'Fl_Color'       %}({{arg.0}}) SvIV(ST({{      size.req | plus: position }}));
            {%when 'Fl_Char'        %}({{arg.0}}) SvIV(ST({{      size.req | plus: position }}));
            {%when 'Fl_Labeltype'   %}({{arg.0}}) SvIV(ST({{      size.req | plus: position }}));
            {%else                  %}({{arg.0}}) (ST({{          size.req | plus: position }}));
            {%endcase%}
        {%if arg.2%}else
            {{arg.1}} = {{arg.2}};{%endif%}{%
    endfor %}{%endif%}
{%comment%}/standard init{%endcomment%}
        {% case function %}
            {% when 'new' %}
            {%assign class.widget_type = definition.returns | split:' ' | first %}
        CTX * ctx;
        Newx(ctx, 1, CTX);

        const char * package = (const char *) SvPV_nolen(ST(0));
        {{assignments}}

        int algo = 0;
        {%capture required_arglist %}{%for arg in definition.required%}{{arg.1}}{%unless forloop.last%}, {%endunless%}{%endfor%}{%endcapture%}
        {%capture optional_arglist %}{%for arg in definition.optional%}{{arg.1}}{%unless forloop.last%}, {%endunless%}{%endfor%}{%endcapture%}
        ctx->cp_ctx    = new WidgetSubclass<{{ definition.returns | split:' ' | first }}>( package, {{required_arglist}}{%if required_arglist and optional_arglist%}, {%endif%}{{optional_arglist}} );
        ctx->algorithm = algo;
        {
            SV * RETVALSV;
            RETVALSV = sv_newmortal();
            sv_setref_pv(RETVALSV, package /*object2package(ctx)*/, (void*)ctx);
            ST(0) = RETVALSV;
            _cache((const char *) &ctx->cp_ctx, package);
        }
        {%assign xs.return = 'XSRETURN(1);' %}
            {% else %}
            {% if is_method %}
        CTX * /*{{ class.package|replace:':','_' }}*/ self;

        if ( sv_isobject( ST(0) ) || ( SvTYPE( SvRV( ST(0) ) ) == SVt_PVMG ) ) {
            IV tmp = SvIV((SV*)SvRV(ST(0)));
            self = INT2PTR(CTX * /*{{ class.package|replace:':','_' }}*/,tmp);
        }
        else
            croak("Attempt to call {{class.package}}->{{function}}( ... ) on a non-object!");
        {%endif%}
        {{assignments}}
        {% if definition.returns and definition.returns != 'void' %}{{definition.returns}} RETVAL;{%endif%}

        {%capture args%}{%
            if definition.c_args %}{{definition.c_args}}{%
            else%}{%
                for arg in definition.required%}{{arg.1}}{%unless forloop.last%}, {%endunless%}{%
                endfor%}{%
                if definition.required and definition.optional%},{%
                endif%}{%
                for arg in definition.optional%} {{arg.1}}{%unless forloop.last%},{%endunless%}{%endfor%}{%endif
            %}{%endcapture%}
        dXSTARG;
        {% case function %}
                {% when 'DESTROY' %}
                    {% assign xs.return = 'XSRETURN_EMPTY;'%}
        if (self->cp_ctx != NULL)
            Fl::delete_widget( self->cp_ctx );
        Safefree(self);
                {% when 'draw' %}
                    {% assign xs.return = 'XSRETURN_EMPTY;'%}
        if (self->cp_ctx != NULL)
            ((WidgetSubclass<{%if class.widget_type%}{{class.widget_type}}{%else%}{{class.package | replace:'::','_'}}{%endif%}> *)self->cp_ctx)->draw( TRUE );
                {% when 'handle' %}
                    {%assign xs.return = 'XSRETURN(1);' %}
        if (self->cp_ctx != NULL)
            RETVAL = ((WidgetSubclass<{%if class.widget_type%}{{class.widget_type}}{%else%}{{class.package | replace:'::','_'}}{%endif%}> *)self->cp_ctx)->handle( TRUE, {{args}} );
                {%else%}
                    {%unless definition.returns %}
                         {%assign xs.return = 'XSRETURN_EMPTY' %}
                    {%else%}
                        {%assign xs.return = 'XSRETURN(1);' %}
                    {%endunless%}
            {% if definition.code %}
                { {{definition.code}} }
            {% else %}
                {% if definition.returns and definition.returns != 'void' %}RETVAL = {%endif%} // {{ class.package }} | {{ class.widget_type }}
                {%if is_method%}((WidgetSubclass<{%if class.widget_type%}{{class.widget_type}}{%else%}{{class.package | replace:'::','_'}}{%endif%}> *)self->cp_ctx)->{{ function }}{%
                else        %}{{ function }}{%
                endif%}( {{args}} );
            {%endif%}
            {% endcase %}
                {% if definition.returns %}
                {%case definition.returns %}
                    {%when 'void'              %}{% assign xs.return = 'XSRETURN_EMPTY;' %}
                    {%when 'double'            %}XSprePUSH; PUSHn((double)RETVAL);
                    {%when 'char *'            %}PUSHp(RETVAL, STRLEN);
                    {%when 'const char *'      %}sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
                    {%when 'unsigned int'      %}XSprePUSH; PUSHu((UV)RETVAL);
                    {%when 'bool'              %}boolSV(RETVAL);
                    {%when 'int'               %}XSprePUSH; PUSHi((IV)RETVAL);
                    {%when 'short'             %}XSprePUSH; PUSHi((IV)RETVAL);
                    {%when 'Fl_Widget *' %}
                        CTX * ctx;
                        Newx(ctx, 1, CTX);
                        ctx->cp_ctx    = (Fl_Widget *) RETVAL;
                        {
                            SV * RETVALSV;
                            RETVALSV = sv_newmortal();
                            sv_setref_pv(RETVALSV, object2package(ctx), (void*)ctx);
                            ST(0) = RETVALSV;
                        }
                    {%when 'Fl_Group *' %}
                        CTX * ctx;
                        Newx(ctx, 1, CTX);
                        ctx->cp_ctx    = (Fl_Widget *) RETVAL;
                        {
                            SV * RETVALSV;
                            RETVALSV = sv_newmortal();
                            sv_setref_pv(RETVALSV, object2package(ctx), (void*)ctx);
                            ST(0) = RETVALSV;
                        }
                    {%when 'Fl_Window *' %}
                        CTX * ctx;
                        Newx(ctx, 1, CTX);
                        ctx->cp_ctx    = (Fl_Widget *) RETVAL;
                        {
                            SV * RETVALSV;
                            RETVALSV = sv_newmortal();
                            sv_setref_pv(RETVALSV, object2package(ctx), (void*)ctx);
                            ST(0) = RETVALSV;
                        }
                    {%when 'Fl_Menu_Button *' %}
                        CTX * ctx;
                        Newx(ctx, 1, CTX);
                        ctx->cp_ctx    = (Fl_Widget *) RETVAL;
                        {
                            SV * RETVALSV;
                            RETVALSV = sv_newmortal();
                            sv_setref_pv(RETVALSV, object2package(ctx), (void*)ctx);
                            ST(0) = RETVALSV;
                        }
                    {%when 'const Fl_Menu_Item *' %}
                        CTX * ctx;
                        Newx(ctx, 1, CTX);
                        ctx->cp_ctx    = (Fl_Widget *) RETVAL;

                        {
                            SV * RETVALSV;
                            RETVALSV = sv_newmortal();
                            sv_setref_pv(RETVALSV, object2package(ctx), (void*)ctx);
                            ST(0) = RETVALSV;
                        }
                    {%when 'CV *'     %}SV * RETVALSV; RETVALSV = newRV((SV*)RETVAL); RETVALSV = sv_2mortal(RETVALSV); ST(0) = RETVALSV;
                    {%when 'AV *'     %}SV * RETVALSV; RETVALSV = newRV((SV*)RETVAL); RETVALSV = sv_2mortal(RETVALSV); ST(0) = RETVALSV;
                    {%when 'HV *'     %}SV * RETVALSV; RETVALSV = newRV((SV*)RETVAL); RETVALSV = sv_2mortal(RETVALSV); ST(0) = RETVALSV;
                    {%else %}XSprePUSH; PUSHi((IV)RETVAL); // {{definition.returns}}
                {%endcase%}
                {%endif%}
        {% endcase %}
        {{definition.cleanup}}
        {{ xs.return }};
    }{%endfor%}
    else
        croak("TODO: Improve usage info! {%if is_method%}{%if function == 'new'%}{{ class.package }}{%else%}${{class.package | split:'::' | last | downcase }}{%endif%}->{%endif%}{{ function }}( ... );");
}
{% endfor %}
#undef PKG_{{ class.package | replace: "::", "_" | upcase }}
/* /package {{class.package}};  */
{%endfor%}

#ifdef __cplusplus
extern "C"
#endif
XS_EXTERNAL(boot_Fl); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_Fl)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if (PERL_REVISION == 5 && PERL_VERSION < 9)
    char* file = (char *) __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

    /* Initialisation Section */

    /* Setup! */
    Fl_stash  = gv_stashpv( "Fl", TRUE );
    Fl_export = get_hv( "Fl::EXPORT_TAGS", TRUE );

    {
{%for class in xsubs%}
    {%for method in class.methods %}newXS_deffile( "{%if method.export%}Fl::{{method.name | split:'::' | last}}{%else%}{{class.package}}::{{method.name}}{%endif%}", {%
    if class.package contains '::'%}XS_{{ class.package| replace:':','_' }}_{{ method.name }}{%
    else                          %}XS_{{ method.name  | replace:':','_' }}{%endif%} );{%
    if method.export%}{%for tag in method.export%}
    export_function( "{{method.name | split:'::' | last}}", "{{tag}}" );{%endfor%}{%
    endif%}
    {%endfor%}
{%endfor%}
    }

    /* Inheritance! */{%for class in xsubs%}{%if class.isa %}
    set_isa( "{{class.package}}", "{{class.isa}}");{%endif%}{%endfor%}

{%for export in exports sorted:value %}
    export_constant( "{{export.key}}", "{{export.value}}", {{export.key}});{%endfor%}

    /* End of Initialisation Section */

#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}