/*
Copyright (C) 2010, Parrot Foundation.
$Id: gziphandle.pmc 47913 2010-06-29 18:16:46Z fperrad $

=head1 NAME

src/dynpmc/gziphandle.pmc - GzipHandle PMC

=head1 DESCRIPTION

The GzipHandle PMC performs I/O operations on a source or destination file.

This PMC wraps the zlib.

=head2 Vtable Functions

=over 4

=cut

*/

#include <zlib.h>
#include "parrot/extend.h"

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */

pmclass GzipHandle extends Handle dynpmc auto_attrs {
    ATTR void *file;

/*

=item C<void init()>

Initializes a newly created FileHandle object.

=cut

*/
    VTABLE void init() {
        PARROT_GZIPHANDLE(SELF)->file = NULL;
    }

/*

=item C<INTVAL get_bool()>

Returns whether the GzipHandle has reached the end of the file.

=cut

*/
    VTABLE INTVAL get_bool() {
        gzFile file;
        GET_ATTR_file(INTERP, SELF, file);
        return !gzeof(file);
    }

/*

=back

=head2 Stream-Oriented Methods

=over 4

=item C<METHOD open(STRING *filename, STRING *mode :optional)>

Opens the file at the given filename (including path) with the given mode. The
invocant is modified and becomes an open filehandle.

=cut

*/
    METHOD open(STRING *filename, STRING *mode :optional,
                               INTVAL has_mode :opt_flag) {
        char * const path  = Parrot_str_to_cstring(INTERP, filename);
        gzFile file;

        if (has_mode) {
            char * const mod = Parrot_str_to_cstring(INTERP, mode);
            file      = gzopen(path, mod);
            Parrot_str_free_cstring(mod);
        }
        else
            file = gzopen(path, "rb");

        Parrot_str_free_cstring(path);

        if (!file)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "gzopen fails");

        SET_ATTR_file(INTERP, SELF, file);
        RETURN(PMC SELF);
    }

/*

=item C<METHOD close()>

Close the gziphandle.

=cut

*/
    METHOD close() {
        INTVAL status;
        gzFile file;
        GET_ATTR_file(INTERP, SELF, file);
        status = gzclose(file);
        RETURN(INTVAL status);
    }

/*

=item C<METHOD eof()>

Returns true if the filehandle is at end-of-file, returns false otherwise.

=cut

*/
    METHOD eof() {
        INTVAL status;
        gzFile file;
        GET_ATTR_file(INTERP, SELF, file);
        status = gzeof(file);
        RETURN(INTVAL status);
    }

/*

=item C<METHOD flush()>

Flushes the gziphandle.

=cut

*/
    METHOD flush() {
        INTVAL status;
        gzFile file;
        GET_ATTR_file(INTERP, SELF, file);
        status = gzflush(file, Z_SYNC_FLUSH);
        RETURN(INTVAL status);
    }

/*

=item C<METHOD print([INTVAL|FLOATVAL|STRING *|PMC*] value)>

Print the passed in integer, number, string, or PMC to the gziphandle.
(Integers, numbers, and strings are auto-boxed as PMCs.)

=cut

*/
    METHOD print(PMC *value) {
        gzFile file;
        STRING * const str = VTABLE_get_string(INTERP, value);
        char   * const buf = Parrot_str_to_cstring(INTERP, str);
        const UINTVAL  len =  Parrot_str_byte_length(INTERP, str);

        GET_ATTR_file(INTERP, SELF, file);
        (void)gzwrite(file, buf, len);
        Parrot_str_free_cstring(buf);
    }


/*

=item C<METHOD puts(STRING *value)>

Print the string to the gziphandle.

=cut

*/
    METHOD puts(STRING *value) {
        INTVAL status;
        gzFile file;
        char * const  buf = Parrot_str_to_cstring(INTERP, value);
        const UINTVAL len =  Parrot_str_byte_length(INTERP, value);

        GET_ATTR_file(INTERP, SELF, file);
        status =  gzwrite(file, buf, len);
        Parrot_str_free_cstring(buf);
        RETURN(INTVAL status);
    }

/*

=item C<METHOD read(INTVAL bytes)>

Read the given number of bytes from the gziphandle and return them in a string.

=cut

*/
    METHOD read(INTVAL length) {
        int result;
        gzFile file;
        STRING *str = STRINGNULL;
        char * const buf = mem_allocate_n_zeroed_typed(length, char);
        GET_ATTR_file(INTERP, SELF, file);
        result = gzread(file, buf, length);
        if (result > 0) {
            str = Parrot_str_new(INTERP, buf, result);
        }
        mem_sys_free(buf);
        RETURN(STRING *str);
    }

/*

=item C<METHOD is_tty()>

GzipHandles are never tty's, returns false.

=cut

*/
    METHOD is_tty() {
        Parrot_warn_deprecated(INTERP,
            "'is_tty' is deprecated, use 'isatty' instead - TT #1689");
        RETURN(INTVAL 0);
    }

/*

=back

=head2 Basic Methods

=over 4

=item C<METHOD version()>

Returns a string with the zlib version.

=back

=cut

*/

    METHOD version() {
        STRING *version = Parrot_str_new_constant(INTERP, zlibVersion());
        RETURN(STRING *version);
    }

    METHOD compress(STRING *str) {
        int rc;
        char *buf;
        STRING *dst = STRINGNULL;
        UINTVAL srcLen, dstLen;
        char * const src = Parrot_str_to_cstring(INTERP, str);

        if (!src)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "failed to allocate");

        srcLen = Parrot_str_byte_length(INTERP, str);
        dstLen = 12 + srcLen + srcLen / 1000;
        buf    = mem_allocate_n_zeroed_typed(dstLen, char);

        if (!buf) {
            Parrot_str_free_cstring(src);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "failed to allocate");
        }

        rc = compress((Bytef *)buf, &dstLen, (const Bytef *)src, srcLen);
        Parrot_str_free_cstring(src);

        switch (rc) {
          case Z_OK:
            dst = Parrot_str_new(INTERP, buf, dstLen);
            mem_sys_free(buf);
            break;

          case Z_MEM_ERROR:
            mem_sys_free(buf);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "not enough memory");
            break;

          case Z_BUF_ERROR:
            mem_sys_free(buf);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "output buffer error");
            break;

          default:
            /* these are the only three documented return values */
            break;
        }

        RETURN(STRING *dst);
    }

    METHOD uncompress(STRING *str) {
        int rc;
        char *buf;
        STRING *dst = STRINGNULL;
        UINTVAL srcLen, dstLen;
        char * const src = Parrot_str_to_cstring(INTERP, str);

        if (!src)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "failed to allocate");

        srcLen = Parrot_str_byte_length(INTERP, str);
        dstLen = 2 * srcLen;
    REDO:
        buf = mem_allocate_n_zeroed_typed(dstLen, char);

        if (!buf) {
            Parrot_str_free_cstring(src);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "failed to allocate");
        }

        rc = uncompress((Bytef *)buf, &dstLen, (const Bytef *)src, srcLen);

        switch (rc) {
          case Z_OK:
            Parrot_str_free_cstring(src);
            dst = Parrot_str_new(INTERP, buf, dstLen);
            mem_sys_free(buf);
            break;

          case Z_MEM_ERROR:
            Parrot_str_free_cstring(src);
            mem_sys_free(buf);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "not enough memory");
            break;

          case Z_BUF_ERROR:
            mem_sys_free(buf);
            dstLen *= 2;
            goto REDO;

          case Z_DATA_ERROR:
            Parrot_str_free_cstring(src);
            mem_sys_free(buf);
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "input data corrupted");
            break;

          default:
            /* these are the only four documented return values */
            break;
        }

        RETURN(STRING *dst);
    }

    METHOD crc32(INTVAL crc, STRING *str) {
        UINTVAL srcLen;
        char * const src = Parrot_str_to_cstring(INTERP, str);

        if (!src)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_ILL_INHERIT,
                "failed to allocate");

        srcLen  = Parrot_str_byte_length(INTERP, str);

        crc = crc32(crc, (const Bytef *)src, srcLen);
        RETURN(INTVAL crc);
    }
}

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */

