//		  ÚÄÄÄÄÄÄÄ¿
//	ÄÄÄÄÄÄÄÄÄ>³ AVNER ³
//	ÄÄÄÄÄÄÄÄÄ>³  BEN  ³ÄÄÄÄÄÄ> Software Engineering Method
//		  ÀÄÄÄÄÄÄÄÙ
//	10 Dov-Hoz st. Tel-Aviv 63416 Israel tel. 972-3-221535

// The Screen NAVigator, ver 1.20 August 1990
// Copyright (c) 1990 by Avner Ben
// Snav is not, and never was, free software.
// for conditions for use refer to file "copyrigh.txt"

// The Screen Navigator is an object-oriented device-independent
// character-graphics driver package, written in the C++ language,
// distributed in the form of C++ source code.
// For further information refer to the documentation files.

// The user may not omit this text, from the beginning of the file, to
// the line of asterisks below.

/***************************************************************************/

// package nucleus part 2 - source code.
// This file defines the "computer-alphabet" object, and offers an
// extended-ASCII implemented, as on the IBM-PC.
// for data-structure logic, refer to file "article".

// History:
// 28.7.89 avner ben coded.
// 7.9.90  - avner ben - last update.
// for further details refer to documentation.

// Site history (of this copy):
// __.__.__ ____________ : __________________.

#include <stdio.h>
#include <string.h>

#define  SNAV1_C
#include "/mtad/snav/snav0.hpp"
#include "/mtad/snav/snav1.hpp"

/**************** indexed access to character attribute table *****************/

int ascii_pc_data[]=
{ // in-memory file to be loaded into the default alphabet when initialized.
//   all semigraphic characters in the ascii collating sequence, pc-extended.

//	num directions-connective   wgt
//	--- ----------------------- ---
	 16,LTDIR,		    3,0, // default right arrow
	 17,RTDIR,		    3,0, // default left arrow
	 30,DNDIR,		    0,3, // default up arrow
	 31,UPDIR,		    0,3, // default down arrow
	 43,RTDIR|UPDIR|LTDIR|DNDIR,0,0, // twg cross
	 45,RTDIR|LTDIR,	    0,0, // twg dash
	 60,RTDIR,		    0,0, // twg left arrow
	 62,LTDIR,		    0,0, // twg right arrow
	 86,UPDIR,		    0,0, // twg down arrow
	 94,DNDIR,		    0,0, // twg up arrow
	124,UPDIR|DNDIR,	    0,0, // twg bar
	179,UPDIR|DNDIR,	    0,1, // bar
	180,UPDIR|LTDIR|DNDIR,	    1,1, // left fork
	181,UPDIR|LTDIR|DNDIR,	    2,1, // left fork
	182,UPDIR|LTDIR|DNDIR,	    1,2, // left fork
	183,LTDIR|DNDIR,	    1,2, // right up corner
	184,LTDIR|DNDIR,	    2,1, // right up corner
	185,UPDIR|LTDIR|DNDIR,	    2,2, // left fork
	186,UPDIR|DNDIR,	    0,2, // bar
	187,LTDIR|DNDIR,	    2,2, // right up  corner
	188,UPDIR|LTDIR,	    2,2, // right down corner
	189,UPDIR|LTDIR,	    1,2, // right down corner
	190,UPDIR|LTDIR,	    2,1, // right down corner
	191,LTDIR|DNDIR,	    1,1, // right up corner
	192,RTDIR|UPDIR,	    1,1, // left down corner
	193,RTDIR|UPDIR|LTDIR,	    1,1, // up fork
	194,RTDIR|LTDIR|DNDIR,	    1,1, // down fork
	195,RTDIR|UPDIR|DNDIR,	    1,1, // right fork
	196,RTDIR|LTDIR,	    1,0, // dash
	197,RTDIR|UPDIR|LTDIR|DNDIR,1,1, // cross
	198,RTDIR|UPDIR|DNDIR,	    2,1, // right fork
	199,RTDIR|UPDIR|DNDIR,	    1,2, // right fork
	200,RTDIR|UPDIR,	    2,2, // left down corner
	201,RTDIR|DNDIR,	    2,2, // left up corner
	202,RTDIR|UPDIR|LTDIR,	    2,2, // up fork
	203,RTDIR|LTDIR|DNDIR,	    2,2, // down fork
	204,RTDIR|UPDIR|DNDIR,	    2,2, // right fork
	205,RTDIR|LTDIR,	    2,0, // dash
	206,RTDIR|UPDIR|LTDIR|DNDIR,2,2, // cross
	207,RTDIR|UPDIR|LTDIR,	    2,1, // up fork
	208,RTDIR|UPDIR|LTDIR,	    1,2, // up fork
	209,RTDIR|LTDIR|DNDIR,	    2,1, // down fork
	210,RTDIR|LTDIR|DNDIR,	    1,2, // down fork
	211,RTDIR|UPDIR,	    1,2, // left down corner
	212,RTDIR|UPDIR,	    2,1, // left down corner
	213,RTDIR|DNDIR,	    2,1, // left up corner
	214,RTDIR|DNDIR,	    1,2, // left up corner
	215,RTDIR|UPDIR|LTDIR|DNDIR,1,2, // cross
	216,RTDIR|UPDIR|LTDIR|DNDIR,2,1, // cross
	217,UPDIR|LTDIR,	    1,1, // right down corner
	218,RTDIR|DNDIR,	    1,1, // left up corner
	219,RTDIR|UPDIR|LTDIR|DNDIR,3,3, // full
	// stream terminator
	  0,NODIR,		    0,0
};

// default alphabet initialized by system
computer_alphabet *alph=
 new computer_alphabet("ASCII_PC",256,ascii_pc_data);

computer_alphabet :: computer_alphabet(char *alphname, int bufsize, int *data)
{
	name=new char[strlen(alphname)+1];
	strcpy(name,alphname);
	numchars=bufsize;
	grafc=new int[numchars*2];
	for (int *c=grafc+(numchars-1)*2; ; c--)
		{ *c=0; if (c==grafc) break; }
	for (c=&(index[15][15]); ; c--)
		{ *c=0; if (c==&index[0][0]) break; }
	if (!data) return;
	for (; *data; data+=4)
		poke_c((char)*data,intersection(*(data+1)),
		 weight_d(*(data+2),*(data+3)));
}

computer_alphabet :: computer_alphabet(computer_alphabet &other)
{
	name=new char[strlen(other.name)+1];
	strcpy(name,other.name);
	numchars=other.numchars;
	grafc=new int[numchars*2];
	for (int i=(numchars-1)*2; i>=0; i--)
		grafc[i]=other.grafc[i];
	for (int j=15; j>=0; j--)
		for (i=15; i>=0; i--)
			index[j][i]=other.index[j][i];
}

computer_alphabet :: ~computer_alphabet(void)
      { delete grafc; delete name; }

computer_alphabet& computer_alphabet :: operator=(computer_alphabet &other)
{
	name=new char[strlen(other.name)+1];
	strcpy(name,other.name);
	numchars=other.numchars;
	grafc=new int[numchars*2];
	for (int i=(numchars-1)*2; i>=0; i--)
		grafc[i]=other.grafc[i];
	for (int j=15; j>=0; j--)
		for (i=15; i>=0; i--)
			index[j][i]=other.index[j][i];
	return *this;
}

void computer_alphabet :: poke_c(char c, intersection avdir, weight_d wgt)
{ // poke attributes in character table and index

	int cnum=(unsigned char)c;
	if (cnum<1 || cnum>=numchars) return;	// caller's bug
	*(grafc+cnum*2)=avdir();
	*(grafc+cnum*2+1)=wgt();
	index[wgt()][avdir()]=(avdir() ? cnum : 0);// the latter is caller's bug
}

char computer_alphabet :: get_c(intersection &avdir, weight_d &inwgt)
{ // retrive character by attributes, or a close match

	weight_d wgt=inwgt;
	// fix caller's neglect - over-weight
	if (wgt.y() && !(avdir.intersects(hdirs)))
		wgt.replace(axis(HDIM),0);
	if (wgt.x() && !(avdir.intersects(vdirs)))
		wgt.replace(axis(VDIM),0);
	if (!(wgt()) && !(avdir()))
		return(' ');            // not semigraphic
	// fix caller's neglect - under-weight
	if (!(wgt.y()) && avdir.intersects(hdirs))
		wgt.replace(axis(HDIM),wgt.x());
	if ((!wgt.x()) && avdir.intersects(vdirs))
		wgt.replace(axis(VDIM),wgt.y());
	int c;
	// 1. exact match
	if ((c=index[wgt()][avdir()])!=0)
		return (char)c;
	// 2. default full width
	if ((c=index[(weight_d((wgt.y()? 3:0),(wgt.x()? 3:0)))()]
	 [avdir()])!=0)
		return (char)c;
	// 3. typewriter graphic - search for cross
	if (!wgt())
		if ((c=index[0][alldirs()])!=0)
			return (char)c;
	// 4. full weight - search for cross
	if (wgt.y()==3 || wgt.x()==3)
		if ((c=index[fullwgt()][alldirs()])!=0)
			return (char)c;
	// 5. close match (nearest intersection in same weight group)
	for (int i=avdir()-1; i; i--)
		if ((c=index[wgt()][i])!=0)
			return (char)c;
	// 6. not found
	return(' ');
}

int computer_alphabet :: cwidth(char inc, axis &dim)
{ return (weight_d(*(grafc+(unsigned char)inc*2+1))).width(dim); }

char computer_alphabet :: translate(char src, weight_d &wgt)
{ // translate character to target width, as close as possible

	char tgt;
	intersection avdir=cdir(src);
	if ((tgt=get_c(avdir,wgt))!=0)
		return tgt;
	return src;
}

void computer_alphabet :: inverse(axis &dim)
{ // invert the whole alphabet (e.g. latin=>semitic)

	for (int i=1; i<numchars-1; i++) {
		intersection avdir=cdir((char)i), newdir=(int)NODIR;
		if (!avdir()) continue;
		for (direction dir; dir(); dir++)
			if (avdir>=dir)
				if (axis(dir)==dim) newdir+=(!dir);
				else newdir+=dir;
		poke_c( (char)i,newdir(),cweight((char)i) );
	}
}

int computer_alphabet :: list(FILE *outf)
{ // print the index (originally intended for debugging)

	if (!outf) return 0;
	int typeface[16], num_faces=0, num_semi=0;
	for (int i=0; i<=15; i++) {
		typeface[i]=0;
		for (int j=0; j<=15; j++)
			if (index[i][j])
				{ typeface[i]=TRUE; num_semi++; }
		if (typeface[i]) num_faces++;
	}
	fprintf(outf,"The %s Alphabet - semigrpahic cross-reference\n",name);
	if (!num_faces)
		{ fprintf(outf,"(null)\n"); return 0; }
	fprintf (outf,"contains %d semigraphic characters in %d weight groups\n",
	 num_semi,num_faces);
	for (int w=0; w<=15; w++) {
		if (!typeface[w]) continue;
		fprintf(outf,"\n");
		fprintf(outf,"WEIGHT GROUP code %d (h:v weight distribution ",w);
		if ((weight_d(w)).y()>2) fprintf(outf,"full:");
		else fprintf(outf,"%d:",(weight_d(w)).y());
		if ((weight_d(w)).x()>2) fprintf(outf,"full)");
		else fprintf(outf,"%d)",(weight_d(w)).x());
		fprintf(outf,"\n");
		for (int d=1; d<=15; d++)
			if (index[w][d])
				fprintf(outf," %3d: %c function: %-8s (connects: %s)\n",index[w][d],
				 (char)index[w][d],(intersection(d)).name(),
				 (intersection(d)).names());
	}
	return i;
}

/*********************** character connectivity ******************************/

boolean computer_alphabet :: ccont(char cfrm, char cto, direction &curdir)
{ /* test if two semigraphics connect, given current direction */

	if (!(curdir())) return(FALSE);
	if (!(intersection(cdir(cfrm))>=curdir))
		return(FALSE); /* caller's bug */
	/* target char must point at opposite direction */
	if (intersection(cdir(cto))>=(!curdir)) {
		axis curdim(curdir);
		int widfrm=(cwidth(cfrm,curdim));
		int widto=(cwidth(cto,curdim));
		if (widfrm==widto		// same width
		|| widto==3 || widfrm==3)	// default terminal symbol
		return(TRUE);
	}
	return(FALSE);
}

char computer_alphabet :: carrw(char *env, direction ind, weight_d wgt,
 int len, int pos, boolean directed)
{ // draw one character within an arc, context sensitive

	if (pos>len) pos=len;		// caller's bug
	axis other_ax(ind); other_ax.opposite();
	wgt.replace(other_ax,alph->cwidth(*env,other_ax));
	if (pos==len)			// end of arc ?
		if (len>1 || directed) {// invert connectivity
			ind.opposite();
			if (directed)	// arrowhead requested
				return get_c(intersection(ind()),wgt);
		} // otherwise (not directed) - allow to "weld"
	intersection avdir=cdir(*env); avdir+=ind;
	if (pos!=1 && pos!=len || avdir.unary())
		avdir+=(!ind);			// opposite added by default
	char c=get_c(avdir,wgt);		// init result
	for (direction dir; dir(); dir++) {
		if (dir!=ind && dir!=(!ind))	// vertical
			if (*(env+dir.serial())!=' '
			&& ccont(c,*(env+dir.serial()),dir)) {
				if (!(avdir>=dir))
					{ avdir+=dir; c=get_c(avdir,wgt); }
			} else if (avdir>=dir)
				{ avdir-=dir; c=get_c(avdir,wgt); }
	}
	if (pos<len && avdir.unary()) { // arrowhead not allowed in middle
		avdir+=!(direction(avdir()));
		c=get_c(avdir,wgt);
	}
	return c;
}

char * computer_alphabet :: cearrw(char *env, direction &indir, weight_d &inwgt,
 int len, int pos, boolean directed)
{ // draw one character within an arc, possibly modifying neighbours

	if (pos>len) pos=len;		// caller's bug
	weight_d wgt=inwgt; direction ind=indir;
	axis other_ax(ind); other_ax.opposite();
	wgt.replace(other_ax,alph->cwidth(*env,other_ax));
	if (pos==len)			// end of arc ?
		if (len>1 || directed) {// invert connectivity
			ind.opposite();
			if (directed) { // arrowhead requested
				*env=get_c(intersection(ind()),wgt);
				return(env);
			}
		} // else (not directed) - allow to "weld"
	intersection avdir=cdir(*env); avdir+=ind;
	if (pos!=1 && pos!=len || avdir.unary())
		avdir+=(!ind);			// opposite added by default
	char *ngbr; intersection vertdirs=other_ax.directions();
	char c=get_c(avdir,wgt); // init result
	for (direction dir; dir(); dir++) {
		ngbr=env+dir.serial();
		if (vertdirs>=dir) {	// vertical
			if (*ngbr!=' ' && ccont(c,*ngbr,dir)) {
				if (!(avdir>=dir))
					{ avdir+=dir; c=get_c(avdir,wgt); }
			} else if (avdir>=dir)
				{ avdir-=dir; c=get_c(avdir,wgt); }
			// normalize weight
			if ((alph->cdir(*env)).unary()) {
				if (ccont(*env,*ngbr,dir)) {
					wgt.replace(axis(dir),(alph->cweight(*ngbr)).width(axis(dir)));
					c=get_c(avdir,wgt);
				}
			}
		}
		if (*ngbr!=' ' && !(avdir>=dir)) { // trim neighbour
			intersection nbdir=cdir(*ngbr);
			if (nbdir>=!dir && nbdir.type()>=J_FORK)
				*ngbr=ctrim(*ngbr,!dir);
		}
	}
	if (pos<len && avdir.unary()) { // arrowhead not allowed in middle
		avdir+=!(direction(avdir()));
		c=get_c(avdir,wgt);
	}
	*env=c;
	return env;
}

char computer_alphabet :: ctrim(char c, direction &dir)
{ // trim char in protruding direction, if present

	intersection avdir=cdir(c);
	if (!(avdir>=dir)) return(c); // not there
	avdir-=dir;
	return get_c(avdir,cweight(c));
}

char computer_alphabet :: celong(char c, direction &dir)
{ // elongate char in missing direction, if not already present

	intersection avdir=cdir(c);
	if (avdir>=dir) return(c); // already there
	avdir+=dir;
	return get_c(avdir,cweight(c));
}
