/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
/*
 * This module is the phi-text interface for the compiler.  Files are
 * orignilly read in as shorts to accomdate phi-text.
 */
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <ctype.h>
#include "utype.h"
#include "interp.h"

extern BYTE startchars[];
extern BYTE symchars[];
extern BYTE whitespacechars[];
extern BYTE commentchars[];

PHITEXT phiparms = { 4,0,0x0f,0,0,0,0x7f };
BOOL phiused = FALSE;
BOOL putback = FALSE;

int currentphifg = 0;

static PHITEXT phidefaults = { 4,0,0x0f,0,0,0,0x7f };
static int pbchar = 0;

int repeat = 0;

void PhiInit(void)
{
	phiparms = phidefaults;
	putback = FALSE;
	phiused = FALSE;
	currentphifg = phidefaults.fgc;
}
void BadString(void)
{
	Error("Bad escape sequence in string");
}
/* Get a char from file */
static BOOL phichar(BYTE *ch, FILE *file, int first)
{
	while (TRUE) {
		*ch = fgetc(file);
		if (feof(file))
			return(FALSE);
      if (first && *ch == 0x1f) {
			phiused = TRUE;
			continue;
		}
		break;
	}
	return(TRUE);		
}
/*
 * Get a line from the file.  At this point the line is still streamed
 * phi-text
 */
BOOL philine(BYTE *buf, int len, FILE *file,int first)
{
   if (len < 2 || !phichar(buf,file,first)) {
		*buf = 0;
		return(FALSE);
	}
	while (len>2) {
		if (*buf == 0x0a || (phiused && (*buf == 0x14 || *buf == 0x15 || *buf == 0x16)))
			break;
      if (!phichar(++buf,file,FALSE))
			break;
		len--;
	}
	*buf++ =0x0a;
	*buf++ = 0;
	return(TRUE);
}
/*
 * Parse the phi-text stream and get the next character.  Attributes are
 * stripped at this stage so all we get is the bank and char info
 */
short parsechar(BYTE **buf)
{
	short rv;
	if (putback) {
		(*buf)++;
		putback = FALSE;
		return(pbchar);
	}
	if (!phiused)
		return(*(*buf)++);
	while (TRUE) {
		if (repeat) {
			repeat--;
			rv = phiparms.cwb + (phiparms.bank << 7);
			break;
		}
		rv = *(*buf)++;
		if (rv & 0x80) {
			char grp = rv & 0x70;
			switch (grp) {
				case 0: {
							int repeatlevel = 0;
							(*buf)--;
							while (((rv = **buf)& 0xf0) == 0x80) {
								(*buf)++;
								repeat += ((rv & 0x0f)<<(repeatlevel++ * 4)) +1;
							}
							repeat++;
						}
						break;
				case 0x10:
						phiparms.bank = rv & 0x0f;
						break;
				case 0x20:
				case 0x30:
						phiparms.attrib = rv & 0x1f;
						break;
				case 0x40:
						phiparms.fgc = rv & 0x0f;
						break;
				case 0x50:
						phiparms.bgc = rv & 0x0f;
						break;
				case 0x60:
						phiparms.style = rv & 0x0f;
						break;
				case 0x70:
						phiparms.size = rv & 0x0f;
						break;
			}
		}
		else {
			if (rv)
				phiparms.cwb = rv;
			if (rv & 0x60) {
            if ((phiparms.attrib & HIDDEN) || phiparms.style)
               return ' ' ;
				rv = rv + (phiparms.bank << 7);
			}
			else
				if (rv == 0x0a)
					phiparms = phidefaults;
			break;
		}
	}
	return(rv);
}
/*
 * In case we have to step back one char
 */
void putphiback(short ch)
{
	putback = TRUE;
	pbchar = ch;
}
/* The next few routines check for specific symbol types, sort of analogous
 * to the ctype library
 */
BOOL isstartchar(short val)
{
	int bit = 1 << (7 - (val & 0x07));
	int byte = val >> 3;
	return (startchars[byte] & bit);
}
BOOL issymchar(short val)
{
	int bit = 1 << (7 - (val & 0x07));
	int byte = val >> 3;
	return (symchars[byte] & bit);
}
BOOL iswhitespacechar(short val)
{
	int bit = 1 << (7 - (val & 0x07));
	int byte = val >> 3;
	return (whitespacechars[byte] & bit);
}
BOOL iscommentchar(short val)
{
	int bit = 1 << (7 - (val & 0x07));
	int byte = val >> 3;
	return (commentchars[byte] & bit);
}
/* These reads in a char for a C style string lit */
static BYTE *getstringchar(BYTE *rv,BYTE *bufptr)
{
	putback = FALSE;
	if (phiused) {
		if (repeat) {
			repeat--;
			*rv = phiparms.cwb;
			return(bufptr);
		}
		if ((*bufptr > 0x7f) || phiparms.bank) {
			*rv = *bufptr;
			if (*rv & 0x80) {
				char grp = *rv & 0x70;
				switch (grp) {
					case 0:
							break;
					case 0x10:
							phiparms.bank = *rv & 0x0f;
							break;
					case 0x20:
					case 0x30:
							phiparms.attrib = *rv & 0x1f;
							break;
					case 0x40:
							phiparms.fgc = *rv & 0x0f;
							break;
					case 0x50:
							phiparms.bgc = *rv & 0x0f;
							break;
					case 0x60:
							phiparms.style = *rv & 0x0f;
							break;
					case 0x70:
							phiparms.size = *rv & 0x0f;
							break;
				}
			}
			return(++bufptr);
		}
	}

			if (*bufptr == '\\') {
				bufptr++;
				if (isdigit(*bufptr) && (*bufptr < '8')) {
					unsigned temp = 0;
					while (isdigit(*bufptr) && *bufptr < '8')
						temp = (temp << 3) + (*bufptr++ - '0');
					if (temp > 255)
						BadString();
						
					*rv = (BYTE)temp;
				}
				else 
					switch(*bufptr++) {
						case 'b':
							*rv = 8;
							break;
						case 'f':
							*rv = 12;
							break;
						case 'n':
							*rv = 10;
							break;
						case 'r':
							*rv = 13;
							break;
						case 't':
							*rv = 9;
							break;
						case '\'':
							*rv = '\'';
							break;
						case '"':
							*rv = '"';
							break;
						case '\\':
							*rv = '\\';
							break;
						case 'x': {
							unsigned temp = 0;
							while (isxdigit(*bufptr)) {
								temp = (temp << 4) + (*bufptr - '0');
							  if (*bufptr++ > '9')
									temp -= 7;
							}
							if (temp > 255)
								BadString();
							*rv = (BYTE)temp;
						}
							break;
						default:
							BadString();
					}
			}
			else
			  *rv = *bufptr++;
	return(bufptr);
}
/* This reads in a string lit */
short getphistring(BYTE *obuf, BYTE **ibuf, short endchar)
{
	BYTE rv;
	if (phiused) {
		if (phidefaults.size != phiparms.size)
			*obuf++ = 0xf0 | phiparms.size;
		if (phidefaults.style != phiparms.style)
			*obuf++ = 0xe0 | phiparms.style;
		if (phidefaults.bgc != phiparms.bgc)
			*obuf++ = 0xd0 | phiparms.bgc;
		if (phidefaults.fgc != phiparms.fgc)
			*obuf++ = 0xc0 | phiparms.fgc;
		if (phidefaults.attrib != phiparms.attrib)
			*obuf++ = 0xa0 | phiparms.attrib;
		if (repeat) {
			BadString();
			repeat = 0;
			return(endchar);
		}
	}
	*ibuf = getstringchar(&rv,*ibuf);
	while (rv && ((rv + (phiparms.bank << 7)) != endchar)) {
		if (phiused && (rv <0x20)) {
			if ((rv == 0x0a)) {
				while (((BYTE) *(obuf-1)) & 0x80)
					obuf--;
			}
		}
		*obuf++ = rv;
		if (phiused && (rv == 0x0a)) {
			if (phidefaults.size != phiparms.size)
				*obuf++ = 0xf0 | phiparms.size;
			if (phidefaults.style != phiparms.style)
				*obuf++ = 0xe0 | phiparms.style;
			if (phidefaults.bgc != phiparms.bgc)
				*obuf++ = 0xd0 | phiparms.bgc;
			if (phidefaults.fgc != phiparms.fgc)
				*obuf++ = 0xc0 | phiparms.fgc;
			if (phidefaults.attrib != phiparms.attrib)
				*obuf++ = 0xa0 | phiparms.attrib;
		}
		*ibuf = getstringchar(&rv,*ibuf);
	}
	*obuf = 0;
	phiparms.cwb = rv;
	return(rv + (phiparms.bank << 7));
}	
/* This is for reading in phi-stream literal strings
 */
long getphichar(BYTE **ibuf)
{
	BYTE rv;
	*ibuf = getstringchar(&rv,*ibuf);
	if (!phiused)
		return(rv);
	while (rv & 0x80)
		*ibuf = getstringchar(&rv,*ibuf);
	if (!rv)
		return(0);
	phiparms.cwb = rv;
	return(((long)phiparms.size << 28) + ((long)phiparms.style << 24) + ((long)phiparms.fgc << 20)
					+ ((long)phiparms.bgc << 16) + ((long)phiparms.attrib << 11) + ((long)phiparms.bank << 7)
					+ (rv & 0x7f));
}
/* The next one writes a color index to a streamed file */
void phifg(int color, FILE *file)
{
	currentphifg = color;
	if (phiused)
		fputc(color | 0xc0, file);
}
/* Now conver to upper case */
short phiupper(short val)
{
	if (val > 0x60 && val < 0x7b)
		return val &~0x20;
	return(val);
}
/* Convert the short stream back to streamed one char at a time */
int installphichar(short curchar, BYTE *buf, int i)
{
	short rv = 0;
	int bank = curchar >> 7;
	int cwb = curchar & 0x7f;
	buf = buf+i;
	if (!phiused) {
		*buf = curchar;
		return(1);
	}
	if (i) {
		buf--;
		if (((*buf) & 0xf0) == 0x90)
			if ((*buf & 0x0f) != bank) {
				*buf++ = 0x90 | bank;
			}
			else
				rv--;
		else {
			buf++;	
			if (bank) {
				*buf++ = 0x90 | bank;
				rv++;
			}
		}
	}
	else
		if (bank) {
			*buf++ = 0x90 | bank;
			rv++;
		}
	*buf++ = cwb;
	rv++;
	if (bank) {
		*buf++ = 0x90 | bank;
		rv++;
	}
	return(rv);
}
/* Convert a stream to flats */
short phistreamtoflat(BYTE *out, BYTE *in, int size, BOOL useparms)
{
	int count = 0,fcount;
	long rv;
	PHITEXT oparms = phiparms;
	PHITEXT lastphiparms = phiparms;
	int orepeat = repeat;

	if (!phiused) {
		count = strlen(in);
		strcpy(out,in);
		return(count);
	}
	repeat = 0;
	while (rv = parsechar(&in)) {
		if ((rv > 0x1f) && useparms)
			rv |= ((long)phiparms.attrib << 11) + ((long)phiparms.fgc << 16) + ((long)phiparms.bgc << 20)
						+ ((long)phiparms.style << 24) + ((long)phiparms.size << 28);
		if (rv == 0x0a || rv == 0x14 || rv == 0x15 || rv == 0x16)
			phiparms = lastphiparms;
		if (size > 2) {
			out[count++] = rv >> 24;
			out[count++] = (rv >>16) & 0xff;
		}
		if (size > 1)
			out[count++] = (rv >>8) & 0xff;
		out[count++] = (rv) & 0xff;
		lastphiparms = phiparms;
	}
	fcount = count;
	out[count++] = 0;
	if (size > 1)
		out[count++] = 0;
	if (size > 2) {
		out[count++] = 0;
		out[count++] = 0;
	}

	repeat = orepeat;
	phiparms = oparms;
	return(fcount);
}
/* Compare two phi-text streams */
int phicmp(BYTE *str1, BYTE *str2)
{
	BYTE buf1[400], buf2[400], *p1=buf1, *p2=buf2;
	int count1, count2;
	if (!phiused)
		return(strcmp(str1,str2));

	count1 = phistreamtoflat(buf1,str1,FALSE,FALSE);
	count2 = phistreamtoflat(buf2,str2,FALSE,FALSE);
	
	while(count1 && count2) {
		int val1 = ((*p1++) << 8) + *p1++;
		int val2 = ((*p2++) << 8) + *p2++;
		if (val1 > val2)
			return(1);
		if (val1 < val2)
			return(-1);
		count1--;
		count2--;
	}
	if (count1 == count2)
		return(0);
	if (count1)
		return(1);
	return(2);
}