#include "microdot.h"

static struct Screen *tscr;
static struct Window *twin, *tw;
static void *tpool;
static struct List tlist;
ULONG tvsig;

static struct Image propimage;
static struct PropInfo propinfo={AUTOKNOB|FREEVERT|PROPNEWLOOK,0,0,0,MAXBODY/2};
static struct Gadget scrollgad={NULL, 625,12,15,-1,  GFLG_GADGHCOMP,GACT_FOLLOWMOUSE|GACT_IMMEDIATE|GACT_RELVERIFY,GTYP_PROPGADGET, &propimage,NULL,NULL,NULL,&propinfo,1,0};

extern struct Menu *protomenustrip;

extern struct TextFont *listfont;

/* One ViewNode */

struct tvn
{
	struct MinNode 	n;
	USHORT			structsize;
	char			string[2];
};

//static char tvnbuf[ 512 ];
static char *tvnbuf;
ALLOCSTATIC( tvnbuf, 512, MEMF_CLEAR )

/*CONSTRUCTOR_P(test,50)
{
	Printf( "tvnbuf = %lx\n", tvnbuf );
	return( 0 );
}


CONSTRUCTOR_P(test,101)
{
	Printf( "tvnbuf = %lx\n", tvnbuf );
	return( 0 );
}

CONSTRUCTOR_P(test,199)
{
	Printf( "tvnbuf = %lx\n", tvnbuf );
	return( 0 );
}
*/

static int  tvnoff, escmode, scrollertop, updatepending, changed;

static int  slines, listlines;

void tv_init( void )
{
	tpool = LibCreatePool( 0, 8192, 1024 );
	tvnoff = 0;
	escmode = 0;
	changed = 0;
	scrollertop = 0;
	updatepending = TRUE;

	NewList( &tlist );
}

void tv_exit( void )
{
	LibDeletePool( tpool );

	if( twin )
	{
		ClearMenuStrip( twin );
		CloseWindow( twin );
	}

	if( tscr )
		CloseScreen( tscr );

	tpool	= NULL;
	twin	= NULL;
	tscr	= NULL;
	tvsig   = 0;
}

/* Zeichen */
static void redraw( void )
{
	struct tvn *n;
	int c, maxx;
	char empty[256];

	if( !twin )
		return;

	n = (struct tvn *) GetEntry( &tlist, scrollertop );
	memset( empty, 32, 256 );
	maxx = ( tscr->Width - 16 ) / 8;

	sabd( &tscr->RastPort, 1, 0, JAM2 );

	for( c = 0; c < slines; c++ )
	{
		Move( twin->RPort, 0, tscr->BarHeight + 1 + listfont->tf_Baseline + ( c * listfont->tf_YSize ) );
		if( n && n->n.mln_Succ )
		{
			if( strlen( n->string ) >= maxx )
			{
				Text( twin->RPort, n->string, maxx );
			}
			else
			{
				Text( twin->RPort, n->string, strlen( n->string ) );
				Text( twin->RPort, empty, maxx - strlen( n->string ) );
			}
			n = (struct tvn *) n->n.mln_Succ;
		}
		else
		{
			Text( twin->RPort, empty, maxx );

		}
	}
}

/* Scroller auslesen & Redraw */
static void reposprop( void )
{
	ULONG newscrollertop, hidden;

	hidden = max( listlines - slines, 0 );

	newscrollertop = ( ( hidden * propinfo.VertPot ) + (MAXPOT / 2 ) ) >> 16;

	if( newscrollertop != scrollertop )
	{
		scrollertop = newscrollertop;
		redraw();
	}
}


/* Scroller berechnen */
static void recalcprop( void )
{
	UWORD body = MAXBODY, pot = 0;
	ULONG hidden;
	char buffer[32];

	if( !twin )
		return;

	listlines = GetNumEntries( &tlist );

	hidden = max( listlines - slines, 0 );

//	Printf( " listlines %ld slines %ld hidden %ld\n", listlines, slines, hidden );

	if( scrollertop < 0 )
		scrollertop = 0;
	else if( scrollertop > hidden )
		scrollertop = hidden;

	if( hidden )
	{
		body 	= ( (ULONG) slines * MAXBODY ) / listlines;
		pot 	= ( (ULONG) scrollertop * MAXPOT ) / hidden;
	}

	NewModifyProp( &scrollgad, twin, NULL, propinfo.Flags,
		propinfo.HorizPot, pot,
		propinfo.HorizBody, body,
		1 );

	sprintf( buffer, "%lc %lD Zeilen     ", ( changed ) ? '×' : ' ', listlines );

	sabd( &tscr->RastPort, 0, 1, JAM2 );
	Move( &tscr->RastPort, 396, tscr->RastPort.Font->tf_Baseline + 1 );
	Text( &tscr->RastPort, buffer, strlen( buffer ) );

}

void tv_update( void )
{
	if( updatepending )
	{
		recalcprop();
		redraw();

		updatepending = FALSE;
	}
}

/* Neue TVN-Node erzeugen und anhängen */

static void addtvn( void )
{
	int tvnsize = sizeof(struct tvn) + tvnoff;
	struct tvn *n;

	FOREVER
	{
		n = LibAllocPooled( tpool, tvnsize );

		if( n )
			break;

		/* Keine mehr frei, von oben freigeben */
		n = (struct tvn *) RemHead( &tlist );
		if( !n )
			break;

		LibFreePooled( tpool, n, n->structsize );
	}

	if( !n )
	{
		tvnoff = 0;
		return;
	}

	n->structsize = tvnsize;
	memcpy( n->string, tvnbuf, tvnoff );
	n->string[tvnoff] = 0;

	tvnoff = 0;

	AddTail( &tlist, (struct Node *) n );

	updatepending = TRUE;
	changed = TRUE;
}


static void addchar( UBYTE ch )
{

	/* ESC-Codes filtern */

	if( escmode == 2 && ch == '[' )
	{
		escmode = 1;
		return;
	}

	if( escmode == 1 )
	{
		if( isspace(ch) || isdigit(ch) || ch == ';' )
			return;
		escmode = 0;
		return;
	}

	if( ch == 0x9b )
	{
		escmode = 1;
		return;
	}

	if( ch == 0x1b )
	{
		escmode = 2;
		return;
	}

	if( ch == 13 )
		return;

	/* Zeichen adden */

	if( ch == 10 || tvnoff > 510 )
	{
		addtvn();
	}

	if( ( ch != 10 ) && ( ch > 31 ) && ( ch != 127 ) )
		tvnbuf[ tvnoff++ ] = ch;
}


void tv_add( char *buf, int size )
{
	while( size-- > 0 )
		addchar( *buf++ );
}

/* Open Screen & Window */
extern struct TextAttr screenfontta;

ULONG tv_open( struct Window *win )
{
	static USHORT pens[] = { 0xffff, 0xffff };

	tw = win;

	/* Bereits offen? */
	if( tscr )
	{
		ScreenToFront( tscr );
		ActivateWindow( twin );
		return( (ULONG) ( 1<<twin->UserPort->mp_SigBit ) );
	}

	if( isv37 )
	{
		tscr = OpenScreenTags( NULL,
			SA_DisplayID, 	GetVPModeID( &scr->ViewPort ),
			SA_Pens,		pens,
			SA_Width,		scr->Width,
			SA_Font,		&screenfontta,
			SA_Height,		scr->Height,
			SA_AutoScroll,	TRUE,
			SA_Depth,		1,
			SA_Title,		"MicroDot Protokoll-Bildschirm",
			TAG_DONE );

		if( !tscr )
		{
			displaybeep();
			return( 0 );
		}

		twin = OpenWindowTags( NULL,
			WA_Top, 0, WA_Left, 0,
			WA_Height, 2048,
			WA_Width, 2048,
			WA_AutoAdjust, TRUE,
			WA_Flags,		BACKDROP|BORDERLESS|NOCAREREFRESH|ACTIVATE,
			WA_IDCMP,		GADGETUP|GADGETDOWN|MOUSEMOVE|MENUPICK|RAWKEY|IDCMP_MENUHELP,
			WA_NewLookMenus,	TRUE,
			WA_CustomScreen,	tscr,
			WA_MenuHelp,		TRUE,
			TAG_DONE );

		SetFont( twin->RPort, listfont );

		if( !twin )
			return( 0 );

	}
	else /* 1.3... */
	{
		struct NewWindow nw;
		struct NewScreen ns;

		memset( &nw, 0, sizeof( nw ) );
		memset( &ns, 0, sizeof( ns ) );

		ns.Width	= scr->Width;
		ns.Height	= scr->Height;
		ns.Depth	= 1;
		ns.DetailPen= 0;
		ns.BlockPen = 1;
		ns.ViewModes	= HIRES;
		if( prefs.flags & MDF_INTERLACE)
			ns.ViewModes|=LACE;
		ns.Font			= &top80;
		ns.DefaultTitle	= "MicroDot Protokoll-Bildschirm";

		tscr = OpenScreen( &ns );
		if( !tscr )
		{
			displaybeep();
			return( 0 );
		}

		nw.Width	= tscr->Width;
		nw.Height	= tscr->Height;
		nw.DetailPen = nw.BlockPen = 0xff;
		nw.IDCMPFlags = GADGETUP|GADGETDOWN|MOUSEMOVE|MENUPICK|RAWKEY;
		nw.Flags	= BACKDROP|BORDERLESS|NOCAREREFRESH|ACTIVATE;
		nw.Screen	= tscr;
		nw.Type		= CUSTOMSCREEN;

		twin = OpenWindow( &nw );
		if( !twin )
			return( 0 );
	}

	if( prefs.tview_cols[0] == prefs.tview_cols[1] )
		prefs.tview_cols[1] = ~prefs.tview_cols[0];

	LoadRGB4( &tscr->ViewPort, prefs.tview_cols, 2 );

	slines = ( tscr->Height - ( tscr->BarHeight + 1 ) ) / listfont->tf_YSize;

	SetMenuStrip( twin, protomenustrip );

	scrollgad.Height 	= tscr->Height-12;
	scrollgad.LeftEdge	= tscr->Width-16;

	AddGList( twin, &scrollgad, -1, -1, NULL );
	RefreshGadgets( &scrollgad, twin, NULL );

	updatepending = TRUE;
	scrollertop = 0;
	tv_update();

	return( tvsig = ( (ULONG) ( 1<<twin->UserPort->mp_SigBit ) ) );
}

/* Als Protokollnachricht speichern... */
extern char lastboxdialed[];
int tv_save_proto( void )
{
	struct tvn *n;
	char buffer[ 128 ];

	listlines = GetNumEntries( &tlist );

	if( !listlines )
	{
		DisplayBeep( NULL );
		return( 0 );
	}

	sprintf( buffer, "Terminal-Mitschnitt von '%s'", lastboxdialed );

	openlogmsg( buffer );

	n = (struct tvn *) tlist.lh_Head;

	while( n->n.mln_Succ )
	{
		printlog( "%s\n", n->string );
		n = (struct tvn *) n->n.mln_Succ;
	}

	closelogmsg();
	changed = 0;
	return( -1 );
}

int tv_save( void )
{
	struct tvn *n;
	char file[108],path[256];
	FILE *f;
	int rc;

	listlines = GetNumEntries( &tlist );

	if( !listlines )
	{
		DisplayBeep( NULL );
		return( 0 );
	}

	if( !rtFileRequest( fr[FR_PROTOCOL], file, "Mitschnitt-Puffer abspeichern...",
		twin ? RT_Window : TAG_IGNORE,	twin,
		RT_ReqPos,	REQPOS_CENTERSCR,
		RT_LockWindow,	TRUE,
		RTFI_Flags,	FREQF_SAVE,
		TAG_DONE ) ) return( 0 );

	strcpy( path, fr[FR_PROTOCOL]->Dir );
	addpart( path, file );

	rc = fileexistsreq( path );

	if( !rc )
		return( 0 );
	else if( rc == 1 )
		f = fopen( path, "w" );
	else if( rc == 2 )
		f = fopen( path, "a" );

	if( !f )
	{
		nofilereq( path, _OSERR );
		return( 0 );
	}

	fsetbuf( f, 2048 );
	/* Nun in Datei schreiben */

	n = (struct tvn *) tlist.lh_Head;

	while( n->n.mln_Succ )
	{
		fprintf( f, "%s\n", n->string );
		n = (struct tvn *) n->n.mln_Succ;
	}

	fclose( f );
	changed = 0;

	return( -1 );
}

void tv_clear( void )
{
	int rc;

	if( listlines )
	{

		if( changed )
		{
			rc = askreq( "Wollen Sie wirklich den Puffer\nmit insgesamt %lD Zeilen Mitschnitt löschen?", "Löschen|Speichern...|Abbruch", listlines );

			if( !rc || ( rc == 2 && !tv_save() ) )
				return;
		}

		LibDeletePool( tpool );
		tv_init();
		tv_update();
	}
}

static void tv_docolprefs(void)
{
	if( rtPaletteRequest( "Bitte Farben einstellen", rinfo,
		RT_Screen, tscr, TAG_DONE ) <0 )
		return;

	prefs.tview_cols[0] = GetRGB4( tscr->ViewPort.ColorMap, 0 );
	prefs.tview_cols[1] = GetRGB4( tscr->ViewPort.ColorMap, 1 );
}

void tv_proc( void )
{
	struct IntuiMessage *im, msg;

	while( ( im = (struct IntuiMessage *) GetMsg( twin->UserPort ) ) )
	{
		msg = *im;
		ReplyMsg( im );

		switch( msg.Class )
		{
			case IDCMP_MENUHELP:
				domenuhelp("terminal_mitschnitt",&msg);
				break;

			case MENUPICK:
				switch( ITEMNUM( msg.Code ) )
				{
					case 0: tv_clear(); break;

					case 1: tv_save(); break;
					case 2: tv_save_proto(); break;

					case 4: tv_docolprefs();
							break;

					case 8: ClearMenuStrip( twin );
							CloseWindow( twin );
							CloseScreen( tscr );
							twin = NULL;
							tscr = NULL;
							tvsig = 0;
							return;

					case 6: ScreenToFront( scr );
							ActivateWindow( tw );
							break;


				}
				recalcprop();
				break;

			case GADGETUP:
			case MOUSEMOVE:
				reposprop();
				break;

			case RAWKEY:
				switch( msg.Code )
				{
					case 0x4c:	/* Cursor hoch */
					case 0x3e:
						if( ISCTRL( &msg ) )
							scrollertop = 0;
						else if( ISSHIFT( &msg ) )
							scrollertop -= slines;
						else
							scrollertop--;
						recalcprop();
						redraw();
						break;

					case 0x4d:	/* Cursor runter */
					case 0x1e:
						if( ISCTRL( &msg ) )
							scrollertop = listlines;
						else if( ISSHIFT( &msg ) )
							scrollertop += slines;
						else
							scrollertop++;
						recalcprop();
						redraw();
						break;

					case 0x3d:	/* Home */
						scrollertop = 0;
						recalcprop();
						redraw();
						break;

					case 0x1d:	/* End */
						scrollertop = listlines;
						recalcprop();
						redraw();
						break;

					case 0x3f:	/* Page up */
						scrollertop -= slines;
						recalcprop();
						redraw();
						break;

					case 0x1f:	/* Page Down */
						scrollertop += slines;
						recalcprop();
						redraw();
						break;
				}
		}
	}
}

int tv_changed( void )
{
	return( !IsListEmpty( &tlist ) && changed );
}
