#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <time.h>

#define	CPU_FREQ_HZ	100000000
#include "libsys.h"

#include "crc32.h"
#include "inflate.h"

char buffer[1024*1024];
char * volatile pPtr_r;
char * volatile pPtr_w;
volatile int timeout_cnt;
volatile int file_len;

void handler3(void)
{
	volatile UINT32 *pUART_stat = (UINT32*)SYS_UART0_STAT;
	volatile UINT32 *pUART_data = (UINT32*)SYS_UART0_DATA;

	while(0x200 & *pUART_stat)
	{
//		sputs("w: "); print_word((int)pPtr_w); sputs("\n");
		if (pPtr_w == &buffer[sizeof(buffer)-1])
			pPtr_w = buffer;
		*(pPtr_w++) = *pUART_data;
		timeout_cnt = 100;
		file_len++;
	}

}

int READBYTE(z_stream *zs) {
   if (zs->avail_in <= 0)
     return -1;

   zs->avail_in --;
   return *(zs->next_in ++);
}

#define MAX_BUFOUT	16*1024*1024
int main(int argc, char **argv)
{
	volatile UINT32 *pUART_stat = (UINT32*)SYS_UART0_STAT;
	unsigned char outData[MAX_BUFOUT];
	FILE *infp;
	int i, gpflags, tmp[4];
	unsigned long size, crc;
	unsigned char *fileData;
	unsigned long bytes, InflatedSize, InflatedCRC, speed_count;
	z_stream zs;
	time_t start_time, work_time;

	UART0_setbaud(460800);

	printf("Reading gzipped stream..\n");

	pPtr_r = buffer;
	pPtr_w = buffer;
	*pUART_stat = (1 << 6);
	interrupt_register(3, handler3);
	interrupt_enable(3);

	file_len = 0;
	timeout_cnt = 1000000;
	while(timeout_cnt--)
	{
		sleep(1);
	}

	printf("file_len: %d\n", file_len);
   bytes = file_len;
   fileData = pPtr_r;

   zs.next_in = fileData;
   zs.avail_in = (unsigned int) bytes;
   zs.next_out = outData;
   zs.avail_out = MAX_BUFOUT;

   tmp[0] = READBYTE(&zs);
   if (tmp[0] == -1)
     {
	// error reading from file
	printf("error reading data from file\n");
	return 0;
     }
   tmp[1] = READBYTE(&zs);

   if (tmp[0] != 0x1f || tmp[1] != 0x8b) {
//      fprintf(stderr, "Magic number mismatch 0x%02x%02x\n",
//	      tmp[0], tmp[1]);
      return 20;
   }

   tmp[0] = READBYTE(&zs);
   if(tmp[0] != 8) {
//      fprintf(stderr, "Unknown compression method: 0x%02x\n", tmp[0]);
      return 20;
   }

   gpflags = READBYTE(&zs);
   if ((gpflags & ~0x1f)) {
      printf("Unknown flags set!\n");
   }

   /* Skip file modification time (4 bytes) */
   READBYTE(&zs);
   READBYTE(&zs);
   READBYTE(&zs);
   READBYTE(&zs);
   /* Skip extra flags and operating system fields (2 bytes) */
   READBYTE(&zs);
   READBYTE(&zs);

   if ((gpflags & 4)) {
      /* Skip extra field */
      tmp[0] = READBYTE(&zs);
      tmp[1] = READBYTE(&zs);
      i = tmp[0] + 256*tmp[1];
      while (i--)
	{
	   READBYTE(&zs);
	}
   }
   if((gpflags & 8)) {
      while((READBYTE(&zs))) {
      }
   }
   if((gpflags & 16)) {
      while((READBYTE(&zs))) {
      }
   }
   if((gpflags & 2)) {
      /* Skip CRC16 */
      READBYTE(&zs);
      READBYTE(&zs);
   }

	printf("Go\n");
	// normal test
	start_time = clock();
	if (InflateData(&zs))
	{
		printf("Problem during decompression!\n");
		return 0;
	}
	work_time = clock() - start_time;


/*
	// speed test
	fprintf(stdout, "SPEED TEST!\n");
	speed_count = 0;
	while (time(NULL) - start_time < 20)
	  {
	     zs_speed.next_in = zs.next_in;
	     zs_speed.next_out = zs.next_out;
	     zs_speed.avail_in = zs.avail_in;
	     zs_speed.avail_out = zs.avail_out;
	     if (InflateData(&zs_speed))
	       {
		  fprintf(stdout, "Problem during decompression!\n");
		  return 0;
	       }
	     speed_count ++;
	  }
	fprintf(stdout, "speed count %ld\n", speed_count);
	zs.next_in = zs_speed.next_in;
	zs.next_out = zs_speed.next_out;
	zs.avail_in = zs_speed.avail_in;
	zs.avail_out = zs_speed.avail_out;
*/

	crc = READBYTE(&zs);
	crc |= (READBYTE(&zs)<<8);
	crc |= (READBYTE(&zs)<<16);
	crc |= (READBYTE(&zs)<<24);

	size = READBYTE(&zs);
	size |= (READBYTE(&zs)<<8);
	size |= (READBYTE(&zs)<<16);
	size |= (READBYTE(&zs)<<24);

	InflatedSize = MAX_BUFOUT - zs.avail_out;
	InflatedCRC = MakeCRC32(outData, InflatedSize, 0);

//	write(1, outData, InflatedSize);
	fprintf(stdout, "CRC:  %08lx %08lx %s\n", crc, InflatedCRC,	(crc != InflatedCRC)?"**error**":"");
	fprintf(stdout, "Size: %08lx %08lx %s\n", size, InflatedSize, (size != InflatedSize)?"**error**":"");
	fprintf(stdout, "Time for decompression was %.2f seconds\n", (float)work_time/1000);
	fprintf(stdout, "Throughput is %d kByte/s\n", InflatedSize/work_time);

   return 0;
}
