//
// HSTEST.C
// this is a little demo & test programm for the kombination
// sliding directory & huffman compression
// the real kompression is done using a sliding directory mechanism pack(),
// which will eventually be farther compressed using a modified huffman
// method which takes some time as well, is currently not very efficient
// implemented, but may be used if space is really important,
// the first method should be chosen if the unpack time is of importance
// unpack(), written in assembler for speed reasons roughly decompresses
// some 25 KByte / Landmark or 1 MB on a 25 MHz 386 maschine
//
// warning : the code is not yet clean for byte ordering, it will work
// only on one type of maschine, the compressed files are probable not
// (yet) portable
//

#include <stdio.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>

#include <tomlib.h>

#include "pack.h"

#define BUFFSIZE 0xf000

char  far *inbuff = NULL;
char  far * pbuff = NULL;
char  far * hbuff = NULL;
char  far *outbuff = NULL;

long p_hufflen = 0;
long p_huffsum = 0;
long p_nohuff  = 0;
long p_huffind = 0;
long p_match,p_nmatch;

int fullprint = 0;

usage()
{
	printf(
		"HSTEST infile outfile [-options]\n"
		"                     options are\n"
		"    -Uncompress infile to outfile, default is compress\n"
		"    -Full (and slow) compression, default is fast mode\n"
		"    -S print statistics\n"
		"    -Quiet mode\n"
		"        have Fun! tom ehlert\n");
	exit(1);
}

int extract 		= FALSE;
int fullcompression = FALSE;
int statistics 		= FALSE;
int quiet   		= FALSE;
int 	fdin = 0
		,fdout = 0;


//********************************************************************
// some support code
//********************************************************************
void qprintf(char *s)
{
	if (!quiet)
		printf(s);
}


main(int argc,char *argv[])
{
	unsigned loop,anzblocks;
	char *argptr;

	while (argptr = getargs(&argc,argv,"/-"))
		while (*++argptr)
			switch(toupper(*argptr))
				{
				case 'U' : extract 			= TRUE; break;
				case 'F' : fullcompression 	= TRUE; break;
				case 'S' : statistics 		= TRUE; break;
				case 'Q' : quiet     		= TRUE; break;
				default  : usage();
				}

	if (argc != 3)
		{
		usage();
		}

	if ((fdin = open(argv[1],O_RDONLY | O_BINARY)) < 0)
		{
		printf("can't read %s",argv[1]);
		exit(1);
		}

	if ((fdout = open(argv[2],O_WRONLY | O_BINARY | O_CREAT | O_TRUNC,
							S_IREAD|S_IWRITE)) < 0)
		{
		printf("can't write %s",argv[2]);
		exit(1);
		}


    if ((inbuff  = dosalloc((long)BUFFSIZE)) == NULL ||
        ( pbuff  = dosalloc((long)BUFFSIZE)) == NULL ||
        ( hbuff  = dosalloc((long)BUFFSIZE)) == NULL ||
        (outbuff = dosalloc((long)BUFFSIZE)) == NULL )
		{
		printf("can't allocate enough memory for buffers");
		exit(1);
		}

	tx_init();				// that supports my own custom profiler
							// forget it

	if (!quiet)
		{
		anzblocks = (unsigned)((filelength(fdin)+(BUFFSIZE-1))/BUFFSIZE);
		for (loop = 0; loop < anzblocks; loop++)
			printf("@");
		for (loop = 0; loop < anzblocks; loop++)
			printf("\b");
		}

	if (extract)
		decompress();
	else
		compress();
}

// this is used for the statistic
		
long rd_time = 0;
long wr_time = 0;
long pack_time = 0;
long huff_time = 0;
long unpack_time = 0;

long insum = 0;
long psum  = 0;
long hsum  = 0;
long osum  = 0;

# define NOT_COMPRESSED (('N'<<8)|'C')
# define  HS_COMPRESSED (('H'<<8)|'S')
# define  SL_COMPRESSED (('S'<<8)|'L')


do_write(unsigned mode,unsigned length,void far *buffer)
{
	osum += length;
	mikro_diff();
	far_write(fdout,&mode,2);
	far_write(fdout,&length,2);
	far_write(fdout,buffer,length);
	wr_time += mikro_diff();
	qprintf("W");
}

compress()
{
	unsigned rd,plen,hlen;
	unsigned long ltime;

	far_write(fdout,"te",2);			  			// justy write my initials
												// to begin of file

	while (1)
		{
		mikro_diff();							// perform time analysis
												// for each function
		if ((rd	= far_read(fdin,inbuff,BUFFSIZE)) == 0)
			break;
		rd_time += mikro_diff();

		qprintf("R\b");

		insum += rd;

		mikro_diff();
		plen = pack (pbuff,rd,inbuff,rd);		// PACK
		pack_time += mikro_diff();

		if (fullprint)
			printf("%5u packed packlen %5u --> time %5ld \n",rd,plen,ltime);

		qprintf("P\b");

		if (plen == 0xffff || plen >= rd)
			{
			psum += rd;
			hsum += rd;
			do_write(NOT_COMPRESSED,rd,inbuff);
			continue;
			}

		psum += plen;

		if (fullcompression)
			{
			mikro_diff();
			hlen = hs_pack (hbuff,plen,pbuff,plen);
			huff_time += mikro_diff();
			qprintf("p\b");

			if (hlen != 0xffff && hlen < plen)
				{
				hsum += hlen;
				do_write(HS_COMPRESSED,hlen,hbuff);
				continue;
				}
			}
		hsum += plen;
		do_write(SL_COMPRESSED,plen,pbuff);
		}

	if (statistics)
		{
		qprintf("\n");
		printf("kompressed %ld Bytes down to %ld ",insum,osum);
		if (fullcompression)
			printf("(packsize %ld",psum);
		printf("\n");

		printf("times (in ms) : reading %ld writing %ld packing %ld",
			rd_time/1000,wr_time/1000,pack_time/1000);
		if(fullcompression)
			printf(" huffing %ld",huff_time / 1000);
		printf("\n");
		}
}


decompress()
{
	char b[2];
	unsigned rd,mode,ulen,length;

	read(fdin,b,2);

	if (memcmp(b,"te",2))
		quit("this is no known kompressed file,sorry\n");

	while (1)
		{
		mikro_diff();							// perform time analysis
												// for each function
		if ((rd	= far_read(fdin,&mode,2)) != 2)
			{
			if (rd == 0)						// assume we are ready
				break;

			quit("error reading input file\n");
			}


		if ((rd	= far_read(fdin,&length,2)) != 2)
			quit("error reading input file\n");

		if ((rd	= far_read(fdin,inbuff,length)) != length)
			quit("error reading input file\n");

		rd_time += mikro_diff();

		qprintf("R\b");

		insum += rd;

		mikro_diff();
		switch(mode)
			{
			case NOT_COMPRESSED: 
				mikro_diff();
				far_write(fdout,inbuff,length);
				wr_time += mikro_diff();
				qprintf("W");
				osum += length;
				break;

			case HS_COMPRESSED: 
				mikro_diff();
				ulen = hs_unpack(outbuff,inbuff,length);
				unpack_time += mikro_diff();
				qprintf("U\b");

				far_write(fdout,outbuff,ulen);
				wr_time += mikro_diff();
				qprintf("W");
				osum += ulen;
				break;

			case SL_COMPRESSED: 
				mikro_diff();
				ulen = unpack(outbuff,inbuff,length);
				unpack_time += mikro_diff();
				qprintf("U\b");

				far_write(fdout,outbuff,ulen);
				wr_time += mikro_diff();
				qprintf("W");
				osum += ulen;
				break;
			}
		}

	if (statistics)
		{
		qprintf("\n");
		printf("decompressed %ld Bytes to %ld\n",insum,osum);

		printf("times (in ms) : reading %ld writing %ld unpacking %ld\n",
			rd_time/1000,wr_time/1000,unpack_time/1000);
		}
}
