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

void
xh_writer_resize_buffer(xh_writer_t *writer, size_t inc)
{
    (void) xh_writer_flush(writer);

    xh_perl_buffer_grow(&writer->main_buf, inc);
}

SV *
xh_writer_flush_buffer(xh_writer_t *writer, xh_perl_buffer_t *buf)
{
    if (writer->perl_obj != NULL) {
        xh_writer_write_to_perl_obj(buf, writer->perl_obj);
        return &PL_sv_undef;
    }
    else if (writer->perl_io != NULL) {
        xh_writer_write_to_perl_io(buf, writer->perl_io);
        return &PL_sv_undef;
    }

    return xh_writer_write_to_perl_scalar(buf);
}

#ifdef XH_HAVE_ENCODER
void
xh_writer_encode_buffer(xh_writer_t *writer, xh_perl_buffer_t *main_buf, xh_perl_buffer_t *enc_buf)
{
    size_t len;

    /* 1 char -> 4 chars and '\0' */
    len = (main_buf->cur - main_buf->start) * 4 + 1;

    if (len > (enc_buf->end - enc_buf->cur)) {
        xh_writer_flush_buffer(writer, enc_buf);

        xh_perl_buffer_grow(enc_buf, len);
    }

    xh_encoder_encode_perl_buffer(writer->encoder, main_buf, enc_buf);
}
#endif

SV *
xh_writer_flush(xh_writer_t *writer)
{
    xh_perl_buffer_t *buf;

#ifdef XH_HAVE_ENCODER
    if (writer->encoder != NULL) {
        xh_writer_encode_buffer(writer, &writer->main_buf, &writer->enc_buf);
        buf = &writer->enc_buf;
    }
    else {
        buf = &writer->main_buf;
    }
#else
    buf = &writer->main_buf;
#endif

    return xh_writer_flush_buffer(writer, buf);
}

void
xh_writer_destroy(xh_writer_t *writer)
{
    if (writer->perl_obj != NULL || writer->perl_io != NULL) {
        if (writer->main_buf.scalar != NULL)
            SvREFCNT_dec(writer->main_buf.scalar);
#ifdef XH_HAVE_ENCODER
        if (writer->enc_buf.scalar != NULL)
            SvREFCNT_dec(writer->enc_buf.scalar);
    }
    else if (writer->encoder != NULL) {
        if (writer->main_buf.scalar != NULL)
            SvREFCNT_dec(writer->main_buf.scalar);
#endif
    }

#ifdef XH_HAVE_ENCODER
    xh_encoder_destroy(writer->encoder);
#endif
}

void
xh_writer_init(xh_writer_t *writer, xh_char_t *encoding, void *output, size_t size, xh_uint_t indent, xh_bool_t trim)
{
    writer->indent = indent;
    writer->trim   = trim;

    xh_perl_buffer_init(&writer->main_buf, size);

    if (encoding[0] != '\0' && xh_strcasecmp(encoding, XH_INTERNAL_ENCODING) != 0) {
#ifdef XH_HAVE_ENCODER
        writer->encoder = xh_encoder_create(encoding, XH_CHAR_CAST XH_INTERNAL_ENCODING);
        if (writer->encoder == NULL) {
            croak("Can't create encoder for '%s'", encoding);
        }

        xh_perl_buffer_init(&writer->enc_buf, size * 4);
#else
        croak("Can't create encoder for '%s'", encoding);
#endif
    }

    if (output != NULL) {
        MAGIC  *mg;
        GV     *gv = (GV *) output;
        IO     *io = GvIO(gv);

        if (!io)
            croak("Can't use file handle as a PerlIO handle");

        if ((mg = SvTIED_mg(MUTABLE_SV(io), PERL_MAGIC_tiedscalar))) {
            /* Tied handle */
            writer->perl_obj = SvTIED_obj(MUTABLE_SV(io), mg);
        }
        else {
            /* PerlIO handle */
            writer->perl_io = IoOFP(io);
        }
    }
}
