/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

/* Code inspired by cairo and adapted for Scribus by Jean Ghali */

#include "scstreamfilter_flate.h"

#include <cstdlib>
#include <cstring>
#include <zlib.h>

#include <QDataStream>

#define BUFFER_SIZE 16384
struct  ScFlateEncodeFilterData
{
	z_stream      zlib_stream;
	unsigned char input_buffer [BUFFER_SIZE];
	unsigned char output_buffer[BUFFER_SIZE];
};

ScFlateEncodeFilter::ScFlateEncodeFilter(QDataStream* stream)
				   : ScStreamFilter(stream)
{

}

ScFlateEncodeFilter::ScFlateEncodeFilter(ScStreamFilter* filter)
				   : ScStreamFilter(filter)
{

}

ScFlateEncodeFilter::~ScFlateEncodeFilter()
{
	if (m_filterData && m_openedFilter)
		ScFlateEncodeFilter::closeFilter();
	freeData();
}

void ScFlateEncodeFilter::freeData()
{
	if (m_filterData)
		free (m_filterData);
	m_filterData = nullptr;
}

bool ScFlateEncodeFilter::openFilter()
{
	freeData();

	m_filterData = (ScFlateEncodeFilterData*) malloc(sizeof(ScFlateEncodeFilterData));
	if (m_filterData == nullptr)
		return false;

	m_filterData->zlib_stream.zalloc = Z_NULL;
	m_filterData->zlib_stream.zfree  = Z_NULL;
	m_filterData->zlib_stream.opaque = Z_NULL;
	
	if (deflateInit (&m_filterData->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK)
	{
		freeData();
		return false;
	}

	m_filterData->zlib_stream.next_in   = m_filterData->input_buffer;
	m_filterData->zlib_stream.avail_in  = 0;
	m_filterData->zlib_stream.next_out  = m_filterData->output_buffer;
	m_filterData->zlib_stream.avail_out = BUFFER_SIZE;

	m_openedFilter = ScStreamFilter::openFilter();
	return m_openedFilter;
}

bool ScFlateEncodeFilter::closeFilter()
{
	bool closeSucceed = writeDeflate(true);
	deflateEnd (&m_filterData->zlib_stream);
	m_openedFilter = false;
	closeSucceed  &= ScStreamFilter::closeFilter();
	return closeSucceed;
}

bool ScFlateEncodeFilter::writeData(const char* data, int dataLen)
{
	bool deflateSuccess = true;
    unsigned int count;
    const unsigned char *p = (const unsigned char *) data;

	if (!m_filterData)
		return false;

	while (dataLen)
	{
		count = dataLen;
		if (count > BUFFER_SIZE - m_filterData->zlib_stream.avail_in)
			count = BUFFER_SIZE - m_filterData->zlib_stream.avail_in;
		memcpy (m_filterData->input_buffer + m_filterData->zlib_stream.avail_in, p, count);
		p += count;
		m_filterData->zlib_stream.avail_in += count;
		dataLen -= count;

		if (m_filterData->zlib_stream.avail_in == BUFFER_SIZE)
			deflateSuccess &= writeDeflate(false);
    }

	return deflateSuccess;
}

bool ScFlateEncodeFilter::writeDeflate(bool flush)
{
	int  ret;
	bool deflateSuccess = true;
	bool finished;
	
	do
	{
		ret = deflate (&m_filterData->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH);
		if (flush || m_filterData->zlib_stream.avail_out == 0)
		{
			deflateSuccess &= writeDataInternal((const char*) m_filterData->output_buffer, BUFFER_SIZE - m_filterData->zlib_stream.avail_out);
			m_filterData->zlib_stream.next_out  = m_filterData->output_buffer;
			m_filterData->zlib_stream.avail_out = BUFFER_SIZE;
		}

		finished = true;
		if (m_filterData->zlib_stream.avail_in != 0)
			finished = false;
		if (flush && ret != Z_STREAM_END)
			finished = false;

	} while (!finished);

	m_filterData->zlib_stream.next_in = m_filterData->input_buffer;
	return deflateSuccess;
}
