/*
 * Most of the decoding code is taken from Ed Casas's efax.c program
 * -----------------------Mitch 12th May 1995------------------------
 */

#include <stdio.h>
#include <ctype.h>		/* ANSI C */
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <fcntl.h>		/* UNIX */
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#include <malloc.h>
#include <getopt.h>
#include "vgaui.h"
#include "miscutils.h"

/* World's fastest T.4 decoder.  Implements a tree search.  Each bit of
   each byte is tested using the T4TST(byte,bitmask) macro. This macro
   invokes the macro T4CODE(len) when it detects a valid T.4 code or on the
   first EOL after an invalid code.  The macro's parameter len will be the
   run length, -1 on a normal EOL or -2 for the EOL following an error.  1D
   coding only.

   As per T.4, run lengths >63 (make-up codes) should be added to the
   subsequent (terminating) code.  The first run of a line is white and
   colors alternate.

   Each node of the tree contains pointers to the next node for a '0' or a
   '1' bit.  NULL pointers indicate terminal nodes in which case, the run
   length (or -1 or -2) is given by the zlen or olen member and the search
   continues with the node given by the znext or onext pointer.

   The decoding tree, stored in packed format (LSB to MSB plus guard bit),
   is built by calling mktree() which returns a pointer to the initial
   value of 't4p' to be used by the T4TST macro.

 */

typedef struct dtree {
  struct dtree *zero,
   *one,
   *onext,
   *znext;
  short int zlen,
    olen;
} dtree;

/* Prototypes herein */
static void fillbuf (short int);
static dtree * unpktree (void);
static void eol (short int *, int);

#define T4CODE(b) fillbuf(b)

#define T4TST(c,m) t4p = ( c & m ) ? \
	( t4p->one  ? t4p->one  : ( T4CODE ( t4p->olen ), t4p->onext ) ) : \
	( t4p->zero ? t4p->zero : ( T4CODE ( t4p->zlen ), t4p->znext ) ) ;

#define T4NODES 228

static const short int pkdt4tree[] =
{

  7, 1, 29, 53, 243, 263, 9, 1, 11, 1, 13, 1, 15, 1, 17, 1, 19, 1, 21, 1, 23, 1, 25, 1,
  27, 1, 27, 2, -2, 31, 49, 43, 33, 75, 35, 37, 4, 10, 121, 39, 41, 173, 4, 63, 4, 0,
  79, 45, 95, 47, 93, 4, 1, 71, 51, 167, 4, 2, 55, 61, 57, 59, 4, 3, 67, 69, 4, 4, 63, 65,
  4, 5, 83, 4, 6, 4, 7, 2, 128, 4, 8, 4, 9, 87, 73, 109, 4, 11, 89, 77, 99, 4, 12, 117,
  103, 81, 107, 4, 13, 85, 2, 64, 4, 14, 4, 15, 4, 16, 4, 17, 119, 91, 203, 4, 18, 4, 19, 127,
  97, 131, 4, 20, 129, 137, 101, 143, 4, 21, 123, 105, 145, 4, 22, 4, 23, 147, 111, 155,
  113, 115, 4, 24, 149, 151, 4, 25, 153, 4, 26, 4, 27, 163, 4, 28, 165, 209, 125,
  4, 29, 4, 30, 4, 31, 4, 32, 4, 33, 4, 34, 133, 135, 4, 35, 4, 36, 4, 37, 4, 38, 139, 141,
  4, 39, 4, 40, 4, 41, 4, 42, 4, 43, 4, 44, 4, 45, 4, 46, 4, 47, 4, 48, 4, 49, 4, 50, 4, 51, 4, 52,
  4, 53, 4, 54, 157, 2, 192, 159, 161, 4, 55, 4, 56, 4, 57, 4, 58, 4, 59, 4, 60, 4, 61, 4, 62,
  175, 169, 181, 171, 197, 2, 256, 2, 320, 2, 384, 2, 1664, 177, 179, 185, 2, 448, 2, 512,
  183, 191, 2, 576, 189, 187, 2, 640, 2, 704, 2, 768, 2, 832, 2, 896, 193, 195, 2, 960, 2, 1024,
  2, 1088, 2, 1152, 199, 201, 2, 1216, 2, 1280, 2, 1344, 2, 1408, 205, 207, 2, 1472, 2, 1536,
  2, 1600, 2, 1728, 235, 211, 213, 217, 215, 223, 2, 1792, 221, 219, 229, 2, 1856, 2, 1920,
  2, 1984, 2, 2048, 225, 227, 2, 2112, 2, 2176, 2, 2240, 2, 2304, 231, 233, 2, 2368, 2, 2432,
  2, 2496, 2, 2560, 237, 1, 239, 1, 241, 1, 241, 2, -1, 245, 261, 247, 265, 249, 267,
  273, 251, 271, 253, 255, 2, 12, 281, 257, 303, 259, 307, 2, 0, 2, 1, 2, 4, 2, 3, 2, 2,
  2, 6, 2, 5, 269, 2, 7, 2, 9, 2, 8, 2, 10, 2, 11, 291, 275, 277, 279, 2, 13, 283, 287, 2, 14,
  2, 15, 299, 313, 285, 383, 2, 16, 289, 309, 2, 17, 357, 423, 293, 295, 321, 297, 317,
  2, 18, 363, 327, 301, 331, 2, 19, 305, 341, 2, 20, 339, 2, 21, 349, 333, 311, 347, 2, 22,
  315, 351, 2, 23, 361, 379, 319, 389, 2, 24, 323, 371, 325, 367, 2, 25, 397, 395, 329,
  2, 26, 2, 27, 2, 28, 2, 29, 335, 337, 2, 30, 2, 31, 2, 32, 2, 33, 2, 34, 2, 35, 343, 345,
  2, 36, 2, 37, 2, 38, 2, 39, 2, 40, 2, 41, 2, 42, 2, 43, 353, 355, 2, 44, 2, 45, 2, 46, 2, 47,
  359, 393, 2, 48, 2, 49, 2, 50, 2, 51, 365, 377, 2, 52, 403, 399, 369, 401, 2, 53,
  373, 4, 64, 375, 409, 2, 54, 407, 405, 2, 55, 381, 387, 2, 56, 415, 385, 391, 2, 57, 2, 58,
  417, 2, 59, 2, 60, 419, 2, 61, 4, 256, 2, 62, 2, 63, 4, 128, 4, 192, 421, 4, 320,
  4, 384, 4, 448, 4, 512, 4, 576, 4, 640, 4, 704, 4, 768, 4, 832, 4, 896, 4, 960, 411, 413,
  4, 1024, 4, 1088, 4, 1152, 4, 1216, 4, 1280, 4, 1344, 4, 1408, 4, 1472, 4, 1536, 4, 1600,
  4, 1664, 4, 1728, 449, 425, 427, 431, 429, 437, 4, 1792, 435, 433, 443, 4, 1856, 4, 1920,
  4, 1984, 4, 2048, 439, 441, 4, 2112, 4, 2176, 4, 2240, 4, 2304, 445, 447, 4, 2368, 4, 2432,
  4, 2496, 4, 2560, 451, 1, 453, 1, 455, 1, 455, 2, -1,
  -1};

/* Unpack the decoding tree. If the LS bit is 1, the entry is a
   pointer to the next node in a code word, else it is a terminal
   node and the value is a pointer to where to continue scanning
   for the next code word and the following value is the run
   length (or -1 or -2). */

static dtree * unpktree (void)
{
  static dtree nodes[T4NODES],
   *p;
  const short int *l;
  for (p = nodes, l = pkdt4tree; *l > 0; p++) {
    if (p - nodes >= T4NODES) {
      fprintf (stderr, "error in T.4 decoding tree\n");
      break;
    }
    if (*l & 1) {
      p->zero = nodes + *l++ / 2;
    } else {
      p->znext = nodes + *l++ / 2;
      p->zlen = *l++;
    };
    if (*l & 1) {
      p->one = nodes + *l++ / 2;
    } else {
      p->onext = nodes + *l++ / 2;
      p->olen = *l++;
    };
  }
  return nodes + 1;		/* search starts at node 1 (white runs) */
}

#define MAXRUNS 2000
#define MAXLINES 2500
#define MAXCOLS	1728
static char debug = 0;
static dtree *t4p;
static int skip,
  linelen = 0;
static int reversebits = 0;

static void T4CTST(char c)  {
	int i;

	for (i = reversebits ? 0 : 7;
		 reversebits ? i<=7 : i>=0;
		 reversebits ? ++i : --i)
			T4TST(c,1<<i);
}

extern char *version;
extern char *progname;
extern vga_image image;

static void fillbuf (short int l)
{
  static int lastlen = 0,
    len = 0;
  static short int buf[MAXRUNS],
   *p = buf,
    n = MAXRUNS,
    ll = 0;

  linelen = 0;
  if (l < 0) {
    if (l == -2)
      if (debug)
	fprintf (stderr, "coding error in line %d\n", image.ht);
    if (len != lastlen) {
      if (lastlen) {
	if (debug)
	  fprintf (stderr, "bad line length = %d\n", len);
      }
      linelen = len;
      lastlen = len;
    }
    eol (buf, MAXRUNS - n);
    p = buf;
    n = MAXRUNS;
    len = 0;
    image.ht++;
  } else {
    len += l;
    linelen = len;
    if (l > 63) {
      ll = l;
    } else {
      if (n) {
	n--;
	*p++ = ll + l;
      }
      ll = 0;
    }
  }
}

static void eol (short int *buf, int rlen)
{
  int i,
    j;

#if 0
  if (!(image.ht % skip))
    return;
#endif
  if (linelen < MAXCOLS && rlen > 1) {
    image.data[image.ht] = malloc (MAXCOLS);
    memset (image.data[image.ht], PAGE_COLOR, MAXCOLS);
    for (i = j = 0; i < rlen && j + buf[i] < MAXCOLS; j += buf[i++]) {
      if (!(i % 2))
	continue;
      memset (j + image.data[image.ht], TEXT_COLOR, buf[i]);
#ifdef PRINT
      printf("%d -%d m %d 0 r ",j,image.ht,buf[i]);
#endif
    }
#ifdef PRINT
      printf("\n");
#endif
    if (image.wid != MAXCOLS) {
      for (i = 0; i < image.wid; i++)
	image.data[image.ht][i] = image.data[image.ht][i * 10 / skip];
      image.data[image.ht] = realloc (image.data[image.ht], image.wid);
    }
  } else
    image.data[image.ht] = NULL;

  if (debug  && !(image.ht % 50)) {
    fprintf (stdout, "Reading line %d\r", image.ht);
    fflush (stdout);
  }
}

#ifdef MMAP
#include <sys/mman.h>
#include <sys/stat.h>

void decode (char *fname)
{
  int fd;
  int c;
  struct stat st;
  caddr_t addr;
  long off = 0;
  long clk;

  image.ht = 0;
  skip = image.scale * 10;
  image.wid = MAXCOLS * image.scale;

  if (fname) {
    image.filename = fname;
    if (stat (image.filename, &st) || (fd = open (image.filename, O_RDONLY)) < 0) {
      fprintf (stderr, "%s: can't open `%s' for reading\n", progname, image.filename);
      exit (1);
    }
    if ((addr = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (caddr_t) -1) {
      fprintf (stderr, "%s: mapping failure for `%s'\n", progname, image.filename);
      exit (1);
    }
    close (fd);
  } else {
    fprintf (stderr, "%s: mmap() can't read from stdin\n", progname);
    exit (1);
  }

  clk = clock ();
  for (c = *(addr + off); off < st.st_size; c = *(addr + off++))
    T4CTST (c);

  munmap (addr, st.st_size);

  if (image.ht <= 1) {
    printf ("%s: `%s' is not a valid g3 file\n", progname, image.filename);
    exit (1);
  }
  if (debug)
    printf ("Read %d lines and %d cols from `%s' in (%ld CPU secs.)\n", image.ht, image.wid, image.filename, clock () - clk);
}

#else

void decode (char *fname)
{
  FILE *fp;
  int c;
  long clk;
  image.ht = 0;
  skip = image.scale * 10;
  image.wid = MAXCOLS * image.scale;

  if (fname) {
    image.filename = fname;
    if ((fp = fopen (image.filename, "r")) == NULL) {
      fprintf (stderr, "%s: can't open `%s' for reading\n", progname, image.filename);
      exit (1);
    }
  } else {
    image.filename = "stdin";
    fp = stdin;
  }

  clk = clock ();
  while ((c = getc (fp)) != EOF)
    T4CTST (c);

  fclose (fp);

  if (debug)
    printf ("Read %d lines and %d cols from `%s' in (%ld CPU secs.)\n", image.ht, image.wid, image.filename, clock () - clk);
}
#endif

#ifndef PRINT
static char keycallback(char c) {
int i;
extern short int xmax, ymax;
#include "help.h"

switch (c) {
	case '+':
		if (image.scale + 0.1 > 1.0)
			return 0;
		image.scale += 0.1;
		for (i = 0; i < image.ht; i++)
			if (image.data[i])
				free (image.data[i]);
		break;

	case '-':
		if (image.scale - 0.1 < 0.2)
			return 0;
		image.scale -= 0.1;
		for (i = 0; i < image.ht; i++)
			if (image.data[i])
				free (image.data[i]);
		break;
	case 'H':
	case 'h':
		vga_showmultiline (40, 40,
				   min(xmax-50, 570), min(ymax-50, 340),
				   YELLOW, RED, help_text, "HELP");
		break;
}
return 1;
}

void main (int argc, char **argv)
{
  int opt;
  extern char (* vga_key_callback)(char);

  vga_key_callback=keycallback;

#ifdef MALLOC_TRACE
  mal_trace(1); 
  mal_statsdump (stdout);
#endif

  version="0.5f";
  t4p = unpktree ();
  image.scale = 0.5;

  while ((opt = getopt (argc, argv, "ds:r")) != EOF)
    switch (opt) {
    case 'r':
     reversebits = 1;
     break;
    case 'd':
      debug = 1;
      break;
    case 's':
      if (!isdigit(*optarg) && *optarg != '.')
	fprintf(stderr, "%s is not a number\n", optarg), exit(1);
      image.scale = atof (optarg);
      image.scale = image.scale > 1.0 ? 1.0 : image.scale < 0 ? 0.4 : image.scale;
      break;
    }
  vga_init ();
  progname = (progname = strrchr (argv[0], '/')) ? ++progname : argv[0];

#if 1
  if ((image.data = malloc (MAXLINES * sizeof (char *))) == NULL) {
#else
  if ((image.data = malloc (MAXLINES * 10 * sizeof (char *) / skip)) == NULL) {
#endif
    fprintf (stderr, "%s: can't alloc screen (%d bytes)\n", progname, MAXLINES);
    exit (1);
  }
  decode  (argv[optind]);
  display (argv, optind);

#ifdef MALLOC_TRACE
/*  mal_statsdump (stdout);*/
#endif

}
#else /* g32ps */

#define DPI_X (72.0 * 8.268)	/* 8.268 inches wide paper */
#define DPI_Y (72.0 * 11.693)	/* 11.693 inches tall paper */

static vga_image image = {
        NULL,		/* The image data */
        MAXCOLS,	/* The width of the image */
        MAXLINES,	/* The height of the image */
        NULL,		/* The rgb colormap */
        1.0,		/* The scale of the image (0-1.0)*/
        NULL		/* The filename of the image */
};
static char *progname;
static char *version="0.2";
void main (int argc, char **argv)
{
  int lowres = 0;
  int opt;

  while ((opt = getopt (argc, argv, "lr")) != EOF)
    switch (opt) {
    case 'r':
     reversebits = 1;
     break;
    case 'l':
      lowres = 1;
    }

  t4p = unpktree ();
  progname = (progname = strrchr (argv[0], '/')) ? ++progname : argv[0];
  if ((image.data = malloc (MAXLINES * sizeof (char *))) == NULL) {
    fprintf (stderr, "%s: can't alloc page (%d bytes)\n", progname, MAXLINES);
    exit (1);
  }
  printf("\
%%!PS-HandWritten (Mitch DSouza)
%%%% g32ps (G3 Fax -> PostScript) version %s 
/m {moveto} def /r {rlineto stroke} def 0 %f translate %f %f scale
",version, DPI_Y, DPI_X/MAXCOLS, DPI_Y/MAXLINES * (lowres ? 2 : 1));
  decode (argv[optind]);
printf("showpage\n");
}
#endif
