/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:char.c 12.0$ */
/* $ACIS:char.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/lib/pmp/RCS/char.c,v $ */

#ifndef lint
static char *rcsid = "$Header:char.c 12.0$";
#endif

#include <stdio.h>
#include <utils.h>
#include <pmp_commands.h>
#include <char.h>
#include <pmp/font3812.h>

/***===================================================================***/

#define WHERE_AM_I	"char.c"
#define CH_MAXFREE	500
#define CH_CHUNKSIZE	5

int	 ch_debug= 0;
CHAR	*ch_error;

static	CHAR	*ch_freelist=	NULL;
static	int	 ch_numfree=	0;
static	int	 ch_maxfree=	CH_MAXFREE;

/***===================================================================***/

#define CH_ERR_WIDTH	16	/* Default printer error character is a	*/
#define CH_ERR_HEIGHT	19	/* 16 x 16 checkerboard.  The error	*/
#define CH_ERR_LTSPACE	0	/* character being downloaded should	*/
#define CH_ERR_RBSPACE	0	/* also be 16 pels wide with no L/T or	*/
#define CH_ERR_OFFSET	24	/* R/B space.				*/
#define CH_ERR_PTRNSIZE	38
#define CH_ERR_ENCODING	CH_RASTER

static UNSIGNED_CHAR
_ch_errpat[CH_ERR_PTRNSIZE] =	{	0x00,0x00,
					0x4A,0x2E,
					0x4B,0x29,
					0x4A,0xA9,
					0x4A,0x69,
					0x7A,0x2E,
					0x00,0x00,
					0x7B,0xCE,
					0x42,0x04,
					0x73,0x84,
					0x42,0x04,
					0x72,0x0E,
					0x00,0x00,
					0x45,0xEE,
					0x65,0x09,
					0x55,0xC9,
					0x4D,0x09,
					0x45,0xEE,
					0x00,0x00};

/***===================================================================***/

#define _ch_isfree(c)	((c)->ch_code&CH_FREED)
#define _ch_hasops(c)	((c)->ch_ops)
#define _ch_ops(c)	(_ch_hasops(c)?(c)->ch_ops:NULL)
#define _ch_next(c)	(_ch_isfree(c)?(CHAR *)(c)->ch_ptrn:NULL)

#define _ch_op_read(o,f,c,s) (o&&o->ch_o_read?(*(o)->ch_o_read)(f,c,s):FALSE)
#define _ch_op(o,c,n,d)    ((o)&&(o->n)?(*(o)->n)(c,(c)->ch_patlength,(c)->ch_ptrn):(d))
#define _ch_op_free(o,c)	_ch_op(o,c,ch_o_free,FALSE)
#define _ch_op_ptrn(o,c)	_ch_op(o,c,ch_o_ptrn,(c)->ch_ptrn)
#define _ch_op_length(o,c)	_ch_op(o,c,ch_o_length,(c)->ch_patlength)

#define _ch_alloc(c)	{\
    			   if (ch_freelist) {\
			       (c)= ch_freelist;\
			       ch_freelist= _ch_next((c));\
			   }\
			   else { (c)= (CHAR *)u_malloc(sizeof(CHAR));} }

/***===================================================================***/

ch_fix(constraint)
register1 int constraint;
{
register2 CHAR *ch;

    D_ENTRY1(ch_debug,"ch_fix(%d)\n",constraint);

    ch_maxfree= constraint*CH_CHUNKSIZE;
    if (ch_numfree<=ch_maxfree) 
	RETURN(TRUE);
    while (ch_numfree>ch_maxfree) {
	if (ch_freelist) {
	    ch= ch_freelist;
	    ch_freelist= _ch_next(ch_freelist);
	    if (ch->ch_ptrn) 
		u_free(ch->ch_ptrn);
	    ch_numfree--;
	}
	else {
	    WSGO("Can't free enough CHARs in ch_fix()\n");
	    action("good luck.\n");
	    ch_numfree=0;
	    RETURN(FALSE);
	}
    }
    RETURN(TRUE);
}

/***===================================================================***/

int
ch_init(constraint)
register1 int constraint;
{
static int beenhere= FALSE;

    D_ENTRY1(ch_debug,"ch_init(%d)\n",constraint);
    if (!beenhere) {
	ch_fix(constraint);
	_ch_alloc(ch_error);
	ch_error->ch_ht=	CH_ERR_HEIGHT;
	ch_error->ch_wid=	CH_ERR_WIDTH;
	ch_error->ch_LT=	CH_ERR_LTSPACE;
	ch_error->ch_RB=	CH_ERR_RBSPACE;
	ch_error->ch_off=	CH_ERR_OFFSET;
	ch_error->ch_patlength=	CH_ERR_PTRNSIZE;
	ch_error->ch_code=	CH_ERR_ENCODING;
	ch_error->ch_ptrn=	_ch_errpat;
	beenhere= TRUE;
    }
    RETURN(TRUE);
}

/***============================================================***/

CHAR_ops *
ch_newops(info,read,free,ptrn,length)
register2 char	  *info;
register3 int	 (*read)();
register4 int	 (*free)();
register5 char	*(*ptrn)();
register6 u_int32 (*length)();
{
register1 CHAR_ops	*co;

    D_ENTRY5(ch_debug,"ch_newops(0x%x,0x%x,0x%x,0x%x,0x%x)\n",info,read,free,
								ptrn,length);
    co= (CHAR_ops *)u_malloc(sizeof(CHAR_ops));
    co->ch_o_info=	info;
    co->ch_o_read=	read;
    co->ch_o_free=	free;
    co->ch_o_ptrn=	ptrn;
    co->ch_o_length=	length;
    RETURN(co);
}
    
/***===================================================================***/

CHAR	*
ch_new(width,height,lt,rb,off,encoding,size,pattern,ops)
int	width,height;
int	lt,rb;
int	off;
int	encoding,size;
char	*pattern;
CHAR_ops *ops;
{
register1 CHAR *ch;

    D_ENTRY4(ch_debug,"ch_new(%d,%d,%d,%d...)\n",width,height,lt,rb);
    _ch_alloc(ch);
    ch->ch_wid=		width;
    ch->ch_ht=		height;
    ch->ch_LT=		lt;
    ch->ch_RB=		rb;
    ch->ch_off=		off;
    ch->ch_code=	encoding;
    ch->ch_patlength=	size;
    ch->ch_ptrn=	pattern;
    ch->ch_ops=		ops;
    RETURN(ch);
}

/***===================================================================***/

CHAR *
ch_free(ch)
register1 CHAR *ch;
{
register2 CHAR_ops *ops;

    D_ENTRY1(ch_debug,"ch_free(0x%x)\n",ch);
    if (ch==ch_error) 
	RETURN(NULL);
    if ((ch)&&(!_ch_isfree(ch))) {
	if (_ch_hasops(ch)) 
	      ops= _ch_ops(ch);
	else  ops= NULL;

	if ((!ops)||(!_ch_op_free(ops,ch))) {
	    if (ch->ch_ptrn&&(ch->ch_code&CH_FREE_PTRN)) {
		u_free(ch->ch_ptrn);
	    }
	}
	if (ch_numfree<ch_maxfree) {
	    ch->ch_code= CH_FREED;
	    ch->ch_ptrn= (char *)ch_freelist;
	    ch_freelist= ch;
	}
    }
    RETURN(NULL);
}

/***===================================================================***/

int
ch_sizepattern(ch)
register1 CHAR *ch;
{
register2 CHAR_ops *ops;

    D_ENTRY1(ch_debug,"ch_size_pattern(0x%x)\n",ch);
    if (!ch) 
	ch= ch_error;
    
    if (_ch_hasops(ch)) {
	 ops= _ch_ops(ch);
	 RETURN(_ch_op_length(ops,ch));
    }
    else RETURN(ch->ch_patlength);
}

/***===================================================================***/

char 	*
ch_pattern(ch)
register1 CHAR *ch;
{
register2 CHAR_ops *ops;

    D_ENTRY1(ch_debug,"ch_pattern(0x%x)\n",ch);
    if (!ch) 
	ch= ch_error;
	
    if (_ch_hasops(ch)) {
	ops= _ch_ops(ch);
	RETURN(_ch_op_ptrn(ops,ch));
    }
    else RETURN(ch->ch_ptrn);
}


/***====================================================***/
/***			READ/WRITE ROUTINES		***/
/***====================================================***/


#define _ch_rdpmpdef(ch,func,eof) {\
	eof= eof||((	ch->ch_ht=	func)==EOF);\
	eof= eof||((	ch->ch_wid=	func)==EOF);\
	eof= eof||((	ch->ch_LT=	func)==EOF);\
	eof= eof||((	ch->ch_RB=	func)==EOF);\
	eof= eof||((	ch->ch_off=	func)==EOF); }

#define _ch_wrpmpdef(fil,ch,func) {\
	func(	ch->ch_ht,	fil);\
	func(	ch->ch_wid,	fil);\
	func(	ch->ch_LT,	fil);\
	func(	ch->ch_RB,	fil);\
	func(	ch->ch_off,	fil); }

#define _ch_getraster(fil,ch,func_name) { \
	ch->ch_code|= CH_FREE_PTRN;\
	ch->ch_ptrn= (char *)u_malloc(ch->ch_patlength);\
	if (fread(ch->ch_ptrn,1,ch->ch_patlength,fil)!=ch->ch_patlength) {\
	    RETURN(NULL);\
	} }

/***===================================================================***/

CHAR *
ch_acisread(fil,encoding,size,ops)
register2 FILE		*fil;
register4 int		 encoding;
register6 int		*size;
register5 CHAR_ops	*ops;
{
register1 CHAR  *ch;
	  char_info t_ich;
register3 char_info *ich= &t_ich;	/* efficiency wart */

    D_ENTRY4(ch_debug,"ch_acisread(0x%x,0x%x,0x%x,0x%x)\n",fil,encoding,size,
									ops);
    if ((!fil)||(feof(fil))) 
	RETURN(NULL);

    if (fread(ich,sizeof(char_info),1,fil)!=1) {
	RETURN(NULL);
    }
    ch= (CHAR *)u_malloc(sizeof(CHAR));
    ch->ch_ht=	 ich->y;
    ch->ch_wid=	 ich->x;
    ch->ch_LT=	 ich->a_space;
    ch->ch_RB=	 ich->c_space;
    ch->ch_off=	 ich->offset;
    ch->ch_code= encoding;
    ch->ch_ops=  ops;
    ch->ch_patlength=	ich->y*((ich->x+7)/8);
    if ((!ops)||(!_ch_op_read(ops,fil,ch,ch->ch_patlength))) {
	_ch_getraster(fil,ch,ch_acisread);
    }
    if (size) 
	*size= sizeof(char_info)+ch_sizepattern(ch);
    RETURN(ch);
}

/***===================================================================***/

CHAR *
ch_pmpread(fil,encoding,size,index,ops)
register2 FILE		*fil;
register4 int		 encoding;
register7 int		*size;
register8 int		*index;
register6 CHAR_ops	*ops;
{
register1 CHAR  *ch;
register3 int	 tmp;
register5 int	 eof=FALSE;
	  int	 lrg_pattern= FALSE;

    D_ENTRY5(ch_debug,"ch_pmpread(0x%x,0x%x,0x%x,0x%x,0x%x\n",fil,encoding,
							     size,index,ops);
    if ((!fil)||(feof(fil))) 
	RETURN(NULL);

    switch (tmp= getc(fil)) {
	case PMP_LOAD_FONT_PATTERN:
		break;
	case PMP_LOAD_LEFT_PATTERN:
	case PMP_LOAD_RIGHT_PATTERN:
	case PMP_LOAD_INVERTED_PATTERN:
		WSGO("Rotated font patterns not supported\n");
		action("characters will be printed sideways (sorry).\n");
		break;
	case PMP_LOAD_LARGE_PATTERN:
		encoding|= CH_LARGE_PTRN;
		lrg_pattern= TRUE;
		if ((tmp=getc(fil))!=0) {	/* magic byte in PMP manual */
		    WSGO1("Expected magic byte 0x00, found 0x%.2x\n",tmp);
		    action("completing read\n");
		}
		break;
	case EOF:
		RETURN(NULL);
	default:
		RETURN(NULL);
    }
    _ch_alloc(ch);
    ch->ch_ops= ops;
    ch->ch_code= encoding;

    if (index) 	*index= getc(fil);	/* report index */
    else 		getc(fil);	/* throw away index */

    if (!lrg_pattern)	{
	_ch_rdpmpdef(ch,getc(fil),eof);
    }
    else {
	_ch_rdpmpdef(ch,u_get16(fil),eof);
    }

    if (lrg_pattern)	ch->ch_patlength= u_get24(fil);
    else		ch->ch_patlength= (ch->ch_ht*((ch->ch_wid+7)/8));

    if ((!ops)||(!_ch_op_read(ops,fil,ch,ch->ch_patlength))) {
	_ch_getraster(fil,ch,ch_acisread);
    }

    if (size) {
	if (lrg_pattern)	*size= CH_LARGE_CHAR_SIZE+ch->ch_patlength;
	else			*size= CH_PMP_CHAR_SIZE+ch->ch_patlength;
    }
    RETURN(ch);
}

/***===================================================================***/

ch_aciswrite(fil,ch)
register3 FILE	*fil;
register1 CHAR	*ch;
{
register4 unsigned   knt;
          char_info  t_ich;
register2 char_info *ich= &t_ich; /* speed wart */
register5 char *ptrn;

   D_ENTRY2(ch_debug,"ch_aciswrite(%x,%x)\n",fil,ch);
   if (fil==NULL)
      RETURN(FALSE);
   ich->x=       ch->ch_wid;
   ich->y=       ch->ch_ht;
   ich->a_space= ch->ch_LT;
   ich->c_space= ch->ch_RB;
   ich->offset=  ch->ch_off;
   
   fwrite(ich,sizeof(char_info),1,fil);
   if (ch->ch_patlength!=0) {
       ptrn= ch_pattern(ch);
       if (ptrn)
         fwrite(ptrn,1,ch->ch_patlength,fil);
      else 
         for (knt=0;knt<ch->ch_patlength;knt++)
	    putc(NULL,fil);
   }
   RETURN(sizeof(char_info)+ch->ch_patlength);
}

/***===================================================================***/

ch_pmpwrite(fil,ch,index)
register1 FILE	*fil;
register2 CHAR	*ch;
register5 int	 index;
{
register3 unsigned	 knt;
register4 char		*ptrn;
register6 int		 size=0;

   D_ENTRY2(ch_debug,"ch_pmpwrite(%x,%x)\n",fil,ch);
   if (!fil)
      RETURN(FALSE);

   putc(PMP_LOAD_FONT_PATTERN,fil);
   putc(index,fil);

   if (ch->ch_code&CH_LARGE_PTRN) {
        putc(0x00,fil); /* magic load pattern byte */
        _ch_wrpmpdef(fil,ch,u_put16);
	u_put24(ch->ch_patlength,fil);
	size= CH_LARGE_CHAR_SIZE;
   }
   else {
       _ch_wrpmpdef(fil,ch,putc);
       size= CH_PMP_CHAR_SIZE;
   }

   if (ch->ch_patlength!=0) {
       ptrn= ch_pattern(ch);
       if (ptrn) {
         fwrite(ptrn,1,ch->ch_patlength,fil);
       }
       else {
	   for (knt=0;knt<ch->ch_patlength;knt++) {
	       putc(NULL,fil);
	   }
       }
   }
   RETURN(ch->ch_patlength+size);
}

/***============================================================***/

#define BitN(w,n)    ((w>>n)&01)

ch_textwrite(fil,fch,index)
register1 FILE	*fil;
register8 CHAR	*fch;
	  int	 index;
{
register2 int	 bit;
register3 int	 bits;
register4 int	 ch;
register5 int	 lowbit;
register6 char	*ptrn;
register7 int	 row;

   D_ENTRY3(ch_debug,"ch_textwrite(0x%x,0x%x,%d)\n",fil,fch,index);
   if (fil==NULL)
      RETURN(FALSE);
   fprintf(fil,"character %d\n",index);
   fprintf(fil,"height=  %d,	width=   %d\n",fch->ch_wid,fch->ch_ht);
   fprintf(fil,"LTspace= %d,	RBspace= %d\n",fch->ch_LT,fch->ch_RB);
   fprintf(fil,"offset=  %d\n",fch->ch_off);
   fprintf(fil,"length=  %d\n",ch_sizepattern(fch));
   ptrn= ch_pattern(fch);
   if (ptrn) {

	for (row=0;row<fch->ch_ht;row++) {
	    fprintf(fil,"%d)",row);
	    if (row<10)        fprintf(fil,"   ");
	    else if (row<100)  fprintf(fil,"  ");
	    else if (row<1000) fprintf(fil," ");
	    for (bits=fch->ch_wid;bits>0;bits-=8) {
		ch= *ptrn++;
		if (bits<8) lowbit= 8-bits;
		else        lowbit=  0;
		for (bit=7;bit>=lowbit;bit--) {
		    if (BitN(ch,bit))   putc('*',fil);
		    else                putc('.',fil);
		}
	    }
	    putc('\n',fil);
	}
   }
   RETURN(fch->ch_patlength);
}

