/*->c.g3 */

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


#include "h.os"


#include "h.fsx"
#include "h.buffer"
#include "h.code"

#include "h.g3"



#define PAD_NONE       0
#define PAD_BYTE       1
#define PAD_NIBBLE     2
#define PAD_ODD_NIBBLE 3


#define MSB_FIRST 0


static int output_fill_order;
static int encode_buffer;
static int encode_position;
static unsigned char * encode_mask;
static unsigned char * encode_head_mask;
static unsigned char * encode_tail_mask;
static putfn putbyte;

       int g3outcount;


/*
 *      initialize the bit-encoding routines
 */

void initialize_encode(void)
{
  static unsigned char msb_mask [8] =
    { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
  static unsigned char lsb_mask [8] =
    { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
  static unsigned char msb_head_mask [8] =
    { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };
  static unsigned char lsb_head_mask [8] =
    { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
  static unsigned char msb_tail_mask [8] =
    { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
  static unsigned char lsb_tail_mask [8] =
    { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };


  output_fill_order=MSB_FIRST;        /* some implementations might
                                                want to change this */
  encode_buffer = 0;
  encode_position = 0;
  if(output_fill_order == MSB_FIRST)
  {
   encode_mask = msb_mask;
   encode_head_mask = msb_head_mask;
   encode_tail_mask = msb_tail_mask;
  }
  else /* output_fill_order == LSB_FIRST */
  {
   encode_mask = lsb_mask;
   encode_head_mask = lsb_head_mask;
   encode_tail_mask = lsb_tail_mask;
  }
}





/*
 *      append a codeword of the specified length in bits to the end
 *      of the current output file
 */

os_error * encode_word(buffer * dest,unsigned int codeword,int length)
{
 /*********
        There are two versions of this procedure.  One or the other
        is to be commented out.  The first is somewhat faster,
        especially on a 386 because it makes use of the barrel
        shifter.  Its limitation is the assumption that the output
        file is filled most-significant-bit first.  The second is
        a little slower, but doesn't make the assumption.
 ********/

/* the faster routine, with the assumption . . . */

 os_error * ep;
 int first_count;
 int second_count;
 int remaining;
 int new_encode_position;

 static char limit_8[]={0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8,
        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};


 ep=NULL;

 if(length)
 {
  new_encode_position=limit_8[encode_position+length];
  first_count=new_encode_position-encode_position;
  encode_buffer|=(codeword>>(encode_position+8));
  encode_position=new_encode_position;
  remaining=length-first_count;

  if(encode_position > 7)
  {
   ep=putbyte(dest,encode_buffer);
   g3outcount++;

   if(remaining)
   {
    encode_position=limit_8[remaining];
    second_count=encode_position;
    remaining-=second_count;
    encode_buffer=((unsigned char)(codeword>>(8-first_count)))
                & encode_head_mask [second_count-1];

    if(encode_position>7)
    {
     ep=putbyte(dest,encode_buffer);
     g3outcount++;

     if(remaining)
     {
      encode_buffer=((unsigned char)(codeword<<first_count))
                & encode_head_mask [remaining-1];
      encode_position = remaining;
     }
     else
     { 
      encode_buffer=0;
      encode_position=0;
     }
    }
   }
   else
   {
    encode_buffer=0;
    encode_position=0; 
   }
  }
 }

 return(ep);
}




static int EOL_code=0x0010;
static int EOL_length=12;



static os_error * encode_black_code(buffer * dest,int runlength,int * rem)
{
  static int term_codes [64] =
  {
    0x0DC0, 0x4000, 0xC000, 0x8000, 0x6000, 0x3000,
    0x2000, 0x1800, 0x1400, 0x1000, 0x0800, 0x0A00,
    0x0E00, 0x0400, 0x0700, 0x0C00, 0x05C0, 0x0600,
    0x0200, 0x0CE0, 0x0D00, 0x0D80, 0x06E0, 0x0500,
    0x02E0, 0x0300, 0x0CA0, 0x0CB0, 0x0CC0, 0x0CD0,
    0x0680, 0x0690, 0x06A0, 0x06B0, 0x0D20, 0x0D30,
    0x0D40, 0x0D50, 0x0D60, 0x0D70, 0x06C0, 0x06D0,
    0x0DA0, 0x0DB0, 0x0540, 0x0550, 0x0560, 0x0570,
    0x0640, 0x0650, 0x0520, 0x0530, 0x0240, 0x0370,
    0x0380, 0x0270, 0x0280, 0x0580, 0x0590, 0x02B0,
    0x02C0, 0x05A0, 0x0660, 0x0670
  };
  static int makeup_codes [40] =
  {
    0x03C0, 0x0C80, 0x0C90, 0x05B0, 0x0330, 0x0340,
    0x0350, 0x0360, 0x0368, 0x0250, 0x0258, 0x0260,
    0x0268, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0,
    0x03B8, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02D0,
    0x02D8, 0x0320, 0x0328, 0x0100, 0x0180, 0x01A0,
    0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0170,
    0x01C0, 0x01D0, 0x01E0, 0x01F0
  };
  static int term_lengths [64] =
  {
    10, 3, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10,
    10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
    12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
    12, 12, 12, 12, 12, 12, 12, 12, 12, 12
  };
  static int makeup_lengths [40] =
  {
    10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
    13, 13, 13, 13, 13, 13, 13, 13, 13, 11, 11, 11, 12, 12, 12, 12, 12, 12,
    12, 12, 12, 12
  };

  os_error * ep;
  int        remaining;

  if(runlength<64)
  {
   ep=encode_word(dest,term_codes[runlength],term_lengths [runlength]);
   *rem=0;
  }
  else
  if(runlength<2561)
  {
   int x;
   x=(runlength - 64) / 64;
   ep=encode_word(dest,makeup_codes[x],makeup_lengths[x]);
   remaining=runlength-(64*(1 + x));
   if(!remaining && !ep) ep=encode_word(dest,term_codes[0],term_lengths[0]);
   *rem=remaining;
  }
  else
  {
   ep=encode_word(dest,makeup_codes[39],makeup_lengths [39]);
   *rem=(runlength-2560);
  }

 return(ep);
}


os_error * g3j_encode_black(buffer * dest,int runlength)
{
 os_error * ep;

 ep=encode_black_code(dest,runlength,&runlength);
 while(runlength && !ep) ep=encode_black_code(dest,runlength,&runlength);
 
return(ep);
}





static os_error * encode_white_code(buffer * dest,int runlength,int * rem)
{
  static int term_codes [64] =
  {
    0x3500, 0x1c00, 0x7000, 0x8000, 0xb000, 0xc000,
    0xe000, 0xf000, 0x9800, 0xa000, 0x3800, 0x4000,
    0x2000, 0x0c00, 0xd000, 0xd400, 0xa800, 0xac00,
    0x4e00, 0x1800, 0x1000, 0x2e00, 0x0600, 0x0800,
    0x5000, 0x5600, 0x2600, 0x4800, 0x3000, 0x0200,
    0x0300, 0x1a00, 0x1b00, 0x1200, 0x1300, 0x1400,
    0x1500, 0x1600, 0x1700, 0x2800, 0x2900, 0x2a00,
    0x2b00, 0x2c00, 0x2d00, 0x0400, 0x0500, 0x0a00,
    0x0b00, 0x5200, 0x5300, 0x5400, 0x5500, 0x2400,
    0x2500, 0x5800, 0x5900, 0x5a00, 0x5b00, 0x4a00,
    0x4b00, 0x3200, 0x3300, 0x3400
  };
  static int makeup_codes [40] =
  {
    0xd800, 0x9000, 0x5c00, 0x6e00, 0x3600, 0x3700,
    0x6400, 0x6500, 0x6800, 0x6700, 0x6600, 0x6680,
    0x6900, 0x6980, 0x6a00, 0x6a80, 0x6b00, 0x6b80,
    0x6c00, 0x6c80, 0x6d00, 0x6d80, 0x4c00, 0x4c80,
    0x4d00, 0x6000, 0x4d80, 0x0100, 0x0180, 0x01a0,
    0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0170,
    0x01c0, 0x01d0, 0x01e0, 0x01f0
  };
  static int term_lengths [64] =
  {
    8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
  };
  static int makeup_lengths [40] =
  {
    5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 6, 9, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
  };

  os_error * ep;
  int        remaining;


  if(runlength<64)
  {
   ep=encode_word(dest,term_codes[runlength],term_lengths[runlength]);
   *rem=0;
  }
  else
  if(runlength<2561)
  {
   int x;
   x=(runlength-64)/64;
   ep=encode_word(dest,makeup_codes[x],makeup_lengths[x]);
   remaining=runlength-(64*(1+x));
   if(!remaining && !ep) ep=encode_word(dest,term_codes[0],term_lengths[0]);
   *rem=remaining;
  }
  else
  {
   ep=encode_word(dest,makeup_codes[39],makeup_lengths[39]);
   *rem=(runlength-2560);
  }

 return(ep);
}



os_error * g3j_encode_white(buffer * dest,int runlength)
{
 os_error * ep;

 ep=encode_white_code(dest,runlength,&runlength);
 while(runlength && !ep) ep=encode_white_code(dest,runlength,&runlength);

 return(ep);
}



void g3j_initialize(putfn putf)
{
 putbyte=putf;
 initialize_encode();
}



os_error * g3j_encode_pad(buffer * dest,int pad_type)
{
 os_error * ep;
 int        bits_to_pad;

 ep=NULL;

 switch (pad_type)
 {
       case PAD_NONE:
                     break;

       case PAD_BYTE:
                     if(encode_position)/* if not already on a byte boundary */
                     ep=encode_word(dest,0,8-encode_position);
                     break;

     case PAD_NIBBLE:
                     bits_to_pad=(8-encode_position) & 3;
                     if(bits_to_pad) ep=encode_word(dest,0,bits_to_pad);
                     break;

 case PAD_ODD_NIBBLE:
                     if(encode_position>0 && encode_position<4)
                     {
                      bits_to_pad=4-encode_position;
                      ep=encode_word(dest,0,bits_to_pad);
                     }
                     else
                     if(encode_position>4)
                     {
                      bits_to_pad=12-encode_position;
                      ep=encode_word(dest,0,bits_to_pad);
                     }
                     break;

             default:
                     break;
 }

 return(ep);
}




os_error * g3j_padnull(buffer * dest,int count)
{
 os_error * ep;
 int        i;

 ep=NULL;

 if(count>0)
 {
  ep=g3j_encode_pad(dest,PAD_BYTE);
  if(!ep)
  {
   for(i=0;i<count;i++) encode_word(dest,0,8);
  }
 }

 return(ep);
}



os_error * g3j_encode_new_row(buffer * dest)
{
 os_error * ep;

 ep=encode_word(dest,EOL_code,EOL_length);

/* ep=g3j_encode_pad(dest,PAD_BYTE); */

 return(ep);
}


os_error * g3j_eop(buffer * dest)
{
 os_error * ep;
 int        i;

 for(i=0;i<6;i++)
 {
  ep=encode_word(dest,EOL_code,EOL_length);
  if(ep) break;
 }

 ep=g3j_encode_pad(dest,PAD_BYTE);

 return(ep);
}

