
#include <iostream>
#include <fstream>
#include <zlib.h>
#include <vector>
#include <memory>

using namespace std;

char pool[100];
typedef vector<char> Buffer;

void* myalloc(void* p, uInt items, uInt size)
{
	void* v = malloc(items*size);
	cout << "request alloc " << v << " for "
			 << items << " items or size " << size << endl;
	return v;
}

void myfree(void* p, void* addr)
{
	cout << "request for freeing of " << addr << endl;
	free(addr);
}

void compress_data()
{
	ifstream ist("memory_usage0.dat");
	ofstream ost("comptest.dat");

	if(!ist)
		{
			cerr << "input file bad" << endl; abort();
		}

	if(!ost)
		{
			cerr << "output file bad" << endl; abort();
		}

	Buffer in(10000);
	Buffer out(10000);

	z_stream str;
	str.next_out = (unsigned char*)&out[0];
	str.avail_out = out.size();
	str.next_in = 0; // data ptr
	str.avail_in = 0; // num bytes
	str.zalloc = myalloc;
	str.zfree = myfree;
	str.opaque = pool;
	
	int rc = 0;

	switch(deflateInit(&str,6))
		{
		case Z_OK:
			break;
		case Z_MEM_ERROR:
			cerr << "deflateInit mem error" << endl; abort();
			break;
		case Z_STREAM_ERROR:
			cerr << "deflateInit stream error" << endl; abort();
			break;
		case Z_VERSION_ERROR:
			cerr << "deflateInit version error" << endl; abort();
			break;
		default:
			cerr << "deflateInit failed - unknown reason" << endl; abort();
		}

	do
		{
			if(str.avail_out==0)
				{
					cout << "avail_out hit zero" << endl;
					ost.write(&out[0],out.size());
					str.next_out = (unsigned char*)&out[0];
					str.avail_out = out.size();
				}

			if(str.avail_in==0)
				{
					int len = ist.readsome(&in[0],in.size());
					if(!ist || len == 0) break;
					str.next_in=(unsigned char*)&in[0];
					str.avail_in = len;
					cout << "read buffer length = " << len << endl;
				}

			switch(rc=deflate(&str,Z_NO_FLUSH)) // Z_FINISH
				{
				case Z_OK: // some progress made
					cout << "avail_out = " << str.avail_out
							 << " avail_in = " << str.avail_in 
							 << endl;
					break;
				case Z_STREAM_END: // all input consumed, all output produced
					cout << "deflate stream end" << endl;
					break;
				case Z_BUF_ERROR: // no progress
					cout << "deflate buf error" << endl;
					break;
				case Z_STREAM_ERROR: 
					cerr << "deflate stream error" << endl; abort();
					break;
				default:
					cerr << "deflate failed - unknown error" << endl; abort();
				}
		}
	while(rc==Z_OK);

	int endloop=false;
	while(endloop==false)
		{
			switch(rc=deflate(&str,Z_FINISH)) // Z_FINISH
				{
				case Z_OK:
				case Z_STREAM_END: // all input consumed, all output produced
				case Z_BUF_ERROR:
					cout << "final deflate stream end" << endl;
					break;
				case Z_STREAM_ERROR: 
					cerr << "final deflate stream error" << endl; abort();
					break;
				default:
					cerr << "final deflate - unknown error" << endl; abort();
				}

			if(str.avail_out!=0) endloop=true;

			if(str.avail_out < out.size())
				{
					cout << "final write of " << out.size()-str.avail_out << endl;
					ost.write(&out[0],out.size()-str.avail_out);
					str.next_out = (unsigned char*)&out[0];
					str.avail_out = out.size();
				}
		}

	switch(deflateEnd(&str))
		{
		case Z_OK:
			break;
		case Z_STREAM_ERROR:
			cerr << "deflate stream error" << endl; abort();
			break;
		case Z_DATA_ERROR:
			cerr << "deflate data error" << endl; abort();
			break;
		default:
			cerr << "deflateEnd failed - unknown error" << endl; abort();
		}
}

void decompress_data()
{
	ifstream ist("comptest.dat");
	ofstream ost("memory_usage1.dat");
}

int main(int argc, char* argv[])
{
	if(argc==1) compress_data();
	else decompress_data();

	return 0;
}
