/***************************************************************************
                           cbz.cpp  -  description
                             -------------------
    begin                : Wed May 15 2002
    copyright            : (C) 2002-2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cbz.h"

#include <stdio.h>

#ifndef BZ_NO_STDIO
#define BZ_NO_STDIO
#endif
#include <bzlib.h>

#include "cbytearray.h"
#include "cstring.h"

#define MAX_BUFFER_LEN	536870912	// 512MiB max memory

/** */
CBZ::CBZ()
{
}

/** */
CBZ::~CBZ()
{
}

/** */
bool CBZ::Compress( CByteArray * src, CByteArray * dst )
{
	if ( src && dst )
	{
		return CBZ::Compress( (char*) src->Data(), src->Size(), dst );
	}
	else
	{
		return false;
	}
}

/** */
bool CBZ::Compress( const CString * src, CByteArray * dst )
{
	if ( src && dst )
	{
		return CBZ::Compress( src->Data(), src->Length(), dst );
	}
	else
	{
		return false;
	}
}

/** */
bool CBZ::Compress( char * src, unsigned int inlen, CByteArray * dst )
{
	bool res = false;
	int i;
	unsigned int outlen = inlen;

	// src and dst already checked non null
	if ( inlen == 0 )
	{
		return res;
	}

	while ( true )
	{
		outlen *= 2;
		
		// check for max memory
		if ( outlen > MAX_BUFFER_LEN )
		{
			printf("CBZ::Compress: max mem reached\n");
			break;
		}
		
		// increase output capacity
		if ( dst->Realloc(outlen) == false )
		{
			printf("CBZ::Compress: realloc 1 failed\n");
			break;
		}
		
		i = BZ2_bzBuffToBuffCompress( (char*)dst->Data(), &outlen, src, inlen, 9, 0, 0);

		// check result
		if ( i == BZ_OK )
		{
			res = true;
			break;
		}
		else if ( i == BZ_OUTBUFF_FULL )
		{
			// nothing, try again
		}
		else
		{
			// another decompression error
			printf("CBZ::Compress: failed with %d\n",i);
			break;
		}
	}

	if ( res )
	{
		/* shrink output to correct size */
		if ( dst->Realloc(outlen) == false )
		{
			printf("CBZ::Compress: realloc 2 failed\n");
			dst->SetSize(0);
			res = false;
		}
	}
	else
	{
		/* otherwise empty output */
		dst->SetSize(0);
	}

	return res;
}

/** */
bool CBZ::Decompress( CByteArray * src, CByteArray * dst )
{
	bool res = false;
	int i;
	unsigned int outlen = src->Size();

	if ( (!src) || (!dst) || (src->Size() == 0) )
	{
		return res;
	}

	while ( true )
	{
		if ( outlen < (50 * 1024 * 1024) )
		{
			outlen *= 10;
		}
		else
		{
			outlen += (50 * 1024 * 1024);
		}
		
		// check for max memory
		if ( outlen > MAX_BUFFER_LEN )
		{
			printf("CBZ::Decompress: max mem reached\n");
			break;
		}

		if ( dst->Realloc(outlen) == false )
		{
			printf("CBZ::Decompress: realloc 1 failed\n");
			break;
		}
		
		i = BZ2_bzBuffToBuffDecompress( (char*)dst->Data(), &outlen, (char*)src->Data(), src->Size(), 0, 0);

		if ( i == BZ_OK )
		{
			res = true;
			break;
		}
		else if ( i == BZ_OUTBUFF_FULL )
		{
			// nothing, try again
		}
		else
		{
			// another decompression error
			printf("CBZ::Decompress: failed with %d\n",i);			
			break;
		}
	}

	if ( res )
	{
		/* shrink output to correct size */
		if ( dst->Realloc(outlen) == false )
		{
			printf("CBZ::Compress: realloc 2 failed\n");
			dst->SetSize(0);
			res = false;
		}
	}
	else
	{
		/* otherwise empty output */
		dst->SetSize(0);
	}

	return res;
}
