#include "compression.h"

#include <zlib.h>

int inflate(const void* const in, const size_t in_size, void* const out, size_t* const out_size, int window_bits)
{
	assert(in != nullptr);
	assert(out != nullptr);
	assert(out_size != nullptr);

	z_stream stream;
	int result;

	memset(&stream, 0, sizeof(stream));

	stream.zalloc = static_cast<alloc_func>(Z_NULL);
	stream.zfree = static_cast<free_func>(Z_NULL);
	stream.opaque = static_cast<voidpf>(Z_NULL);

	stream.next_in = const_cast<z_const Bytef*>(static_cast<const Bytef*>(in));
	stream.avail_in = static_cast<uInt>(in_size);
	stream.next_out = static_cast<Bytef*>(out);
	stream.avail_out = static_cast<uInt>(*out_size);

	result = inflateInit2(&stream, window_bits);
	if (result != Z_OK)
		return result;

	result = inflate(&stream, Z_FINISH);
	if (result != Z_STREAM_END) {
		inflateEnd(&stream);
		if (result == Z_NEED_DICT || (result == Z_BUF_ERROR && stream.avail_in == 0))
			return Z_DATA_ERROR;
		return result;
	}

	*out_size = stream.total_out;

	result = inflateEnd(&stream);

	return result;
}

int deflate(const void* const in, const size_t in_size, void* const out, size_t* const out_size, int window_bits, int level)
{
	assert(in != nullptr);
	assert(out != nullptr);
	assert(out_size != nullptr);

	z_stream stream;
	int result;

	memset(&stream, 0, sizeof(stream));

	stream.zalloc = static_cast<alloc_func>(Z_NULL);
	stream.zfree = static_cast<free_func>(Z_NULL);
	stream.opaque = static_cast<voidpf>(Z_NULL);

	stream.next_in = const_cast<z_const Bytef*>(static_cast<const Bytef*>(in));
	stream.avail_in = static_cast<uInt>(in_size);
	stream.next_out = static_cast<Bytef*>(out);
	stream.avail_out = static_cast<uInt>(*out_size);

	result = deflateInit2(&stream, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
	if (result != Z_OK)
		return result;

	result = deflate(&stream, Z_FINISH);
	if (result != Z_STREAM_END) {
		deflateEnd(&stream);
		return result == Z_OK ? Z_BUF_ERROR : result;
	}

	*out_size = stream.total_out;

	result = deflateEnd(&stream);

	return result;
}
