//
// tardy - a tar post-processor
// Copyright (C) 2012 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/>.
//

#include <libtardy/file/output/bzip2.h>


void
file_output_bzip2::bzlib_fatal_error(int err)
{
    fatal("bzip2 %s: %s", filename().c_str(), BZ2_strerror(err));
}


#if 0
#define trace(fmt, ...) \
    fprintf(stderr, "%s: %d: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define trace1(fmt) \
    fprintf(stderr, "%s: %d: " fmt "\n", __FILE__, __LINE__)
#define trace0() \
    fprintf(stderr, "%s: %d:\n", __FILE__, __LINE__)
#else
#define trace(fmt, ...)
#define trace1(fmt)
#define trace0()
#endif


file_output_bzip2::~file_output_bzip2()
{
    trace("file_output_bzip2::~file_output_bzip2(this = %p) {", this);

    //
    // finish sending the compressed stream
    //
    for (;;)
    {
        trace1("Before call to BZ2_bzCompress FIN:");
        trace("stream.avail_in = %d", stream.avail_in);
        trace("stream.next_in = %p", stream.next_in);
        trace("stream.avail_out = %d", stream.avail_out);
        trace("stream.next_out = %p", stream.next_out);
        int err = BZ2_bzCompress(&stream, BZ_FINISH);
        if (err != BZ_FINISH_OK && err != BZ_STREAM_END)
            bzlib_fatal_error(err);
        trace1("After call to BZ2_bzCompress FIN:");
        trace("stream.avail_in = %d", stream.avail_in);
        trace("stream.next_in = %p", stream.next_in);
        trace("stream.avail_out = %d", stream.avail_out);
        trace("stream.next_out = %p", stream.next_out);
        if (stream.avail_out < BUFFER_SIZE)
        {
            int n = BUFFER_SIZE - stream.avail_out;
            deeper->write(buf, n);
            stream.avail_out = BUFFER_SIZE;
            stream.next_out = buf;
        }
        if (err == BZ_STREAM_END)
            break;
    }

    //
    // Clean up any resources we were using.
    //
    delete [] buf;
    buf = 0;
    trace1("}");
}


file_output_bzip2::file_output_bzip2(const file_output::pointer &a_deeper) :
    deeper(a_deeper),
    buf(new char [BUFFER_SIZE])
{
    trace("file_output_bzip2::file_output_bzip2(this = %p) {", this);
    stream.bzalloc = 0;
    stream.bzfree = 0;
    stream.opaque = 0;
    stream.avail_in = 0;
    stream.next_in = 0;
    stream.avail_out = BUFFER_SIZE;
    stream.next_out = buf;

    int block_size_100k = 3;
    int verbosity = 0;
    int work_factor = 30;
    int err =
        BZ2_bzCompressInit(&stream, block_size_100k, verbosity, work_factor);
    if (err != BZ_OK)
        bzlib_fatal_error(err);
    trace("stream.avail_in = %d", stream.avail_in);
    trace("stream.next_in = %p", stream.next_in);
    trace("stream.avail_out = %d", stream.avail_out);
    trace("stream.next_out = %p", stream.next_out);
    trace1("}");
}


file_output::pointer
file_output_bzip2::create(const file_output::pointer &a_deeper)
{
    return pointer(new file_output_bzip2(a_deeper));
}


file_output::pointer
file_output_bzip2::create_if_candidate(const file_output::pointer &a_deeper)
{
    if (candidate(a_deeper->filename()))
        return create(a_deeper);
    else
        return a_deeper;
}


void
file_output_bzip2::write(const void *data, size_t data_size)
{
    trace("file_output_bzip2::write_inner(this = %p, data = %p, "
        "data_size = %ld) {", this, data, (long)data_size);
    if (!data)
        bzlib_fatal_error(BZ_PARAM_ERROR);
    if (data_size == 0)
    {
        trace1("}");
        return;
    }
    stream.avail_in = data_size;
    stream.next_in = (char *)data;
    for (;;)
    {
        trace1("Before call to BZ2_bzCompress:");
        trace("stream.avail_in = %d", stream.avail_in);
        trace("stream.next_in = %p", stream.next_in);
        trace("stream.avail_out = %d", stream.avail_out);
        trace("stream.next_out = %p", stream.next_out);
        int err = BZ2_bzCompress(&stream, BZ_RUN);
        if (err != BZ_RUN_OK)
            bzlib_fatal_error(err);
        trace1("After call to BZ2_bzCompress:");
        trace("stream.avail_in = %d", stream.avail_in);
        trace("stream.next_in = %p", stream.next_in);
        trace("stream.avail_out = %d", stream.avail_out);
        trace("stream.next_out = %p", stream.next_out);
        if (stream.avail_out < BUFFER_SIZE)
        {
            int n = BUFFER_SIZE - stream.avail_out;
            deeper->write(buf, n);
            stream.avail_out = BUFFER_SIZE;
            stream.next_out = buf;
        }
        if (stream.avail_in == 0)
        {
            trace1("}");
            return;
        }
    }
}


// vim: set ts=8 sw=4 et :
