/*
This product contains certain software code or other information
("AT&T Software") proprietary to AT&T Corp. ("AT&T").  The AT&T
Software is provided to you "AS IS".  YOU ASSUME TOTAL RESPONSIBILITY
AND RISK FOR USE OF THE AT&T SOFTWARE.  AT&T DOES NOT MAKE, AND
EXPRESSLY DISCLAIMS, ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WARRANTIES OF
TITLE OR NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS, ANY
WARRANTIES ARISING BY USAGE OF TRADE, COURSE OF DEALING OR COURSE OF
PERFORMANCE, OR ANY WARRANTY THAT THE AT&T SOFTWARE IS "ERROR FREE" OR
WILL MEET YOUR REQUIREMENTS.

Unless you accept a license to use the AT&T Software, you shall not
reverse compile, disassemble or otherwise reverse engineer this
product to ascertain the source code for any AT&T Software.

(c) AT&T Corp. All rights reserved.  AT&T is a registered trademark of AT&T Corp.

***********************************************************************

History:

      24/11/99  - initial release by Hartmut Liefke, liefke@seas.upenn.edu
                                     Dan Suciu,      suciu@research.att.com
*/

//**************************************************************************
//**************************************************************************

// This module implements 'Compressor' and 'Uncompressor' -
// the interfaces to the ZLIB (either gzip or bzip) libary

#pragma once

#include "bzlib.h"
#include <zlib.h>
#include "Model.h"
#include "Coder.h"

class Output;
class MemStreamer;
class Input;
class Session;
class Settings;

//************************************************************************
// The three strategy classes for compression (GZIP, BZIP, uncompressed)

extern void *bzalloc(void *opaque,int items,int size);
extern void *zalloc(void *opaque,unsigned items,unsigned size);
extern void zfree(void *opaque,void *ptr);

class BZip;
class GZip;
class NoZip;
class PPMDI;

class Zipper
{
	bool is_compressing;
public:
	int idx;
	static Zipper* NewZipper(char ziptype, int idx, bool iscomp = true);

	Zipper(int i, bool iscomp);
	~Zipper();

	virtual void initCompress()=0;
	virtual void setCompPtrs(char *nextout, char* nextin, int availin)=0;
	virtual int doCompress(int flag = -1)=0;
	virtual void resetCompress();
	virtual void endCompress() {};

	virtual void initUncompress()=0;
	virtual void setUncompPtrs(char *nextout, int availout, int availin)=0;
	virtual int doUncompress()=0;
	virtual void endUncompress()=0;

	virtual char** getNextInPtr()=0;
	virtual int getAvailIn()=0;
	virtual int* getAvailInPtr()=0;
	virtual int getAvailOut()=0;
	virtual int* getAvailOutPtr()=0;
	virtual int getTotalOut()=0;
	virtual int getTotalIn()=0;
	virtual void setNextOut(char* no)=0;
	virtual void setNextIn(char* nextin)=0;
	virtual void setAvailIn(int availin)=0;

	virtual void resetTotals()=0;

	virtual bool needsReInit();
};

class GZip: public Zipper
{
   z_stream       zstate;
public:
	GZip(int idx, bool iscomp);
	~GZip();
	
	void initCompress();
	void setCompPtrs(char *nextout, char* nextin, int availin);
	int doCompress(int flag = -1);
	void endCompress();
	void resetCompress();

	void initUncompress();
	void setUncompPtrs(char *nextout, int availout, int availin);
	int doUncompress();
	void endUncompress();

	void setNextOut(char* nextout);
	void setNextIn(char* nextin);
	char **getNextInPtr();

	void setAvailIn(int availin);
	int getAvailIn();
	int* getAvailInPtr();
	int getAvailOut();
	int* getAvailOutPtr();

	int getTotalOut();
	int getTotalIn();
	void resetTotals();
};

class BZip: public Zipper
{
   bz_stream      bzstate;
public:
	BZip(int idx, bool iscomp);
	~BZip();

	void initCompress();
	void setCompPtrs(char *nextout, char* nextin, int availin);
	int doCompress(int flag = -1);
	bool needsReInit();
	void endCompress();

	void initUncompress();
	void setUncompPtrs(char *nextout, int availout, int availin);
	int doUncompress();
	void endUncompress();

	void setNextOut(char* nextout);
	void setNextIn(char* nextin);
	char **getNextInPtr();

	void setAvailIn(int availin);
	int getAvailIn();
	int* getAvailInPtr();
	int getAvailOut();
	int* getAvailOutPtr();

	int getTotalOut();
	int getTotalIn();
	void resetTotals();
};

class NoZip: public Zipper 
{
	char* nextin;
	char* nextout;
	int availin;
	int availout;
	int totalin;
	int totalout;
	bool initialized;

public:
	NoZip(int idx, bool iscomp);
	~NoZip();

	void initCompress();
	void setCompPtrs(char *nextout, char* nextin, int availin);
	int doCompress(int flag = -1);
	void endCompress();

	void initUncompress();
	void setUncompPtrs(char *nextout, int availout, int availin);
	int doUncompress();
	void endUncompress();

	void setNextOut(char* nextout);
	void setNextIn(char* nextin);
	char **getNextInPtr();

	void setAvailIn(int availin);
	int getAvailIn();
	int* getAvailInPtr();
	int getAvailOut();
	int* getAvailOutPtr();

	int getTotalOut();
	int getTotalIn();
	void resetTotals();
};

struct PPMDIstatus
{
	char* next_in;
	char* next_out;
	int avail_in;
	int avail_out;
	int total_in;
	int total_out;
};

class PPMDI: public Zipper
{
   PPMDIstatus PPMDIstate;
	bool initialized;
   PPM_ENCODER *enc;
   PPM_DECODER *dec;
   int size;
   int order;
   unsigned char *src, *origsrc, *endptr, *dst;
   bool eof, resetsrc;

   unsigned int start, end;

   PPMDIData *data;

   int compress(unsigned char *dst, unsigned char *src, int inlen, int *inused, int outlen, int *outused);
   int uncompress(unsigned char *dst, unsigned char *src, int inlen, int outlen, int *outused, int *inused);

   void setSrc(unsigned char *s, int len);
   void setDst(unsigned char *s);

public:
	PPMDI(int idx, bool iscomp);
	~PPMDI();

   void setData(PPMDIData *d);

   void _putc(char c);
   int _getc();

	void initCompress();
	void setCompPtrs(char *nextout, char* nextin, int availin);
	int doCompress(int flag = -1);
	bool needsReInit();
	void endCompress();

	void initUncompress();
	void setUncompPtrs(char *nextout, int availout, int availin);
	int doUncompress();
	void endUncompress();

	void setNextOut(char* nextout);
	void setNextIn(char* nextin);
	char **getNextInPtr();

	void setAvailIn(int availin);
	int getAvailIn();
	int* getAvailInPtr();
	int getAvailOut();
	int* getAvailOutPtr();

	int getTotalOut();
	int getTotalIn();

	void resetTotals();
};

//************************************************************************

// The Compressor

class Compressor
{
   // We store the state for the zlib compressor
	Zipper		  *zipper;
   Output        *coutput;
   unsigned char  isinitialized:1;

public:
   Compressor(Settings *se, Output *myoutput);
   ~Compressor();
   void CompressMemStream(MemStreamer *memstream);
      // Reads the data from 'memstream' and sends
      // it to the compressor

   void CompressData(unsigned char *ptr,unsigned len);
      // Compresses the data at position 'ptr' of length 'len'

   void FinishCompress(unsigned long *uncompressedsize,unsigned long *compressedsize);
      // Finishes the compression and stores the input data size and
      // the output data size in 'uncompressedsize' and 'compressedsize'
};

//************************************************************************
// The Decompressor

class Uncompressor
{
	Zipper		  *zipper;
	Session		  *session;

public:
   unsigned char     isinitialized:1;

	Uncompressor(Session *s);
	~Uncompressor();

   char Uncompress(Input *input,unsigned char *dataptr,unsigned long *len);
      // Decompresses the data from 'input' and stores
      // the result in 'dataptr'. It decompresses at most *len
      // bytes. Afterwards, '*len' is set to the actual number
      // of bytes uncompressed.
      // The function returns 1, if output buffer is full and
      // there is more data to read. Otherwise, the function returns 0.
};
