#include "pk.h"
#include "pxl.h"

#define PK_PRE 247
#define PK_ID 89
#define PK_POST 245
#define PK_NOP 246
#define PK_YYY 244
#define PK_XXX1 240
#define PK_XXX2 241
#define PK_XXX3 242
#define PK_XXX4 243

#define pk4() swap4 (((long *) (pk += 4))[-1])
#define upk3() swap4 ((((long *) (pk += 3))[-1] & 0xFFFFFF))
#define upk2() uswap2 (((unsigned short *) (pk += 2))[-1])
#define spk2() swap2 (((short *) (pk += 2))[-1])
#define spk() ((char *) ++pk)[-1]
#define upk() (++pk)[-1]

#if __convex__			/* || other big endian */

#define swap4(x) (x)
#define swap2(x) (x)
#define uswap2(x) (x)

#elif __linux__

static __inline__ long swap4 (long x)
{
  register unsigned long tmp __asm__ ("ax") = x;
  __asm__ __volatile__ ("xchgb %%al,%%ah\n\t"
		"rorl $16,%%eax\n\t"
		"xchgb %%al,%%ah"
		: "=a" (tmp) : "a" (tmp) );
  return tmp;
}

static __inline__ short swap2 (short x)
{
  register short tmp __asm__ ("ax") = x;
  __asm__ __volatile__ ("xchgb %%al,%%ah" : "=a" (tmp) : "a" (tmp));
  return tmp;
}

static __inline__ unsigned short uswap2 (unsigned short x)
{
  register unsigned short tmp __asm__ ("ax") = x;
  __asm__ __volatile__ ("xchgb %%al,%%ah" : "=a" (tmp) : "a" (tmp));
  return tmp;
}

#endif

/*
typedef struct L_HDR L_HDR;
typedef struct M_HDR M_HDR;
typedef struct S_HDR S_HDR;

struct L_HDR {
    unsigned char flag;
    long pl,cc,tfm,dx,dy,w,h,hoff,voff;};

struct M_HDR {
    unsigned char flag;
    unsigned short pl;
    union {
	unsigned char u_cc;
	long u_tfm;
    } u;
    unsigned short dm,w,h;
    short xoff,voff;};
    
struct S_HDR {
    unsigned char flag,pl;
    union {
	unsigned char u_cc;
	long u_tfm;
    } u;
    unsigned char dm,w,h;
    char hoff,voff;};
*/

font_is_pk (px, pk, sz)
    struct pxltail *px;		/* an empty pxltail */
    unsigned char *pk;		/* hypothetical pk file */
    int sz;			/* size of file */
{
  if (pk[0] != PK_PRE || pk[1] != PK_ID) return 0;
  else {
    unsigned char *pkend = &pk[sz];
    while (*--pkend != PK_POST) {
      if (*pkend != PK_NOP) return 0;
    }
  }
	
  pk += 2;			/* pk_pre, pk_id */
  pk += upk();			/* comment */
  px->px_designsize = pk4();	/* design size */
  px->px_checksum = pk4();	/* checksum */
  px->px_magnification = .5 + (pk4() * 72.27 * 5. / 65536.); /* hppp */
  pk += 4;			/* vppp */

  for (;;) {
    long flag;
    unsigned char *flagloc;

    while (*pk >= 240) {	/* skip specials */
      switch (*pk) {
      case PK_POST: px->px_fmt = PKFMT; return 1;
      case PK_NOP: pk++; continue;
      case PK_YYY: pk += 5; continue;
      case PK_XXX1: ++pk; pk += upk(); continue;
      case PK_XXX2: ++pk; pk += upk2(); continue;
      case PK_XXX3: ++pk; pk += upk3(); continue;
      case PK_XXX4: ++pk; pk += pk4(); continue;
      default: return 0;
      }
    }

    flagloc = pk;		/* start of a char definition */
    flag = upk() & 7;
    if (flag == 7) {		/* long */
      long len = pk4();
      long c = pk4();
      struct chinfo *ch = &px->px_info[c];
      ch->ch_TFMwidth = pk4();
      pk += 4;			/* device width will be loaded later */
      pk += 4;			/* device height, assumed 0 */
      ch->ch_width = pk4();
      ch->ch_height = pk4();
      ch->ch_xoffset = pk4();
      ch->ch_yoffset = pk4();
      pk = flagloc + len + 9;
      px->px_info[c].ch_raster = (unsigned long *) flagloc;
    }
    else if (flag > 3) {
      long len = ((flag - 4) << 16) + upk2();
      long c = upk();
      struct chinfo *ch = &px->px_info[c];
      ch->ch_TFMwidth = upk3();
      pk += 2;			/* ** device width */
      ch->ch_width = upk2();
      ch->ch_height = upk2();
      ch->ch_xoffset = spk2();
      ch->ch_yoffset = spk2();
      pk = flagloc + len + 4;
      px->px_info[c].ch_raster = (unsigned long *) flagloc;
    }
    else {
      long len = (flag << 8) + upk();
      long c = upk();
      struct chinfo *ch = &px->px_info[c];
      ch->ch_TFMwidth = upk3(); /* TFM width */
      pk += 1;			/* ** device width */
      ch->ch_width = upk();
      ch->ch_height = upk();
      ch->ch_xoffset = spk();
      ch->ch_yoffset = spk();
      pk = flagloc + len + 3;
      px->px_info[c].ch_raster = (unsigned long *) flagloc;
    }
  }
}

static unsigned char *pk;
static long repeat_count;
static long odd, bitpos, remdr;	/* state variables for get routines */

static long power[] = 
{
  1<< 0,1<< 1,1<< 2,1<< 3,1<< 4,1<< 5,1<< 6,1<< 7,1<< 8,1<< 9,
  1<<10,1<<11,1<<12,1<<13,1<<14,1<<15,1<<16,1<<17,1<<18,1<<19,
  1<<20,1<<21,1<<22,1<<23,1<<24,1<<25,1<<26,1<<27,1<<28,1<<29,
  1<<30,1<<31,
};

#define X 0x7FFFFFFF
static long gpower[] = 
{
  X>>31,X>>30,
  X>>29,X>>28,X>>27,X>>26,X>>25,X>>24,X>>23,X>>22,X>>21,X>>20,
  X>>19,X>>18,X>>17,X>>16,X>>15,X>>14,X>>13,X>>12,X>>11,X>>10,
  X>> 9,X>> 8,X>> 7,X>> 6,X>> 5,X>> 4,X>> 3,X>> 2,X>> 1,X>> 0,
  0xFFFFFFFF,
};
#undef X

long *pktopxl (p, cwidth)
    unsigned char *p;		/* pk packed string */
    int *cwidth;		/* where to store char width */

/* 
 *  Malloc and return a pxl-format raster string.
 */

{
  long flag, t, pxlw, pxlh;
  pk = p;			/* start at the beginning */

  flag = upk();
  t = flag & 7;
  if (t == 7) {			/* long */
    *cwidth = (swap4 (((long *) pk)[3]) + 32768) >> 16;
    pxlw = swap4 (((long *) pk)[5]);
    pxlh = swap4 (((long *) pk)[6]);
    pk += 36;
  }
  else if (t > 3) {		/* medium */
    *cwidth = uswap2 (((unsigned short *) pk)[3]);
    pxlw = uswap2 (((unsigned short *) pk)[4]);
    pxlh = uswap2 (((unsigned short *) pk)[5]);
    pk += 16;
  }
  else {			/* short */
    *cwidth = pk[5];
    pxlw = pk[6];
    pxlh = pk[7];
    pk += 10;
  }

  {
    long word_width = (pxlw + 31) >> 5;
    long byte_height = (pxlh + 7) & -8;
    long *pxlp = (long *) malloc (byte_height * word_width * 4);
    long *retval = pxlp;
    long dyn_f = flag >> 4;
    long turn_on = (flag & 8) != 0;

    /* pxl format already produces rows padded to a word (4-byte) boundary.
       For greater transposing ease, also pad the number of rows to be
       a multiple of 8. */ 
    byte_height -= pxlh;
    if (byte_height)
      bzero (pxlp + pxlh * word_width, byte_height * word_width * 4);

    if (dyn_f == 14) {		/* bit packed */
      bitpos = 0;
      while (--pxlh >= 0) {
	long word_weight = 31;	/* bit position in current word */
	long word = 0;		/* word being assembled */
	long j;
	for (j = pxlw; --j >= 0;) {
	  if (get_bit()) word |= power[word_weight];
	  if (--word_weight < 0) {
	    *pxlp++ = word; word = 0; word_weight = 31;
	  }
	}
	if (word_weight < 31) {
	  *pxlp++ = word;
	}
      }
    }
    else {			/* run packed */
      long rows_left = pxlh;	/* pxl row number */
      register long h_bit = pxlw; /* bits left in current row */
      register long w_bit = 32;	/* bits left in current word */
      register long word = 0;	/* word being assembled */

      repeat_count = 0;		/* global set when repeat count seen
				   by pk_packed_num */
      odd = 1;			/* start off pk_packed_num right */
      while (rows_left > 0) {
	register long count = pk_packed_num (dyn_f);
	while (count > 0) {
	  if (count < w_bit && count < h_bit) {
	    /* count ends first */
	    if (turn_on)
	      word |= gpower[w_bit] - gpower[w_bit - count];
	    h_bit -= count; w_bit -= count; count = 0;
	  }
	  else if (h_bit <= count && h_bit <= w_bit) {
	    /* row ends first */
	    if (turn_on)
	      word |= gpower[w_bit] - gpower[w_bit - h_bit];
	    *pxlp++ = word;
	    rows_left -= repeat_count + 1;
	    if (repeat_count > 0) {
	      do {bcopy (&pxlp[-word_width], pxlp, word_width*4);
		  pxlp += word_width;
		} while (--repeat_count != 0);
	    }
	    count -= h_bit; h_bit = pxlw; w_bit = 32; word = 0;
	  }
	  else {		/* word ends first */
	    if (turn_on)
	      word |= gpower[w_bit];
	    *pxlp++ = word;  
	    count -= w_bit; h_bit -= w_bit; w_bit = 32; word = 0;
	  }
	}
	turn_on ^= 1;
      }
    }
    return retval;
  }
}
    
pk_packed_num (dyn_f)
    long dyn_f;
{
  long i,j;
  i = get_nyb();
  if (i == 0) {
    do {
      j = get_nyb(); i++;
    } while (j == 0);
    while (--i >= 0) {
      j = j * 16 + get_nyb();
    }
    return j - 15 + (13 - dyn_f) * 16 + dyn_f;
  }
  else {
    if (i <= dyn_f)
      return i;
    else if (i < 14)
      return (i - dyn_f - 1) * 16 + get_nyb() + dyn_f + 1;
    else {
      if (i == 14) repeat_count = pk_packed_num(dyn_f);
      else repeat_count = 1;
      return pk_packed_num(dyn_f);
    }
  }
}

	
get_nyb ()
{
  if (odd ^= 1)
    return remdr;
  else {
    unsigned char c = *pk++;
    remdr = c & 0xF;
    return c >> 4;
  }
}

get_bit ()
{
  if ((bitpos >>= 1) == 0)
    {
      remdr = *pk++;
      bitpos = 128;
    }
  return remdr & bitpos;
}
