/* *******************************************************************
   Procedure for testing huffrle
   ******************************************************************* */
#include <time.h>
#include <string.h>
#include <sys/times.h>
#include <unistd.h>
#include "huffrle.h"


// ==================== variabili globali private ============
static FILE *Infile, *Outfile;
static int Infile_size;               // dimensione del file da comprimere
// =============================================================


// =================== prototypes =======================
int compress_file(FILE *fin, int rleflag, int bsize, FILE *fout);
void decompress_file(FILE *fin, FILE *fout);
void open_files(char *Infile_name, char *outfile_name, int decompress);
void fatal_error(char *s);
void out_of_mem(char *f);
// ======================================================


int main (int argc, char *argv[])
{
  int rleflag, decompress, block_size, bits, outfile_size;
  char c, *infile_name, *outfile_name;
  clock_t times(struct tms *buffer);
  clock_t end, start;
  struct tms r;
  double tot_time;    
  extern char *optarg; 
  extern int optind, opterr, optopt;

  if (argc<2) {
    fprintf(stderr, "Usage:\n\t");
    fprintf(stderr, "huffrle [-d] [-v] [-b blok_size] [-r rle] "
                    "[-o outfile] infile \n\n");
    exit(1);
  }

  Huffrle_Verbose=0;
  decompress=0;
  rleflag=257;                      // default no rle
  infile_name=outfile_name=NULL;
  block_size=1000;
  opterr=0;
  while ((c=getopt(argc, argv, "do:vr:b:"))  != -1) {
    switch (c) {
      case 'v':
          Huffrle_Verbose++;  break;
      case 'd':
          decompress = 1;  break;
      case 'r':
          rleflag = atoi(optarg);  break;
      case 'b':
          block_size = atoi(optarg);  break;
      case 'o':
          outfile_name = optarg;  break;
      case '?':
          fprintf(stderr, "Unknown option: %c (main)\n", optopt);
	  exit(1);
    }
  }
  if (optind<argc)
    infile_name=argv[optind];
  open_files(infile_name, outfile_name, decompress);

  // --------- start measuring time -----------------
  times(&r);
  start=(r.tms_utime=r.tms_stime);

  if(decompress)   // se comprime calcola la dimensione del file da comprimere
    decompress_file(Infile, Outfile);          
  else {
    bits = compress_file(Infile,rleflag,block_size,Outfile);
    fseek(Outfile, 0, SEEK_END);
    outfile_size=ftell(Outfile);
    fprintf(stderr, "File %s\n", infile_name);
    fprintf(stderr,"Infile_size: %d, Outfile_size: %d (%d bits)\n", 
	    Infile_size, outfile_size, bits);
  }
  fclose(Outfile);
  fclose(Infile);

  // fine della misurazione del tempo
  times(&r);
  end=(r.tms_utime+r.tms_stime);
  tot_time = ((double) (end-start))/CLK_TCK;

  if(decompress)
    fprintf(stderr,"Decompression time:  %f seconds\n", tot_time);
  else {
    fprintf(stderr,"Compression time:  %f seconds\n", tot_time);
  }
  return 0;
}

int compress_file(FILE *fin, int rleflag, int bsize, FILE *fout)
{
  plain_text pt;
  uint8 *t;
  int i,size,bits=0;

  size = Infile_size;
  assert(size>0);
  t = (uint8 *) malloc(size);
  if(t == NULL)
    out_of_mem("main");
  rewind(fin);
  for (i=0; i<size; i++)
    t[i]=getc(Infile);
  // ---- write header 
  init_bit_buffer();
  uint32_write(fout,size);
  fbit_write(fout,16,rleflag);
  uint32_write(fout,bsize);
  bits += 80;
  // ---- write huffman compressed blocks
  for(i=0;i<size;i+=bsize) {
    pt.text = t+i;
    pt.tsize=(size-i>bsize)?bsize:size-i;
    pt.asize=256;
    bits += huffrle_compr(&pt,rleflag,fout);
  }
  fbit_flush(fout);
  return bits;
}


void decompress_file(FILE *fin, FILE *fout)
{
  plain_text pt;
  uint8 *t;
  int infile_size, n, i, rleflag, bsize;

  infile_size = uint32_read(fin);
  showdn(infile_size);
  assert(infile_size>0);
  rleflag = fbit_read(fin,16);
  bsize = uint32_read(fin);
  t = (uint8 *) malloc(infile_size);
  if(t==NULL) 
    out_of_mem("decompress");
  for(n=i=0;i<infile_size;i+=bsize) {
    pt.tsize=infile_size-i;
    pt.asize=256;
    pt.text = t+i;
    n += huffrle_decompr(&pt,rleflag,fin);
  }
  assert(n==infile_size);
  for(i=0;i<n;i++)
    putc(t[i],fout);
}


/*!
   ***************************************************************
   open input and output files; initializes outfile, Infile, Infile_size
   *************************************************************** */
void open_files(char *Infile_name, char *outfile_name, int decompress)
{
  FILE *fopen(const char *path, const char *mode);

  /* ------ open input and output files ------ */
  if(Infile_name==NULL) {
    fprintf(stderr,"Please provide the input file name (open_files)\n");
    exit(1);
  }
  else {
    Infile=fopen( Infile_name, "rb"); // b is for binary: required by DOS
    if(Infile==NULL) {
      perror(Infile_name);
      exit(1);
    }
  }
  // ---- write input file length in Input_size
  if(!decompress) {  // calcola la dimensione del file da comprimere
    fseek(Infile,0,SEEK_END);
    Infile_size=ftell(Infile);
    if (Infile_size==0)   {
     fprintf(stderr,"Input file empty (open_files)\n");
     exit(1);
    }
  }
  // --- open output file
  if(outfile_name==NULL) {
    /* add ".hrl" for compression ".y" for decompression */
    outfile_name = (char *) malloc(strlen(Infile_name)+5);
    outfile_name = strcpy(outfile_name, Infile_name);
    if(decompress)
      outfile_name = strcat(outfile_name, ".y");
    else
      outfile_name = strcat(outfile_name, ".hrl");
  }
  Outfile = fopen( outfile_name, "wb"); // b is for binary: required by DOS
  if(Outfile==NULL) {
    perror(outfile_name);
    exit(1);
  }
}

void fatal_error(char *s)
{
  fprintf(stderr,"%s\n",s);
  exit(1);
}

void out_of_mem(char *f)
{
  fprintf(stderr, "Out of memory in function %s!\n", f);
  exit(1);
}
