/* Y o u r   D e s c r i p t i o n                       */
/*                            AppBuilder Photon Code Lib */
/*                                         Version 0.91B */

/* Standard headers */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>

/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>

/* Local headers */
#include "phterm.h"
#include "abimport.h"
#include "proto.h"
#include "wgtid.h"

/*************  Fix sizes in cfg ***********/

static void fixsizes( void ) {
	if ( cfg.mins.r > cfg.size.r ) cfg.mins.r = cfg.size.r;
	if ( cfg.mins.c > cfg.size.c ) cfg.mins.c = cfg.size.c;
	if ( cfg.maxs.r < cfg.size.r ) cfg.maxs.r = cfg.size.r;
	if ( cfg.maxs.c < cfg.size.c ) cfg.maxs.c = cfg.size.c;
	}

/*****  Allocate new string *******/

static void chgstr( char **var, char *val ) {
	if ( *var )
		free( *var );
	*var = val;
	}

void newstr( char **var, const char *val ) {
	chgstr( var, val == NULL ? NULL : strdup( val ) );
	}


/*****  Title clock  *******/

int
timer_tick( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) {
	if ( xflags & (XF_TPRG|XF_TCLK) )
		set_title();

	/* eliminate 'unreferenced' warnings */
	apinfo = apinfo, cbinfo = cbinfo;
	return Pt_CONTINUE;
	}

/************  Parsing the title  *******************/

static void set_title_flags( void ) {
	const char *t;
	xflags &= ~(XF_TDEV|XF_TPRG|XF_TCMD|XF_TCLK);
	if ( ( t = title ) != NULL )
		for (;;)
			switch ( *t++ ) {
				case '\0' : return;
				case '%' :
					switch ( *t++ ) {
						case '\0' : return;
						case 'p' : case 'd' : case 'n' : case 't' :
							xflags |= XF_TDEV; break;
						case 'T' :
							xflags |= XF_TCLK; break;
						case 'P' : case 'N' : case '$' :
							xflags |= XF_TPRG; break;
						case 'A' : case 'a' : case '0' :
							xflags |= XF_TPRG | XF_TCMD; break;
	}			}		}

/*************  Cursor timer  **************/

static void settimer( void ) {
	int cflags;
	set_title_flags();
	switch ( cfg.flags & ( FL_CAB | FL_CNB ) ) {
		default :      cflags = Pt_TERM_CURSOR_ON_FOCUS; break;
		case FL_CAB :  cflags = Pt_TERM_CURSOR_ALWAYS; break;
		case FL_CNB :  cflags = Pt_TERM_CURSOR_NEVER; break;
		}

	if ( xflags & (XF_TPRG|XF_TCLK) )
		cflags |= Pt_TERM_CURSOR_TIMER;
	setres_len( ABW_tty, Pt_ARG_TERM_CURSOR_FLAGS, cflags, 
		Pt_TERM_CURSOR_MASK | Pt_TERM_CURSOR_TIMER
		);
	}

/************  Setting the title  *******************/

void check_title( void ) {
	set_title_flags();
	settimer();
	set_title();
	}

/*************  Transfer cfg data to the PtTty widget ***********/

static int getnumstring( PtWidget_t *wgt ) {
	return atoi( getstring( wgt ) );
	}

static int butison( PtWidget_t *wgt ) {
	return getres( wgt, Pt_ARG_ONOFF_STATE ) != 0;
	}

#define SET_ARG( ptr, type, val, len ) \
	do { PtSetArg( ptr, type, val, len ); ++ptr; } while(0)

void cfg2wgt( void ) {
	PtArg_t args[ 15 ], *arg = args;

	fixsizes();

	SET_ARG( arg, Pt_ARG_TERM_FONT_LIST,
		curfontset->fullnames, curfontset->nfonts
		);
	SET_ARG( arg, Pt_ARG_TERM_FONT_INDEX, curfontset->dflt, 0 );

	SET_ARG( arg, Pt_ARG_TERM_PROTOCOL,
		( cfg.flags & FL_QNX ) == 0, 0
		);
	SET_ARG( arg, Pt_ARG_TERM_VISUAL_BELL, cfg.flags & FL_VBL ? 20 : 0, 0 );

	SET_ARG( arg, Pt_ARG_TERM_MINSIZE, &cfg.mins, 0 );
	SET_ARG( arg, Pt_ARG_TERM_MAXSIZE, &cfg.maxs, 0 );
	SET_ARG( arg, Pt_ARG_TERM_SIZE, &cfg.size, 0 );
	SET_ARG( arg, Pt_ARG_TERM_SCRLBK_LIMIT, xcfg.scrlbk, 0 );

	PtSetResources( ABW_tty, arg-args, args );

	PtSetArg( &args[0], Pt_ARG_WINDOW_STATE,
		cfg.flags & FL_ALT ? Pt_TRUE : Pt_FALSE,
		Ph_WM_STATE_ISALTKEY
		);

	PtSetResources( ABW_base, 1, args );

	check_title();
	}

/****  Some useful fuctions and macros ****/

long getres( PtWidget_t *wgt, long res ) {
	PtArg_t arg;
	PtSetArg( &arg, res, 0, 0 );
	PtGetResources( wgt, 1, &arg );
	return arg.value;
	}

const char *getstring( PtWidget_t *wgt ) {
	return getptr( wgt, Pt_ARG_TEXT_STRING );
	}

void setresl_len( PtWidget_t *wgt, long res, long val, long len ) {
	PtArg_t arg;
	PtSetArg( &arg, res, val, len );
	PtSetResources( wgt, 1, &arg );
	}

void setresl( PtWidget_t *wgt, long res, long val ) {
	setresl_len( wgt, res, val, 0 );
	}

void setstring( PtWidget_t *wgt, const char *str ) {
	setres( wgt, Pt_ARG_TEXT_STRING, (long) str );
	}

/*************  Transfer cfg data from the PtTty widget ***********/

long getttyres( long res ) {
	return getres( ABW_tty, res );
	}

static void getttysize( long res, PtTerminalRowCol_t *dst ) {
	PtTerminalRowCol_t const *src = getttyptr( res );
	*dst = *src;
	}

static void setcfgflag( int yes, unsigned mask ) {
	if ( yes )
		cfg.flags |= mask;
	else
		cfg.flags &= ~mask;
	}

void wgt2cfg( void ) {
	short index;
	getttysize( Pt_ARG_TERM_SIZE, &cfg.size );
	getttysize( Pt_ARG_TERM_MINSIZE, &cfg.mins );
	getttysize( Pt_ARG_TERM_MAXSIZE, &cfg.maxs );

	if ( ( index = getttyres( Pt_ARG_TERM_FONT_INDEX ) ) >= 0 )
		curfontset->dflt = index;
	xcfg.scrlbk = getttyres( Pt_ARG_TERM_SCRLBK_LIMIT );

	setcfgflag(
		getttyres( Pt_ARG_TERM_PROTOCOL ) == 0,
		FL_QNX
		);

	setcfgflag( getttyres( Pt_ARG_TERM_VISUAL_BELL ), FL_VBL );
	}

/*****  Transferring data between cfg and config module  ********/

#define ENDOF( array )	( & (array)[ sizeof(array) / sizeof((array)[0]) ] )

struct SizeField {
	/* A pair of PtText widgets defines a terminal size */
	PtTerminalRowCol_t *cfg;
	WIDGET_ID_T	r, c;
	};

struct FlagField {
	/* A PtButton widget defines a flag */
	unsigned val;
	WIDGET_ID_T but;
	};

static const struct SizeField sizesf[] = {
	{ &cfg.size, WIDGET_ID( config, txt_curR ), WIDGET_ID( config, txt_curC ) },
	{ &cfg.mins, WIDGET_ID( config, txt_minR ), WIDGET_ID( config, txt_minC ) },
	{ &cfg.maxs, WIDGET_ID( config, txt_maxR ), WIDGET_ID( config, txt_maxC ) }
	};

static const struct FlagField cflagsf[] = {
	{ FL_QNX, WIDGET_ID( config, but_QNX ) },
	{ FL_VBL, WIDGET_ID( config, but_visualBell ) },
	{ FL_ALT, WIDGET_ID( config, but_vedit ) },
	};

void setfontsizecombo( void ) {
	int n; const char *font;
	setres_len( ABW_combo_fontSize, Pt_ARG_CBOX_ITEMS,
		curfontset->dispnames, n = curfontset->nfonts
		);
	but_able( n != 0, ABW_combo_fontSize );
	if ( n == 0 ) {
		setstring( ABW_combo_fontSize, "" );
		font = "helv10i";
		}
	else {
		n = curfontset->dflt;
		setres( ABW_combo_fontSize, Pt_ARG_CBOX_SEL_ITEM, n+1 );
		font = curfontset->fullnames[ n ];
		}
	setfont( ABW_lbl_font, font );
	}

static void setfontsetcombo( void ) {
	setres( ABW_combo_fontSet, Pt_ARG_CBOX_SEL_ITEM,
		( curfontset - fontsets ) + 1
		);
	setfontsizecombo();
	}

/*
static void getfontlist( PtWidget_t *wgt, long res ) {
	PtArg_t arg; int i;
	PtSetArg( &arg, res, 0, 0 );
	PtGetResources( wgt, 1, &arg );
	if ( fonts != NULL ) {
		for ( i=0; i<cfg.nfonts; ++i )
			free( fonts[i] );
		free( fonts );
		fonts = NULL;
		}
	cfg.nfonts = 0;
	if	(	arg.len != 0
		&&	( fonts = calloc( arg.len, sizeof(fonts[0]) ) ) != NULL
			) {
		const char *const *fl = (char const*const*) arg.value;
		while	(	( fonts[ cfg.nfonts ] = strdup( *fl++ ) ) != NULL
				&&	++cfg.nfonts != arg.len
					);
	}	}
*/

static void set_but_cfgfonts( void ) {
	but_able( curfontset->prefix == NULL, ABW_but_cfgfonts );
	}

static void getfontsetcombo( void ) {
	/* Handle a change in ABW_combo_fontSet */
	int i;

	if ( ( i = getres( ABW_combo_fontSet, Pt_ARG_CBOX_SEL_ITEM ) ) != 0 )
		--i;

	if ( curfontset != fontsets + i ) {
		char const *size = getstring( ABW_combo_fontSize );
		curfontset = fontsets + i;
		if	(	( i = curfontset->nfonts ) > 0
			&&	strcmp( size,  curfontset->dispnames[ curfontset->dflt ] )
				)
			do	if ( ! strcmp( size, curfontset->dispnames[ --i ] ) )
					curfontset->dflt = i;
				while ( i );
		setfontsizecombo();
		set_but_cfgfonts();
	}	}

int
fontset_changed( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	if ( cbinfo->reason_subtype == Pt_LIST_SELECTION_FINAL )
		getfontsetcombo();
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	return Pt_CONTINUE;
	}

static void getfontsizecombo( void ) {
	if ( curfontset->nfonts == 0 )
		for ( curfontset = fontsets; curfontset->nfonts == 0; ++curfontset );
	curfontset->dflt
	=	getres( ABW_combo_fontSize, Pt_ARG_CBOX_SEL_ITEM ) - 1;
	}

static void setnumstring( PtWidget_t *wgt, int num ) {
	char buf[ 10 ];
	setstring( wgt, itoa( num, buf, 10 ) );
	}

static void fillcfgdlg( void ) {
	/* Fill the dialog with data from cfg variables */
	const struct SizeField *s;
	const struct FlagField *f;
	PtWidget_t *wgt1, *wgt2;

	for ( s=sizesf; s < ENDOF( sizesf ); ++s ) {
		setnumstring( WIDGET( s->r ), s->cfg->r );
		setnumstring( WIDGET( s->c ), s->cfg->c );
		}

	setnumstring( ABW_scrlbk, xcfg.scrlbk );

	for ( f=cflagsf; f < ENDOF( cflagsf ); ++f )
		setres( WIDGET( f->but ), Pt_ARG_ONOFF_STATE,
			( cfg.flags & f->val ) != 0
			);
	setres( ABW_but_ANSI, Pt_ARG_ONOFF_STATE, ! butison( ABW_but_QNX ) );

	setfontsetcombo();
	set_but_cfgfonts();

	setres(
		cfg.flags & FL_CNB
		?	ABW_but_blinkNever
		:	cfg.flags & FL_CAB
			?	ABW_but_blinkAlways
			:	ABW_but_blinkFocus,
		Pt_ARG_ONOFF_STATE,
		1
		);

	setstring( wgt1 = ABW_but_ptermRc, afterslash( ptermrc ) );
	setstring( wgt2 = ABW_but_appRc, afterslash( cmdrc ) );

	setres(
		xflags & XF_SPECF
		?	wgt2
		:	wgt1,
		Pt_ARG_ONOFF_STATE,
		1
		);

	setstring( ABW_txt_window, title );
	}

static int flushcfgdlg( PtWidget_t *wgt, PtCallbackInfo_t *cb ) {
	/* Fill cfg variables with data from dialog */
	const struct SizeField *s;
	const struct FlagField *f;
	const PtTerminalSelection_t *sel;
	int np;
	unsigned const oldcfgfl = cfg.flags;

	if	(	! ( oldcfgfl & FL_QNX )
		!=	( np = butison( ABW_but_ANSI ) )
		&&	ask_for_stty( np, wgt, cb ) == -1
			)
		return -1;

	for ( s=sizesf; s < ENDOF( sizesf ); ++s ) {
		s->cfg->r = getnumstring( WIDGET( s->r ) );
		s->cfg->c = getnumstring( WIDGET( s->c ) );
		}
	xcfg.scrlbk = getnumstring( ABW_scrlbk );

	sel = getttyptr( Pt_ARG_TERM_SELECTION );
	if ( sel->flags & Pt_TERM_SELECTION_ALWAYS )
			cfg.flags |= FL_MSE;
		else
			cfg.flags &= ~FL_MSE;

	for ( f=cflagsf; f < ENDOF( cflagsf ); ++f )
		if ( butison( WIDGET( f->but ) ) )
			cfg.flags |= f->val;
		else
			cfg.flags &= ~f->val;

	fontsets_apply();
	getfontsizecombo();
	xcfg.fontset = curfontset - fontsets + 1;
	cfg.fidx = curfontset->dflt;

	cfg.flags &= ~(FL_CAB|FL_CNB);
	if ( butison( ABW_but_blinkNever ) )
		cfg.flags |= FL_CNB;
	else
		if ( butison( ABW_but_blinkAlways ) )
			cfg.flags |= FL_CAB;

	if ( butison( ABW_but_appRc ) )
		xflags |= XF_SPECF;
	else
		xflags &= ~XF_SPECF;

	newstr( &title, getstring( ABW_txt_window ) );
	return 0;
	}

/*****  config module callbacks  ********/

int
config_init( PtWidget_t *link_instance, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	link_instance = link_instance, apinfo = apinfo;

	if ( xflags & XF_NOCFG )
		return Pt_END;

	wgt2cfg();
	fillcfgdlg();

	PtContainerGiveFocus( ABW_txt_curR, cbinfo->event );

	return Pt_CONTINUE;
	}


int
config_apply( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo;

	if ( flushcfgdlg( ABW_but_configApply, cbinfo ) )
		return Pt_HALT;

	cfg2wgt();
	setcharset();

	/* ... and put the results back into the dialog */
	wgt2cfg();
	fillcfgdlg();

	return Pt_CONTINUE;
	}

int
config_enter( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	widget = widget;
	return
		cbinfo->reason_subtype == Pt_EDIT_ACTIVATE
		?	config_apply( ABW_but_configApply, apinfo, cbinfo )
		:	Pt_HALT;
	}

int
config_save( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo;

	if ( flushcfgdlg( ABW_but_configSave, cbinfo ) )
		return Pt_HALT;

	cfg2wgt();
	setcharset();
	if ( ! cfgsave() )
		return Pt_CONTINUE;
	else {
		/*	Write error: put the results back into the dialog.
		 */
		wgt2cfg();
		fillcfgdlg();
		return Pt_END;
	}	}

int
config_destroy( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	if ( xflags & XF_FDLG )
		PtDestroyWidget( ABW_cfgfonts );
	if ( xflags & XF_HDLG )
		PtDestroyWidget( ABW_tithelp );
	xflags &= ~XF_CDLG;
	fontsets_cancel();
	curfontset = fontsets + xcfg.fontset - 1;

	return Pt_CONTINUE;
	}


int
title_esc( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo;

	if	(	cbinfo->reason_subtype == 't'
		&&	cbinfo->cbdata != NULL
			) {
		newstr( &title, cbinfo->cbdata );
		set_title();
		}

	return Pt_CONTINUE;
	}


/*****  myalloc  *****/

#if TRACE_ALLOC

	void *myalloc( void *mem, unsigned len,
			const char *file, int line, const char *fun
			) {
		#undef realloc
		void *result;
		result = realloc( mem, len );
		printf( "%7s %10s %3d %4d %p -> %p\n",
			fun, file, line, len, mem, result
			);
		return result;
		}

#endif

