#include "xh_config.h"
#include "xh_core.h"

SV *
xh_h2x(xh_h2x_ctx_t *ctx, SV *hash)
{
    SV *result;

    /* run */
    dXCPT;
    XCPT_TRY_START
    {
        xh_stack_init(&ctx->stash, XH_H2X_STASH_SIZE, sizeof(SV *));
        xh_writer_init(&ctx->writer, ctx->opts.encoding, ctx->opts.output, ctx->opts.buf_size, ctx->opts.indent, ctx->opts.trim);

        if (ctx->opts.xml_decl) {
            xh_xml_write_xml_declaration(&ctx->writer, ctx->opts.version, ctx->opts.encoding);
        }

        switch (ctx->opts.method) {
            case XH_METHOD_NATIVE:
                xh_h2x_native(ctx, ctx->opts.root, xh_strlen(ctx->opts.root), SvRV(hash));
                break;
            case XH_METHOD_NATIVE_ATTR_MODE:
                (void) xh_h2x_native_attr(ctx, ctx->opts.root, xh_strlen(ctx->opts.root), SvRV(hash), XH_H2X_F_COMPLEX);
                break;
            case XH_METHOD_LX:
                xh_h2x_lx(ctx, hash, XH_H2X_F_NONE);
                break;
            default:
                croak("Invalid method");
        }
    } XCPT_TRY_END

    XCPT_CATCH
    {
        xh_stash_clean(&ctx->stash);
        xh_writer_destroy(&ctx->writer);
        XCPT_RETHROW;
    }

    xh_stash_clean(&ctx->stash);
    result = xh_writer_flush(&ctx->writer);
    if (result != NULL && ctx->opts.utf8) {
#ifdef XH_HAVE_ENCODER
        if (ctx->writer.encoder == NULL) {
            SvUTF8_on(result);
        }
#else
         SvUTF8_on(result);
#endif
    }
    xh_writer_destroy(&ctx->writer);

    return result;
}

#ifdef XH_HAVE_DOM
SV *
xh_h2d(xh_h2x_ctx_t *ctx, SV *hash)
{
    dXCPT;

    xmlDocPtr doc = xmlNewDoc(BAD_CAST ctx->opts.version);
    if (doc == NULL) {
        croak("Can't create new document");
    }
    if (ctx->opts.encoding[0] == '\0') {
        doc->encoding = (const xmlChar*) xmlStrdup((const xmlChar*) XH_INTERNAL_ENCODING);
    }
    else {
        doc->encoding = (const xmlChar*) xmlStrdup((const xmlChar*) ctx->opts.encoding);
    }

    XCPT_TRY_START
    {
        xh_stack_init(&ctx->stash, XH_H2X_STASH_SIZE, sizeof(SV *));
        switch (ctx->opts.method) {
            case XH_METHOD_NATIVE:
                xh_h2d_native(ctx, (xmlNodePtr) doc, ctx->opts.root, xh_strlen(ctx->opts.root), SvRV(hash));
                break;
            case XH_METHOD_NATIVE_ATTR_MODE:
                (void) xh_h2d_native_attr(ctx, (xmlNodePtr) doc, ctx->opts.root, xh_strlen(ctx->opts.root), SvRV(hash), XH_H2X_F_COMPLEX);
                break;
            case XH_METHOD_LX:
                xh_h2d_lx(ctx, (xmlNodePtr) doc, hash, XH_H2X_F_NONE);
                break;
            default:
                croak("Invalid method");
        }
    } XCPT_TRY_END

    XCPT_CATCH
    {
        xh_stash_clean(&ctx->stash);
        XCPT_RETHROW;
    }

    xh_stash_clean(&ctx->stash);

    return x_PmmNodeToSv((xmlNodePtr) doc, NULL);
}
#endif
