//
// tardy - a tar post-processor
// Copyright (C) 2003, 2008, 2009, 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#ifndef LIBTARDY_TAR_INPUT_FILTER_GUNZIP_H
#define LIBTARDY_TAR_INPUT_FILTER_GUNZIP_H

#include <libtardy/ac/zlib.h>
#include <libtardy/tar/input/filter.h>

/**
  * The tar_input_filter_gunzip class is used to represent a filter
  * which uncompresses compressed files on input.  Files which are not
  * compressed are passed through unaltered.
  */
class tar_input_filter_gunzip:
    public tar_input_filter
{
public:
    /**
      * The destructor.
      */
    virtual ~tar_input_filter_gunzip();

    /**
      * The create class method is used to create new dynamically
      * allocated instances of this class.
      *
      * @param deeper
      *     The input stream to be filtered.
      */
    static pointer create(const tar_input::pointer &deeper);

protected:
    // See base class for documentation.
    bool read_header(tar_header &hdr);

    // See base class for documentation.
    void read_header_padding(void);

    // See base class for documentation.
    int read_data(void *data, int data_size);

    // See base class for documentation.
    void read_data_padding(void);

private:
    /**
      * The constructor.
      * It is provate on purpose, use the #create class method instead.
      *
      * @param deeper
      *     The input stream to be filtered.
      */
    tar_input_filter_gunzip(const tar_input::pointer &deeper);

    /**
      * The pass_through instance variable is used to remember wherther
      * we are passing through the file data unchanged (if true) or
      * inflating it on-the-fly (if false).
      */
    bool pass_through;

    /**
      * The stream instance variable is used to remember the internal
      * state of the zlib library when inflating the compressed data.
      */
    z_stream stream;

    /**
      * The z_eof instance variable is used to remember whether or not
      * we have reached the end-of-file within the compressed data.
      */
    bool z_eof;

    /**
      * The crc instance variable is used to remember the running cyclic
      * redundancy check of the decompressed data.  It is used to verify
      * the data, by comparing it with the CRC at the end of the data.
      */
    uLong crc;

    /**
      * The buf instance variable is used to remember an array of data
      * currently being consumed by the inflat function to genetate the
      * decompressed output data.
      */
    Byte *buf;

    /**
      * The buffered_data instance variable is used to remember a pointer
      * to a buffer of data, read from the deeper file.
      */
    unsigned char *buffered_data;

    /**
      * The buffered_data_pos instance variable is used to remember
      * where the next byte is to be read from within the data buffer
      * pointed to by buffered_data.
      */
    size_t buffered_data_pos;

    /**
      * The buffered_data_size instance variable is used to remember
      * how much data was read into the buffered_data array.
      *
      * assert(buffred_data_pos <= buffered_data_size);
      */
    size_t buffered_data_size;

    /**
      * The buffered_data_size_max instance variable is used to remember
      * the number of bytes allocated to hold the buffered_data.
      *
      * assert(buffred_data_size <= buffered_data_size_max);
      */
    size_t buffered_data_size_max;

    /**
      * The inflated_file_pos instance variable is used to remember the
      * current position within the decompressed file.
      */
    size_t inflated_file_pos;

    /**
      * The inflated_file_length instance variable is used to remember
      * the length of the decompressed file.
      */
    size_t inflated_file_length;

    /**
      * The deflated_file_pos instance variable is used to remember the
      * current position within the compressed file.
      */
    size_t deflated_file_pos;

    /**
      * The deflated_file_length instance variable is used to remember
      * the length of the compressed file.
      */
    size_t deflated_file_length;

    /**
      * The zlib_fatal_error method is used to report fatal errors from
      * the zlib library.
      */
    void zlib_fatal_error(int);

    /**
      * The get_long method is used to read a little-endian 4-byte value
      * from the input stream.
      */
    unsigned long get_long();

    /**
      * The buffered_getc() method is used to get a byte of data from
      * the deeper file, possibly through the buffered_data array.
      * It will return (-1) for end-of-file.
      */
    int buffered_getc();

    /**
      * The buffered_read method is used to read some data from the
      * deeper file, possibly coping with data which has been "pushed
      * back" into the stream.
      */
    int buffered_read(void *data, int len);

    /**
      * The buffered_unread method is used to "push back" data into the
      * sources, as seen from the buffered_read method.
      */
    void buffered_unread(const void *data, size_t len);

    /**
      * The default constructor.  Do not use.
      */
    tar_input_filter_gunzip();

    /**
      * The copy constructor.  Do not use.
      *
      * @param rhs
      *     The right hand side of the initialization.
      */
    tar_input_filter_gunzip(const tar_input_filter_gunzip &rhs);

    /**
      * The assignment operator.  Do not use.
      *
      * @param rhs
      *     The right hand side of the assignment.
      */
    tar_input_filter_gunzip &operator=(const tar_input_filter_gunzip &rhs);
};

#endif // LIBTARDY_TAR_INPUT_FILTER_GUNZIP_H
