/*
 *  TEX Device Driver  ver 2.39.1-
 *  (c) copyright 1988, 1989 by TSG, 1990-93 by SHIMA
 *
 *				July 18, 1993 : 1st edition by SHIMA
 *
 *  			output in G3 FAX format
 */

#define	EXTENDED		1
#define	BIG_DATA		1

#define MAX_A4X			1728
#define	MAX_B4X			2048
#define MAX_A4Y			2290

#if	EXTENDED
  #define MAX_FAX_WIDTH	2623
#else
  #define MAX_FAX_WIDTH	1791
#endif

#if BIG_DATA
  #define LIMIT_WIDTH 12000
#else
  #define LIMIT_WIDTH MAX_FAX_WIDTH
#endif

/*
 *		OFF_DEST*8  <= LIMIT_WIDTH
 *		OFF_DEST + FAX_BUF_SIZE < COMMON_SIZE
 */

#define	OFF_DEST	1500		/* maximal width: 12000 dot */
#define	FAX_BUF_SIZE 512


struct encode	{
	int		data;
	uchar	length;
};

static struct encode white_count_list[]={

	0x00AC,      8,	/*    0 */
	0x0038,      6,	/*    1 */
	0x000E,      4,	/*    2 */
	0x0001,      4,	/*    3 */
	0x000D,      4,	/*    4 */
	0x0003,      4,	/*    5 */
	0x0007,      4,	/*    6 */
	0x000F,      4,	/*    7 */
	0x0019,      5,	/*    8 */
	0x0005,      5,	/*    9 */
	0x001C,      5,	/*   10 */
	0x0002,      5,	/*   11 */
	0x0004,      6,	/*   12 */
	0x0030,      6,	/*   13 */
	0x000B,      6,	/*   14 */
	0x002B,      6,	/*   15 */
	0x0015,      6,	/*   16 */
	0x0035,      6,	/*   17 */
	0x0072,      7,	/*   18 */
	0x0018,      7,	/*   19 */
	0x0008,      7,	/*   20 */
	0x0074,      7,	/*   21 */
	0x0060,      7,	/*   22 */
	0x0010,      7,	/*   23 */
	0x000A,      7,	/*   24 */
	0x006A,      7,	/*   25 */
	0x0064,      7,	/*   26 */
	0x0012,      7,	/*   27 */
	0x000C,      7,	/*   28 */
	0x0040,      8,	/*   29 */
	0x00C0,      8,	/*   30 */
	0x0058,      8,	/*   31 */
	0x00D8,      8,	/*   32 */
	0x0048,      8,	/*   33 */
	0x00C8,      8,	/*   34 */
	0x0028,      8,	/*   35 */
	0x00A8,      8,	/*   36 */
	0x0068,      8,	/*   37 */
	0x00E8,      8,	/*   38 */
	0x0014,      8,	/*   39 */
	0x0094,      8,	/*   40 */
	0x0054,      8,	/*   41 */
	0x00D4,      8,	/*   42 */
	0x0034,      8,	/*   43 */
	0x00B4,      8,	/*   44 */
	0x0020,      8,	/*   45 */
	0x00A0,      8,	/*   46 */
	0x0050,      8,	/*   47 */
	0x00D0,      8,	/*   48 */
	0x004A,      8,	/*   49 */
	0x00CA,      8,	/*   50 */
	0x002A,      8,	/*   51 */
	0x00AA,      8,	/*   52 */
	0x0024,      8,	/*   53 */
	0x00A4,      8,	/*   54 */
	0x001A,      8,	/*   55 */
	0x009A,      8,	/*   56 */
	0x005A,      8,	/*   57 */
	0x00DA,      8,	/*   58 */
	0x0052,      8,	/*   59 */
	0x00D2,      8,	/*   60 */
	0x004C,      8,	/*   61 */
	0x00CC,      8,	/*   62 */
	0x002C,      8,	/*   63 */

	0x001B,      5,	/*   64 */
	0x0009,      5,	/*  128 */
	0x003A,      6,	/*  192 */
	0x0076,      7,	/*  256 */
	0x006C,      8,	/*  320 */
	0x00EC,      8,	/*  384 */
	0x0026,      8,	/*  448 */
	0x00A6,      8,	/*  512 */
	0x0016,      8,	/*  576 */
	0x00E6,      8,	/*  640 */
	0x0066,      9,	/*  704 */
	0x0166,      9,	/*  768 */
	0x0096,      9,	/*  832 */
	0x0196,      9,	/*  896 */
	0x0056,      9,	/*  960 */
	0x0156,      9,	/* 1024 */
	0x00D6,      9,	/* 1088 */
	0x01D6,      9,	/* 1152 */
	0x0036,      9,	/* 1216 */
	0x0136,      9,	/* 1280 */
	0x00B6,      9,	/* 1344 */
	0x01B6,      9,	/* 1408 */
	0x0032,      9,	/* 1472 */
	0x0132,      9,	/* 1536 */
	0x00B2,      9,	/* 1600 */
	0x0006,      6,	/* 1664 */
	0x01B2,      9 	/* 1728 */
#if	EXTENDED
	,
	0x0080,     11,	/* 1792 */
	0x0180,     11,	/* 1856 */
	0x0580,     11,	/* 1920 */
	0x0480,     12,	/* 1984 */
	0x0C80,     12,	/* 2048 */
	0x0280,     12,	/* 2112 */
	0x0A80,     12,	/* 2176 */
	0x0680,     12,	/* 2240 */
	0x0E80,     12,	/* 2304 */
	0x0380,     12,	/* 2368 */
	0x0B80,     12,	/* 2432 */
	0x0780,     12,	/* 2496 */
	0x0F80,     12 	/* 2560 */
#endif
};

static struct encode black_count_list[]={

	0x03B0,     10,	/*    0 */
	0x0002,      3,	/*    1 */
	0x0003,      2,	/*    2 */
	0x0001,      2,	/*    3 */
	0x0006,      3,	/*    4 */
	0x000C,      4,	/*    5 */
	0x0004,      4,	/*    6 */
	0x0018,      5,	/*    7 */
	0x0028,      6,	/*    8 */
	0x0008,      6,	/*    9 */
	0x0010,      7,	/*   10 */
	0x0050,      7,	/*   11 */
	0x0070,      7,	/*   12 */
	0x0020,      8,	/*   13 */
	0x00E0,      8,	/*   14 */
	0x0030,      9,	/*   15 */
	0x03A0,     10,	/*   16 */
	0x0060,     10,	/*   17 */
	0x0040,     10,	/*   18 */
	0x0730,     11,	/*   19 */
	0x00B0,     11,	/*   20 */
	0x01B0,     11,	/*   21 */
	0x0760,     11,	/*   22 */
	0x00A0,     11,	/*   23 */
	0x0740,     11,	/*   24 */
	0x00C0,     11,	/*   25 */
	0x0530,     12,	/*   26 */
	0x0D30,     12,	/*   27 */
	0x0330,     12,	/*   28 */
	0x0B30,     12,	/*   29 */
	0x0160,     12,	/*   30 */
	0x0960,     12,	/*   31 */
	0x0560,     12,	/*   32 */
	0x0D60,     12,	/*   33 */
	0x04B0,     12,	/*   34 */
	0x0CB0,     12,	/*   35 */
	0x02B0,     12,	/*   36 */
	0x0AB0,     12,	/*   37 */
	0x06B0,     12,	/*   38 */
	0x0EB0,     12,	/*   39 */
	0x0360,     12,	/*   40 */
	0x0B60,     12,	/*   41 */
	0x05B0,     12,	/*   42 */
	0x0DB0,     12,	/*   43 */
	0x02A0,     12,	/*   44 */
	0x0AA0,     12,	/*   45 */
	0x06A0,     12,	/*   46 */
	0x0EA0,     12,	/*   47 */
	0x0260,     12,	/*   48 */
	0x0A60,     12,	/*   49 */
	0x04A0,     12,	/*   50 */
	0x0CA0,     12,	/*   51 */
	0x0240,     12,	/*   52 */
	0x0EC0,     12,	/*   53 */
	0x01C0,     12,	/*   54 */
	0x0E40,     12,	/*   55 */
	0x0140,     12,	/*   56 */
	0x01A0,     12,	/*   57 */
	0x09A0,     12,	/*   58 */
	0x0D40,     12,	/*   59 */
	0x0340,     12,	/*   60 */
	0x05A0,     12,	/*   61 */
	0x0660,     12,	/*   62 */
	0x0E60,     12,	/*   63 */

	0x03C0,     10,	/*   64 */
	0x0130,     12,	/*  128 */
	0x0930,     12,	/*  192 */
	0x0DA0,     12,	/*  256 */
	0x0CC0,     12,	/*  320 */
	0x02C0,     12,	/*  384 */
	0x0AC0,     12,	/*  448 */
	0x06C0,     13,	/*  512 */
	0x16C0,     13,	/*  576 */
	0x0A40,     13,	/*  640 */
	0x1A40,     13,	/*  704 */
	0x0640,     13,	/*  768 */
	0x1640,     13,	/*  832 */
	0x09C0,     13,	/*  896 */
	0x19C0,     13,	/*  960 */
	0x05C0,     13,	/* 1024 */
	0x15C0,     13,	/* 1088 */
	0x0DC0,     13,	/* 1152 */
	0x1DC0,     13,	/* 1216 */
	0x0940,     13,	/* 1280 */
	0x1940,     13,	/* 1344 */
	0x0540,     13,	/* 1408 */
	0x1540,     13,	/* 1472 */
	0x0B40,     13,	/* 1536 */
	0x1B40,     13,	/* 1600 */
	0x04C0,     13,	/* 1664 */
	0x14C0,     13 	/* 1728 */
#if	EXTENDED
	,
	0x0080,     11,	/* 1792 */
	0x0180,     11,	/* 1856 */
	0x0580,     11,	/* 1920 */
	0x0480,     12,	/* 1984 */
	0x0C80,     12,	/* 2048 */
	0x0280,     12,	/* 2112 */
	0x0A80,     12,	/* 2176 */
	0x0680,     12,	/* 2240 */
	0x0E80,     12,	/* 2304 */
	0x0380,     12,	/* 2368 */
	0x0B80,     12,	/* 2432 */
	0x0780,     12,	/* 2496 */
	0x0F80,     12 	/* 2560 */
#endif
};

static uchar bitget_bitbuf;
static int bitget_count;
static uchar *bitget_buf;

static uchar bitout_bitbuf;
static int bitout_count;
static int bitout_bufcount;
static uchar *bitout_buf;

static int top_bit_count;
static int white_bit_count;
static int black_bit_count;

static int fax_width = MAX_A4X;			/* maximal width by bit 		*/
static int fax_max_height = MAX_A4Y;	/* maximal height of by bit		*/
static int fax_left_blank;				/* width of left blank by bit	*/
static int fax_data_width;				/* width of the data by byte	*/

static void fax_encode_line(uchar *dast, uchar *src);

static void set_FAX_ctl(void)
{
	int	ch, num;
	char *s;

	/*
		Several options will be set by reading char *f_fax
		currently A4 fine mode: 1798 x 2290 dot
	 */

	for(s = f_fax; ; ){
		ch = *s++;
		switch(toupper(ch)){

			case 'F':
				num = 0;
				while(isdigit(*s))
					num = num * 10 + *s++ - '0';
				if (!num) break;
				if ( (fax_width = num) > LIMIT_WIDTH )
					error(ILLEGAL_ARGS, "Too much width -FAX=%s", f_fax);
				if (!*s++) goto quit;
				num = 0;
				while(isdigit(*s))
					num = num * 10 + *s++ - '0';
				if (num)
					fax_max_height = num;
				break;

			default:
				break;
		}
		while(!isalpha(*s))
			if (!*s++) goto quit;		/* skip up to alphabet */
	}

quit:
	if (dviout_dimension.prt_type == FAXFORM){
		if (fax_width == MAX_B4X)
			dot_cr[FAXFORM][14] = 0x02;
		form_feed = nrm_cr[FAXFORM];
	}

	if ((fax_left_blank = fax_width - dviout_dimension.output_width) < 0){
		fax_left_blank = 0;
		fax_data_width = fax_width/8;
	}
	else{
		fax_left_blank /= 2;
		fax_data_width =  dviout_dimension.output_width/8;
	}
}


static void fax_encode_block(int h_max, int v_max, HUGE_BUF *top, int flag)
{
	int	i;
	uint y, v_bit_max;
	int	bit_no;
	uchar *src;

	for (i = fax_width/8; i >= 0; i--)
		tmp_buf[i] = 0;						/* clear sorce buffer */

	if (flag){
		bit_no = 7;
		top += (ulong)(h_max * 8 - 1) * v_max;
	}
	v_bit_max = v_max*8;

	for (y = 0; y < v_bit_max && num_cr < fax_max_height; y++, num_cr++){
		if (flag){							/* Vertical mode */
			p_sublbp(v_max, top, bit_no, fax_data_width, LEFT_IS_HIGH);
			if (!bit_no--){
				bit_no = 7;
				top++;
			}
		}
		else {								/* Horizontal mode */
			src = tmp_buf;
			for (i = 0; i < fax_data_width; i++)
				*src++ = ((BUFFER *)top)[i];
			top += h_max;
		}
		fax_encode_line(tmp_buf + OFF_DEST, tmp_buf);
	}
}


static void set_bitcount(struct encode *pt)
{
	int	data, length;

	data = pt->data;
	length = pt->length;
	while( (length -= bitout_count) >= 0 ){
		*bitout_buf++ = ((data << (8 - bitout_count))|bitout_bitbuf);
		bitout_bitbuf = 0;
		bitout_bufcount++;
		data >>= bitout_count;
		bitout_count = 8;
	}
	bitout_bitbuf |= (data << (8 - bitout_count));
	bitout_count = -length;
}

static void set_black_encode(int count);

static void set_white_encode(int count)
{
#if	BIG_DATA
	while (count >= 64){
		if(count <= MAX_FAX_WIDTH){
			set_bitcount((white_count_list + 63) + (count/64));
			break;
		}
		set_white_encode(MAX_FAX_WIDTH);
		set_black_encode(0);
		count -= MAX_FAX_WIDTH;
	}
#else
	if (count >= 64)
			set_bitcount((white_count_list + 63) + (count/64));
#endif
	set_bitcount(white_count_list + (count & 63) );
}


static void set_black_encode(int count)
{
#if	BIG_DATA
	while (count >= 64){
		if(count <= MAX_FAX_WIDTH){
			set_bitcount((black_count_list + 63) + (count/64));
			break;
		}
		set_black_encode(MAX_FAX_WIDTH);
		set_white_encode(0);
		count -= MAX_FAX_WIDTH;
	}
#else
	if (count >= 64)
		set_bitcount((black_count_list + 63) + (count/64));
#endif
	set_bitcount(black_count_list + (count & 63));
}

/*
 *		count run length (white_bit_count, black_bit_count)
 */
static void count_run_length(void)
{
	static uchar ROW[9] =
		{0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
	static uchar MASK[9] =
		{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};

							/* remaining bits in the current byte are white? */
	if (!(bitget_bitbuf &= MASK[bitget_count])){
		do{
			top_bit_count += 8;
												/* next byte is also white	*/
		} while(!(bitget_bitbuf = *bitget_buf++));
		bitget_count = 8;
	}
							/* current byte contains white and black bits 	*/
	while(!(bitget_bitbuf & ROW[bitget_count]))
		bitget_count--;									/* skip white bits 	*/
	white_bit_count = top_bit_count - (black_bit_count = bitget_count);

						/* remaining bits in the current byte are black?	*/
	if (bitget_bitbuf == MASK[bitget_count]){
		do{
			black_bit_count += 8;
												/* next byte is also black	*/
		} while((bitget_bitbuf = *bitget_buf++) == 0xff);
		bitget_count = 8;
	}
	else bitget_count--;						/* skip the top black bit	*/

							/* current byte contains white and black bits 	*/
	while(bitget_bitbuf & ROW[bitget_count])
		bitget_count--;							/* skip black bits	 		*/
	black_bit_count -= (top_bit_count = bitget_count);
}

/*
 *			encode one rastor line
 */
static void fax_encode_line(uchar *dast, uchar *src)
{
	int allruns;
	static struct encode eol_code = {0x800,12};

	src[fax_width/8+1] = 0xAA;		/* black, white, black, white,... */
	bitget_buf = src;
	bitget_bitbuf = *bitget_buf++;
	bitget_count = 8;
	top_bit_count = fax_left_blank + 8;

	bitout_bufcount = 0;
	bitout_bitbuf = 0;
	bitout_count = 8;
	bitout_buf = dast;

	set_bitcount(&eol_code);		/* set EOL */
	allruns = 0;
	for(;;){
		count_run_length();
		if((allruns += white_bit_count) < fax_width)
			set_white_encode(white_bit_count);
		else{
			set_white_encode(white_bit_count + fax_width - allruns);
			break;
		}
		if((allruns += black_bit_count) < fax_width)
			set_black_encode(black_bit_count);
		else{
			set_black_encode(black_bit_count + fax_width - allruns);
			break;
		}
		if (bitout_bufcount >= FAX_BUF_SIZE){
			pr_putnc(dast, bitout_bufcount);
			bitout_bufcount = 0;
			*dast = *bitout_buf;
			bitout_buf = dast;
		}
	}
										/* finish the last byte */
	*bitout_buf = ((bitout_count < 8)? bitout_bitbuf:0);
	pr_putnc(dast, bitout_bufcount + 1);
}
