#include <stdio.h>
#include <sys/time.h>	 
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "memtype.h"
#include "mem.h"
#include "6502.h"
#include "atari.h"
#include "keyboard.h"

/* Atari interface for terminals
 *   by Frank Barrus
 */

#define WID 80
#define HGT 24

static int scr[WID*(HGT+1)];
static int uscr[WID*HGT];
static FILE *tty = NULL;
static char tty_name[80];
static int framecount = 15;
static int scr_to_ascii[256];
static int cscr_to_ascii[256];

#define SCR_INVERSE	0x200
#define SCR_BRIGHT	0x100
#define SCR_GRAPH	0x080 

char *interface_name = "tty interface v0.01 by Frank Barrus";


static int device_pos(char type, int unit)
{
int y;
	switch(type) {
	case 'D':	y = unit; break;
	case 'P':	y = unit+8; break;
	case 'R':	y = unit+10; break;
	default:	y = 13; 
	}
	return (y+1)*WID+41;
}


void interface_device_cmd(char type, int unit, char *cmd)
{
int x, i;
	x = device_pos(type, unit)+5;
	for(i=0; i<10; i++)
		if(*cmd)
			scr[x++] = *cmd++;
		else
			scr[x++] = 0x20;
}


void interface_device_param(char type, int unit,
		char *cmd, char *param)
{
char str[40];
char *sp;
int x;
	sprintf(str, "%c%d: [%10.10s] %-20.20s", type, unit+1, cmd, param);
	x = device_pos(type, unit);
	sp = str;
	while(*sp)
		scr[x++] = *sp++;
}


static void showmap(int y, char *name, uint adr, int type)
{
char *sp;
char str[34];
int x, i;
	switch(type) {	
	case MTYPE_NULL:
		sprintf(str, "%s: unused region", name);
		break;	
	case MTYPE_PBI:
		sprintf(str, "%s: Parallel Bus", name);
		break;
	case MTYPE_RAM:
		sprintf(str, "%s: RAM base: %04x", name, adr);
		break;
	case MTYPE_ROM:
		if((adr&0x3fff) == 0x1000)
			sprintf(str, "%s: Self-Test ROM", name);
		else
			sprintf(str, "%s: %-20.20s", name, sys.romfile);
		break;
	case MTYPE_CARTA:
		sprintf(str, "%s: %-20.20s", name, sys.cartAfile);
		break;	
	case MTYPE_CARTB:
		sprintf(str, "%s: %-20.20s", name, sys.cartBfile);
		break;	
	case MTYPE_GTIA: case MTYPE_POKEY: case MTYPE_PIA: case MTYPE_ANTIC:
		sprintf(str, "%s: GTIA/POKEY/PIA/ANTIC", name);
		break;
	default:
		sprintf(str, "%s: type: %02x", name, type);
	}
	x = (y+14)*WID+42;
	sp = str;
	for(i=0; i<31; i++)
		if(*sp)
			scr[x++] = *sp++;
		else
			scr[x++] = 0x20;
}



void interface_map_update(uint base, int len, uint adr, int type)
{
	switch(base) {
	case 0x0000:	showmap(0, "0000-3FFF", adr, type); adr += 0x4000;
			if((len -= 0x4000) <= 0) break; 
	case 0x4000:	showmap(1, "4000-4FFF", adr, type); adr += 0x1000;
			if((len -= 0x1000) <= 0) break; 
	case 0x5000:	showmap(2, "5000-57FF", adr, type); adr += 0x0800;
			if((len -= 0x0800) <= 0) break; 
	case 0x5800:	showmap(3, "5800-7FFF", adr, type); adr += 0x2800;
			if((len -= 0x2800) <= 0) break; 
	case 0x8000:	showmap(4, "8000-9FFF", adr, type); adr += 0x2000;
			if((len -= 0x2000) <= 0) break; 
	case 0xa000:	showmap(5, "A000-BFFF", adr, type); adr += 0x2000;
			if((len -= 0x2000) <= 0) break; 
	case 0xc000:	showmap(6, "C000-CFFF", adr, type); adr += 0x1000;
			if((len -= 0x1000) <= 0) break; 
	case 0xd000:	len -= 0x100; adr += 0x100;
	case 0xd100:	len -= 0x100; adr += 0x100;
	case 0xd200:	len -= 0x100; adr += 0x100;
	case 0xd300:	len -= 0x100; adr += 0x100;
	case 0xd400:	showmap(7, "D000-D7FF", adr, type); adr += 0x400;
			if((len -= 0x0400) <= 0) break; 
	case 0xd800:	showmap(8, "D800-FFFF", adr, type); adr += 0x2800;
			if((len -= 0x2800) <= 0) break; 
	default:
			printf("invalid map: %04x-%04x: %02x:%04x\n",
				base, base+len-1, type, adr);
			return;
	}
}


void interface_screen_update(void)
{
uint dlist;
uint code;
int nscan;
int x,y,yp,tx,ty,c,i;
int ns[16] = {0,0,8,8,8,16,8,16,8,4,4,2,1,2,1,1};
int wid[16] = {0,0,4,4,4,4,2,2,1,1,2,2,2,4,4,4};
byte ch;
static uint adr = 0;
int inv, bright, graph;
char scrbuf[420*24];
char *cp;
static int frame = 0;
	if(!sys.dmactl_dma)
		return; 
	if(frame++ < framecount)
		return;
	frame = 0;
	dlist = sys.dlist;
	for(nscan=0; nscan < HGT*8; nscan += ns[code&0xf]) {
		MEM_READ(dlist, code); dlist++;
		y = nscan/8;
		yp = y*WID;
		if(code==0x41)
			break;
		if(code&0x40) {
			MEM_WREAD(dlist, adr);
			dlist += 2;
		}
		switch(code&0x0f) {
		case 0x02: case 0x03: case 0x04: 
			for(x=0; x<40; x++) {
				MEM_READ(adr+x,ch);
				scr[yp+x] = scr_to_ascii[ch];
			}	
			break;
		case 0x05:
			for(x=0; x<40; x++) {
				MEM_READ(adr+x,ch);
				scr[yp+x] = scr_to_ascii[ch];
				scr[yp+WID+x] = scr_to_ascii[ch&0x80];
			}
			break;
		case 0x06:
			for(x=0; x<20; x++) {
				MEM_READ(adr+x,ch);
				scr[yp+x*2] = cscr_to_ascii[ch];
				scr[yp+x*2+1] = cscr_to_ascii[ch&0xc0];
			}
			break;
		case 0x07:
			for(x=0; x<20; x++) {
				MEM_READ(adr+x,ch);
				scr[yp+x*2] = cscr_to_ascii[ch];
				scr[yp+x*2+1] = cscr_to_ascii[ch&0xc0];
				scr[yp+WID+x*2] = cscr_to_ascii[ch&0xc0];
				scr[yp+WID+x*2+1] = cscr_to_ascii[ch&0xc0];
			}
			break;
		default:
			break;
		}
		adr += (wid[code&0xf]*10);
	}
	cp = scrbuf;
	inv = bright = graph = False;
	tx = 0; ty = -2;
	for(y=0, yp=0; y<HGT; y++, yp+=WID) for(x=0; x<WID; x++) {
		if((c = scr[yp+x]) != uscr[yp+x]) {
			uscr[yp+x] = c;
			if(y == ty) {
				sprintf(cp, "\033[%02d;%02dH", y+1, x+1);
				cp += 8;
			} else {
				if(y == ty+1 && x == 0) {
					*cp++ = 'r';
					*cp++ = 'n';
				} else {
					sprintf(cp,"\033[%02d;%02dH",y+1,x+1);
					cp += 8;
				}
			}
			if(c == 0x20) {
				for(i=x; i<WID; i++) 
					if(scr[yp+i] != 0x20)
						break;
				if(i==WID) {
					strcpy(cp, "\033[K"); cp += 3;
					for(i=x; i<WID; i++)
						uscr[yp+i] = 0x20;
					x = WID;
					continue;
				}
			} 
			if( (!(c&SCR_INVERSE) && inv)
			 || (!(c&SCR_BRIGHT) && bright)) {
				strcpy(cp,"\033[0m"); cp += 4;
				bright = False; inv = False;
			}
			if((c&SCR_INVERSE) && !inv) {
				strcpy(cp,"\033[7m"); cp += 4; inv = True;
			}
			if((c&SCR_BRIGHT) && !bright) {
				strcpy(cp,"\033[1m"); cp += 4; bright = True;
			}
			if((c&SCR_GRAPH) && !graph) {
				*cp++ = 'N'-0x40; graph = True;
			}
			if(!(c&SCR_GRAPH) && graph) {
				*cp++ = 'O'-0x40; graph = False;
			}
			*cp++ = c & 0x7f;
			tx = x;  ty = y;
		} 
	}
	if(inv || bright) {
		strcpy(cp, "\033[0m"); cp += 4;
	}
	if(graph)
		*cp++ = 'O'-0x40;
	if(cp != scrbuf) {
		strcpy(cp, "\033[1;80H"); cp += 7;
		write(fileno(tty), scrbuf, cp-scrbuf);
	}
}


void interface_sound_update(void)
{
}

void interface_click(int c)
{
}


void interface_control_update(void)
{
char buf[20];
int i,n;
fd_set r;
struct timeval tm;
	FD_ZERO(&r);
	FD_SET(fileno(tty), &r);
	tm.tv_sec = 0;
	tm.tv_usec = 0;
	if(select(fileno(tty)+1,&r,0,0,&tm) == 1) {
		if((n = read(fileno(tty),buf,1)) > 0) 
			for(i=0; i<n; i++)
				keyboard_press_ascii(buf[i]);
	}
}

#define SPACE		0x20

#define DIAMOND		SCR_GRAPH|'@'
#define ESCAPE		SCR_GRAPH|'a'
#define DOT		SCR_GRAPH|'f'
#define LRCORNER	SCR_GRAPH|'j'
#define URCORNER	SCR_GRAPH|'k'
#define ULCORNER	SCR_GRAPH|'l'
#define LLCORNER	SCR_GRAPH|'m'
#define CENTER		SCR_GRAPH|'n'
#define TOP		SCR_GRAPH|'o'
#define HLINE		SCR_GRAPH|'q'
#define BOTTOM		SCR_GRAPH|'s'
#define LTEE		SCR_GRAPH|'t'
#define RTEE		SCR_GRAPH|'u'
#define BTEE		SCR_GRAPH|'v'
#define TTEE		SCR_GRAPH|'w'
#define VLINE		SCR_GRAPH|'x'

#define HEART		'H'
#define CLUB		'C'
#define	SPADE		'S'
#define LEFT		VLINE
#define RIGHT		VLINE
#define LLEFT		URCORNER
#define LRIGHT		ULCORNER
#define ULEFT		LRCORNER
#define URIGHT		LLCORNER
#define LHALF		' '
#define BHALF		' '
#define LRIGHTF		'/'
#define LLEFTF		'\\'


int gtrans[] = {
	81, ULCORNER, 87, TTEE, 69, URCORNER, 
	65, LTEE, 83, CENTER, 68, RTEE,
	90, LLCORNER, 88, BTEE, 67, LRCORNER,
	82, HLINE, 124, VLINE, 
	70, '/', 71, '\\',
	84, DOT, 89, LHALF, 85, BHALF, 73, LRIGHT,
	79, LLEFT, 80, CLUB, 72, LRIGHTF, 74, LLEFTF,
	75, URIGHT, 76, ULEFT, 123, SPADE,
	86, LEFT, 66, RIGHT, 78, BOTTOM, 77, TOP,
	64, HEART, 96, DIAMOND,
	91, ESCAPE
};


int edge_to_binary(int ch)
{
	switch(ch) {
	case LLCORNER:	return 3;
	case VLINE:	return 5;
	case ULCORNER:	return 6;
	case LTEE:	return 7;
	case LRCORNER:	return 9;
	case HLINE:	return 10;
	case BTEE:	return 11;
	case URCORNER:	return 12;
	case RTEE:	return 13;
	case TTEE:	return 14;
	case CENTER:	return 15;
	default:	return 0;
	}
}

int binary_to_edge[16] = 
{SPACE, SPACE, SPACE, LLCORNER, SPACE, VLINE, ULCORNER, LTEE,
 SPACE, LRCORNER, HLINE, BTEE, URCORNER, RTEE, TTEE, CENTER};



void draw_edge(int x, int y, int ch)
{
	scr[y*WID+x] = binary_to_edge[
		edge_to_binary(scr[y*WID+x])|edge_to_binary(ch)];
}


void draw_box(int x1, int y1, int x2, int y2)
{
int x,y;
	for(x=x1+1; x<x2; x++) {
		draw_edge(x, y1, HLINE);
		draw_edge(x, y2, HLINE);
	}
	for(y=y1+1; y<y2; y++) {
		draw_edge(x1, y, VLINE);
		draw_edge(x2, y, VLINE);
	}
	draw_edge(x1, y1, ULCORNER);
	draw_edge(x2, y1, URCORNER);
	draw_edge(x1, y2, LLCORNER);
	draw_edge(x2, y2, LRCORNER);
}


void init_screen(void)
{
int i;
	for(i=0; i<WID*HGT; i++) 
		scr[i] = 0x20;
	draw_box(40, 13, 79, 23);
	draw_box(40, 0, 79, 23); 
}


int interface_init(int argc, char **argv)
{
static int init = True;
int narg = 0;
char **argp = argv;
int i,c,n;
	if(init) {
		for(i=0; i<256; i++) {
			c = i&0x7f;
			if(c < 0x40)
				c += 0x20;
			else if(c < 0x60)
				c = 0x20;
			for(n=0; n<sizeof(gtrans)/sizeof(int); n += 2) {
				if((i&0x7f) == gtrans[n]) {
					c = gtrans[n+1];
					break;	
				}
			}
			if(i&0x80)
				c |= SCR_INVERSE;
			scr_to_ascii[i] = c;
			c = i&0x3f;
			if(c < 0x40)
				c += 0x20;
			else if(c < 0x60)
				c = 0x20;
			if(i&0x80)
				c |= SCR_INVERSE;	
			if(i&0x40)
				c |= SCR_BRIGHT;
			cscr_to_ascii[i] = c;
		}
		init_screen();
	}
	while(argc--) {
		if(init && !strcmp(*argv,"-tty")) {
			argv++; argc--;
			if(**argv == '/')
				strcpy(tty_name, *argv);
			else
				sprintf(tty_name, "/dev/tty%s", *argv);
			if(!(tty = fopen(tty_name, "r+"))) {
				perror(tty_name);
				exit(1);
			}
		} else if(!strcmp(*argv,"-frame")) {
			framecount = atoi(*(++argv));
		} else {
			*argp++ = *argv; narg++;
		}
		if(!strcmp(*argv++,"-h")) {
			printf("Text interface options:\n");
			if(init)
				printf("\t-tty <device>\t\ttty name for I/O\n");
			printf("\t-frame <n>\t\trefresh every <n>th frame\n");
			return narg;
		}
	}
	init = False;
	return narg;
}


void interface_open(void)
{
char cmd[80];
int i;
	if(tty) {
		sprintf(cmd, "stty -echo raw -icanon < %s", tty_name);
		system(cmd);
		fprintf(tty, "\033[2J\033=\033)0");
		fflush(tty);
		for(i=0; i<WID*HGT; i++) 
			uscr[i] = 0x20;
	}
}


void interface_close(void)
{
char cmd[80];
	if(tty) {
		fprintf(tty,"\033[H\033>\033)B");
		fflush(tty);
		sprintf(cmd, "stty -raw icanon echo < %s", tty_name);
		system(cmd);
	}
}


void interface_end(void)
{
}

