/**********************************************************************
 * ISO MPEG Audio Subgroup Software Simulation Group (1996)
 * ISO 13818-3 MPEG-2 Audio Decoder - Lower Sampling Frequency Extension
 *
 * $Id: huffman.c,v 1.2 1996/03/28 03:13:37 rowlands Exp $
 *
 * $Log: huffman.c,v $
 * Revision 1.2  1996/03/28 03:13:37  rowlands
 * Merged layers 1-2 and layer 3 revisions
 *
 * Revision 1.1  1996/02/14 03:45:52  rowlands
 * Initial revision
 *
 * Received from FhG
 **********************************************************************/
/**********************************************************************
 *   date   programmers                comment                        *
 *27.2.92   F.O.Witte                  (ITT Intermetall)              *
 *				       email: otto.witte@itt-sc.de    *
 *				       tel:   ++49 (761)517-125	      *
 *				       fax:   ++49 (761)517-880	      *
 *12.6.92   J. Pineda                  Added sign bit to decoder.     *
 * 08/24/93 M. Iwadare                 Changed for 1 pass decoding.   *
 *--------------------------------------------------------------------*
 *  7/14/94 Juergen Koller      Bug fixes in Layer III code           *
 *--------------------------------------------------------------------*
 * 12/16/96 Johan Hagman	Adapted for Solaris (mpeg3play 0.9)   *
 *********************************************************************/	

#include "common.h"
#include "huffman.h"
#include "stdlib.h"
     
HUFFBITS dmask = (HUFFBITS)1 << (sizeof(HUFFBITS)*8-1);
unsigned int hs = sizeof(HUFFBITS)*8;

struct huffcodetab ht[HTN];	/* array of all huffcodtable headers	*/
				/* 0..31 Huffman code table 0..31	*/
				/* 32,33 count1-tables			*/

/* read the huffman encode table */
int read_huffcodetab(FILE *fi)
{

  char line[100],command[40],huffdata[40];
  unsigned int t,i,j,k,nn,x,y,n=0;
  unsigned int xl, yl, len;
  HUFFBITS h;
  int	hsize;
  
  hsize = sizeof(HUFFBITS)*8; 
  do {
      fgets(line,99,fi);
  } while ((line[0] == '#') || (line[0] < ' ') );
  
  do {    
    while ((line[0]=='#') || (line[0] < ' ')) {
      fgets(line,99,fi);
    } 

    sscanf(line,"%s %s %u %u %u",command,ht[n].tablename,
			         &xl,&yl,&ht[n].linbits);
    if (strcmp(command,".end")==0)
      return n;
    else if (strcmp(command,".table")!=0) {
      fprintf(stderr,"huffman table %u data corrupted\n",n);
      return -1;
    }
    ht[n].linmax = (1<<ht[n].linbits)-1;
   
    sscanf(ht[n].tablename,"%u",&nn);
    if (nn != n) {
      fprintf(stderr,"wrong table number %u\n",n);
      return(-2);
    } 

    ht[n].xlen = xl;
    ht[n].ylen = yl;

    do {
      fgets(line,99,fi);
    } while ((line[0] == '#') || (line[0] < ' '));

    sscanf(line,"%s %u",command,&t);
    if (strcmp(command,".reference")==0) {
      ht[n].ref   = t;
      ht[n].table = ht[t].table;
      ht[n].hlen  = ht[t].hlen;
      if ( (xl != ht[t].xlen) ||
           (yl != ht[t].ylen)  ) {
        fprintf(stderr,"wrong table %u reference\n",n);
        return (-3);
      };
      do {
        fgets(line,99,fi);
      } while ((line[0] == '#') || (line[0] < ' ') );
    } 
    else {
      ht[n].ref  = -1;
      ht[n].table=(HUFFBITS *) calloc(xl*yl,sizeof(HUFFBITS));
      if (ht[n].table == NULL) {
         fprintf(stderr,"unsufficient heap error\n");
         return (-4);
      }
      ht[n].hlen=(unsigned char *) calloc(xl*yl,sizeof(unsigned char));
      if (ht[n].hlen == NULL) {
         fprintf(stderr,"unsufficient heap error\n");
         return (-4);
      }
      for (i=0; i<xl; i++) {
        for (j=0;j<yl; j++) {
	  if (xl>1) 
            sscanf(line,"%u %u %u %s",&x, &y, &len,huffdata);
	  else 
            sscanf(line,"%u %u %s",&x,&len,huffdata);
          h=0;k=0;
	  while (huffdata[k]) {
            h <<= 1;
            if (huffdata[k] == '1')
              h++;
            else if (huffdata[k] != '0'){
              fprintf(stderr,"huffman-table %u bit error\n",n);
              return (-5);
            };
            k++;
          };
          if (k != len) {
           fprintf(stderr,
              "warning: wrong codelen in table %u, pos [%2u][%2u]\n",
	       n,i,j);
          };
          ht[n].table[i*xl+j] = h;
          ht[n].hlen[i*xl+j] = (unsigned char) len;
	  do {
            fgets(line,99,fi);
          } while ((line[0] == '#') || (line[0] < ' '));
        }
      }
    }
    n++;
  } while (1);
}

#ifndef BUILTIN_TABLES

/* Read the huffman decoder table */
int read_decoder_table(FILE *fi)
{
    int			n, i, nn, t;
    unsigned int	v0, v1;
    char		command[100], line[100];

    for (n = 0; n < HTN; n++) {
	/* .table number treelen xlen ylen linbits */ 
	do {
	    fgets(line, 99, fi);
	} while ((line[0] == '#') || (line[0] < ' '));
     
	sscanf(line,"%s %s %u %u %u %u", command, ht[n].tablename,
		&ht[n].treelen, &ht[n].xlen, &ht[n].ylen, &ht[n].linbits);
	if (strcmp(command, ".end") == 0)
	    return n;
	else if (strcmp(command,".table") != 0) {
	    fprintf(stderr, "huffman table %u data corrupted\n",n);
	    return -1;
	}
	ht[n].linmax = (1 << ht[n].linbits) - 1;
   
	sscanf(ht[n].tablename, "%u", &nn);
	if (nn != n) {
	    fprintf(stderr, "wrong table number %u\n", n);
	    return -2;
	} 
	do {
	    fgets(line, 99, fi);
	} while ((line[0] == '#') || (line[0] < ' '));

	sscanf(line, "%s %u", command, &t);

	if (strcmp(command, ".reference") == 0) {
	    ht[n].ref		= t;
	    ht[n].val		= ht[t].val;
	    ht[n].treelen	= ht[t].treelen;
	    if ((ht[n].xlen != ht[t].xlen) || (ht[n].ylen != ht[t].ylen)) {
		fprintf(stderr,"wrong table %u reference\n",n);
		return -3;
	    };
	    while ((line[0] == '#') || (line[0] < ' ') ) {
		fgets(line,99,fi);
	    }

	} else if (strcmp(command,".treedata") == 0) {

	    ht[n].ref	= -1;
	    ht[n].val	= (unsigned char (*)[2]) 
	    calloc(2 * (ht[n].treelen), sizeof(unsigned char));
	    if (ht[n].val == NULL) {
		fprintf(stderr, "huffman.c: heap error at table %d\n", n);
		exit(-10);
	    }
	    for (i = 0; i < ht[n].treelen; i++) {
		fscanf(fi, "%x %x", &v0, &v1);
		ht[n].val[i][0] = (unsigned char)v0;
		ht[n].val[i][1] = (unsigned char)v1;
	    }
	    fgets(line, 99, fi);	/* read the rest of the line */

	} else {
	    fprintf(stderr,"huffman decodertable error at table %d\n",n);
	}
    }
    return n;
}

#endif


/* do the huffman coding,  */
/* note! for counta,countb - the 4 bit value is passed in y, set x to 0 */
/* return value: 0-no error, 1 decode error				*/
void huffman_coder(
    unsigned int x,	 	/* x-value */
    unsigned int y, 		/* y-value */
    struct huffcodetab *h, 	/* pointer to huffman code record  */
    Bit_stream_struc *bs)	/* pointer to open write bitstream */
{
  HUFFBITS huffbits; /* data left aligned */
  HUFFBITS linbitsX; 
  HUFFBITS linbitsY;
  unsigned int len;
  unsigned int xl1 = h->xlen-1;
  unsigned int yl1 = h->ylen-1;
  linbitsX = 0;
  linbitsY = 0;
  if (h->table == NULL) return;
  if (((x < xl1) || (xl1==0)) && (y < yl1)) {
    huffbits = h->table[x*(h->xlen)+y];
    len = h->hlen[x*(h->xlen)+y];
    putbits(bs,huffbits,len);
    return;
  }  
  else if (x >= xl1) {
    linbitsX = x-xl1;
    if (linbitsX > h->linmax) {
      fprintf(stderr,"warning: Huffman X table overflow\n");
      linbitsX= h->linmax;
    };
    if (y >= yl1) {
      huffbits = h->table[(h->ylen)*(h->xlen)-1];
      len = h->hlen[(h->ylen)*(h->xlen)-1];
      putbits(bs,huffbits,len);
      linbitsY = y-yl1;
      if (linbitsY > h->linmax) {
        fprintf(stderr,"warning: Huffman Y table overflow\n");
        linbitsY = h->linmax;
      };
      if (h->linbits) {
        putbits(bs,linbitsX,h->linbits);
        putbits(bs,linbitsY,h->linbits);
      }
    } 
    else { /* x>= h->xlen, y<h->ylen */
      huffbits = h->table[(h->ylen)*xl1+y];
      len = h->hlen[(h->ylen)*xl1+y];
      putbits(bs,huffbits,len);
      if (h->linbits) {
        putbits(bs,linbitsX,h->linbits);
      }
    }
  }
  else  { /* ((x < h->xlen) && (y>=h->ylen)) */
    huffbits = h->table[(h->ylen)*x+yl1];
    len = h->hlen[(h->ylen)*x+yl1];
    putbits(bs,huffbits,len);
    linbitsY = y-yl1;
    if (linbitsY > h->linmax) {
      fprintf(stderr,"warning: Huffman Y table overflow\n");
      linbitsY = h->linmax;
    };
    if (h->linbits) {
       putbits(bs,linbitsY,h->linbits);
    }
  }
}


/*
 *  Do the huffman-decoding
 *  Note! for counta,countb -the 4 bit value is returned in y, discard x
 */
int huffman_decoder(
    struct huffcodetab *h,	/* pointer to huffman code record*/
    /* unsigned */ int *x, 	/* returns decoded x value*/
    /* unsigned */ int *y,	/* returns decoded y value*/
    int *v,
    int *w)
{  
    HUFFBITS	level;
    int		point = 0;
    int		error = 1;

    level = dmask;
    if (h->val == NULL)
	return 2;

    /* Table 0 needs no bits */
    if ( h->treelen == 0) {
	*x = *y = 0;
	return 0;
    }

    /* Lookup in Huffman table */

    do {
	if (h->val[point][0]==0) {	/* end of tree */
	    *x = h->val[point][1] >> 4;
	    *y = h->val[point][1] & 0xf;

	    error = 0;
	    break;
	} 
	if (hget1bit()) {
	    while (h->val[point][1] >= MXOFF) point += h->val[point][1]; 
	    point += h->val[point][1];
	} else {
	    while (h->val[point][0] >= MXOFF) point += h->val[point][0]; 
	    point += h->val[point][0];
	}
	level >>= 1;
    } while (level || (point < ht->treelen) );
  
    /* Check for error */
  
    if (error) { /* set x and y to a medium value as a simple concealment */
	fprintf(stderr, "Illegal Huffman code in data.\n");
	*x = ((h->xlen-1) << 1);
	*y = ((h->ylen-1) << 1);
    }

    /* Process sign encodings for quadruples tables */

    if (h->tablename[0] == '3' && (h->tablename[1] == '2' ||
			h->tablename[1] == '3')) {
	*v = (*y>>3) & 1;
	*w = (*y>>2) & 1;
	*x = (*y>>1) & 1;
	*y = *y & 1;

	/* v, w, x and y are reversed in the bitstream. */
	/* Switch them around to make test bistream work.*/
     
	/* {int i=*v; *v=*y; *y=i; i=*w; *w=*x; *x=i;}  MI */

	if (*v) {
	    if (hget1bit() == 1)
		*v = -*v;
	}
	if (*w) {
	    if (hget1bit() == 1)
		*w = -*w;
	}
	if (*x) {
	    if (hget1bit() == 1)
		*x = -*x;
	}
	if (*y) {
	    if (hget1bit() == 1)
		*y = -*y;
	}
    } else {
	/* Process sign and escape encodings for dual tables */

	/* x and y are reversed in the test bitstream.*/
	/* Reverse x and y here to make test bitstream work.*/

	/*    removed 11/11/92 -ag  
	 *	{int i=*x; *x=*y; *y=i;} 
	 */      

	if (h->linbits)
	    if ((h->xlen-1) == *x) 
		*x += hgetbits(h->linbits);
	if (*x) {
	    if (hget1bit() == 1)
		*x = -*x;
	}
	if (h->linbits)	  
	    if ((h->ylen-1) == *y)
		*y += hgetbits(h->linbits);
	if (*y) {
	    if (hget1bit() == 1)
		*y = -*y;
	}
    }
    return error;  
}
