/* 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

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

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

static struct FontSet *custfontset;	/* The font set loaded in the fontset dlg */

static PtTerminalCharsets_t *newcset;

#define CSLEAK 1 /* Nonzero enables charset-leak detection... */

#if ! CSLEAK
	#define freecs free
	#define freecsd free
	#define INC_CS_CTR( ptr ) ((void)(ptr))
	#define INC_CSD_CTR( ptr ) ((void)(ptr))
#else
	static unsigned cs_ctr, csd_ctr;
	#define INC_CS_CTR( ptr )  (++cs_ctr)
	#define INC_CSD_CTR( ptr ) (++csd_ctr)
	static void freecs( PtTerminalCharsets_t *cs ) {
		if ( cs ) {
			free( cs );
			--cs_ctr;
		}	}
	static void freecsd( PtTerminalCsXlatData_t *csd ) {
		if ( csd ) {
			free( csd );
			--csd_ctr;
		}	}
#endif

void but_able( int en, PtWidget_t *wgt ) {
	/* Enable or disable a button (or another widget) */
	PtArg_t arg;
	PtSetArg( &arg, Pt_ARG_FLAGS,
		en ? Pt_FALSE : Pt_TRUE,
		Pt_BLOCKED | Pt_GHOST
		);
	PtSetResources( wgt, 1, &arg );
	}


static void fontlist_able( const char *font ) {
	/* Enable or disable font list buttons */
	int const en = font != NULL;
	but_able( en, ABW_but_delFont );
	but_able( en, ABW_but_chgFont );
	but_able( en, ABW_but_moveUp );
	but_able( en, ABW_but_moveDown );
	}

static void strtabfree( char **tab, int n ) {
	if ( n ) {
		char **p = tab;
		do	free( *p++ );
			while ( --n );
		}
	free( tab );
	}

static int strclone( char **pp ) {
	char const *p;
	return ( p = *pp ) == NULL || ( *pp = strdup(p) ) != NULL;
	}

static int strtabclone( char ***tabp, int n ) {
	if ( n ) {
		char **tab = *tabp;
		char **result;
		if ( ( result = calloc( n, sizeof(*result) ) ) == NULL )
			return 0;
		else {
			char **p = result; int i = n;
			do	if ( ( *p++ = strdup(*tab++) ) == NULL ) {
					strtabfree( result, n-i );
					return 0;
					}
				while ( --i );
			*tabp = result;
		}	}
	return 1;
	}

static struct FontSet *fssave( struct FontSet *fs ) {
	/* Save old fontset info in fs->saved */
	if ( fs->saved == NULL ) {
		struct FontSet *ss;
		if ( ( ss = malloc( sizeof(*ss) ) ) != NULL ) {
			int const n = fs->nfonts;
			*ss = *fs;
			if ( strtabclone( &ss->dispnames, n ) ) {
				ss->fullnames = ss->dispnames;
				fs->csets  = NULL;
				fs->csdata = NULL;
				if ( strclone( &ss->csfile ) )
					return fs->saved = ss;
				strtabfree( ss->dispnames, n );
				}
			free( ss );
		}	}
	return fs->saved;
	}

static void fsempty( struct FontSet *fs ) {
	free( fs->csfile );
	freecs( fs->csets );
	freecsd( fs->csdata );
	strtabfree( fs->dispnames, fs->nfonts );
	}

void fontsets_apply( void ) {
	/* Free the saved fontset info */
	struct FontSet *fs = fontsets;
	unsigned n = nfontsets;
	do	{
		struct FontSet *ss;
		if ( ( ss = fs->saved ) != NULL ) {
			fs->saved = NULL;
			if	(	fs->csfile && ! fs->csets && ss->csfile
				&&	!strcmp( fs->csfile, ss->csfile )
					// This strcmp() should always be true...
					) {
				fs->csdata = ss->csdata;
				fs->csets  = ss->csets;
				ss->csdata = NULL;
				ss->csets  = NULL;
				}
			fsempty( ss );
			free( ss );
			}
		if ( fs == custfontset )
			fssave( custfontset );
		++fs;
		}	while ( --n );

	}

void fontsets_cancel( void ) {
	/* Restore the saved fontset info */
	struct FontSet *fs = fontsets;
	unsigned n = nfontsets;
	do	{
		struct FontSet *ss;
		if ( ( ss = fs->saved ) != NULL ) {
			fsempty( fs );
			*fs = *ss;
			free( ss );
			}
		++fs;
		}	while ( --n );
	}

static void setcsfilename( const char *path ) {
	PtArg_t args[2];
	PtSetArg( &args[0], Pt_ARG_TEXT_STRING, path, 0 );
	PtSetArg( &args[1], Pt_ARG_FLAGS, Pt_SET, Pt_SET | Pt_GHOST | Pt_BLOCKED );
	PtSetResources( ABW_csfilename, 2, args );
	}

static const char *getcsfilename( void ) {
	PtArg_t args[2];
	PtSetArg( &args[0], Pt_ARG_TEXT_STRING, 0, 0 );
	PtSetArg( &args[1], Pt_ARG_FLAGS, 0, 0 );
	PtGetResources( ABW_csfilename, 2, args );
	return args[1].value & Pt_SET ? (char*) args[0].value : NULL;
	}

int
fillfcfg( PtWidget_t *link_instance, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	struct FontSet *fs;

	/* eliminate 'unreferenced' warnings */
	link_instance = link_instance, apinfo = apinfo, cbinfo = cbinfo;

	if ( ! fssave( custfontset = fs = curfontset ) )
		return Pt_END;

	setres_len( ABW_list_fonts, Pt_ARG_ITEMS, fs->fullnames, fs->nfonts );
	if ( fs->nfonts ) {
		unsigned short const index = fs->dflt + 1;
		const char *font = fs->fullnames[ fs->dflt ];
		setres_len( ABW_list_fonts, Pt_ARG_SELECTION_INDEXES, &index, 1 );
		setres( ABW_fontsel, Pt_ARG_FONT_NAME, font );
		fontlist_able( font );
		}
	if ( fs->csfile )
		setcsfilename( fs->csfile );

	xflags |= XF_FDLG;

	PtContainerGiveFocus( ABW_fontsel, cbinfo->event );

	return Pt_CONTINUE;
	}

static void getcsfn( void ) {
	/*	Apply either newcset or NULL to *custfontset,
	 *	depending on the state of the buttons
	 */
	const char *cs = getcsfilename();
	struct FontSet *fs = custfontset;
	PtTerminalCharsets_t *oldcs, *newcs;
	newstr( &fs->csfile, cs );
	if ( cs == NULL ) {
		newcs = NULL;
		oldcs = newcset;
		}
	else
		if ( ( newcs = newcset ) == NULL )
			return;
		else
			oldcs =  fs->csets;
	freecs( oldcs );
	fs->csets = newcs;
	freecsd( fs->csdata );
	fs->csdata = NULL;
	newcset = NULL;
	}

int
donefdlg( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	struct FontSet *fs;
	PtArg_t arg; char **ff; int i;
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	PtSetArg( &arg, Pt_ARG_ITEMS, &ff, 0 );
	PtGetResources( ABW_list_fonts, 1, &arg );

	if ( ! strtabclone( &ff, i = arg.len ) )
		return Pt_END;

	fs = custfontset;
	strtabfree( fs->dispnames, fs->nfonts );
	fs->dispnames = fs->fullnames = ff;
	fs->nfonts = i;

	if ( fs->dflt >= i )
		fs->dflt = 0;

	if ( i > 0 ) {
		struct FontSet *ss = fs->saved;
		if ( ss->nfonts ) {
			const char *f = fs->saved->dispnames[ fs->saved->dflt ];
			while ( --i >= 0 && strcmp( f, ff[i] ) );
			if ( i >= 0 )
				fs->dflt = i;
		}	}

	getcsfn();

	if ( fs == curfontset )
		setfontsizecombo();

	return Pt_CONTINUE;
	}

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

	setres( ABW_txt_curR, Pt_ARG_TEXT_STRING, "25" );
	setres( ABW_txt_curC, Pt_ARG_TEXT_STRING, "80" );

	return Pt_CONTINUE;
	}

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

	setstring( ABW_txt_window, default_title );

	return Pt_CONTINUE;
	}

static char **getlist( unsigned *count, unsigned *selected ) {
	/* Make a copy of the items list in one chunk of memory */
	PtArg_t args[2]; unsigned sum, i, n;
	char **ptr; char const* const* list;
	PtSetArg( &args[0], Pt_ARG_ITEMS, 0, 0 );
	PtSetArg( &args[1], Pt_ARG_SEL_INDEXES, 0, 0 );
	PtGetResources( ABW_list_fonts, 2, args );
	*selected
	=	args[1].len > 0
	?	* (unsigned short const*) args[1].value
	:	0;
	*count = sum = n = args[0].len;
	list = (char const* const*) args[0].value;
	for ( i=0; i<n; ++i )
		sum += strlen( list[i] );
	if ( ( ptr = malloc( sum + (n+1) * sizeof(ptr[0]) ) ) != NULL ) {
		char *s = (char*)( ptr + n + 1 );
		ptr[ n ] = NULL;
		for ( i=0; i<n; ++i )
			s += 1 + strlen( strcpy( ptr[i] = s, list[i] ) );
		}
	return ptr;
	}

static void setlist( char **list, unsigned n, unsigned short sel ) {
	/* Put the list back into widget and free the list */
	PtArg_t args[2];
	PtSetArg( &args[0], Pt_ARG_ITEMS, list, n );
	PtSetArg( &args[1], Pt_ARG_SEL_INDEXES, &sel, sel ? 1 : 0 );
	PtSetResources( ABW_list_fonts, 2, args );
	free( list );
	}

int
addFont( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	const char *txt; char *fnt;
	char **list;
	unsigned len, sel, i;
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	if	(	( txt = getptr( ABW_fontsel, Pt_ARG_FONT_NAME ) ) != NULL
		&&	txt[0] != '\0'
		&&	( list = getlist( &len, &sel ) ) != NULL
		&&	( fnt = PtTerminalFont( txt, NULL, NULL ) ) != NULL
			) {
		for ( i=len; i>sel; --i )
			list[ i ] = list[ i-1 ];
		list[ i ] = fnt;
		fontlist_able( list[sel] );
		setlist( list, len+1, sel+1 );
		free( fnt );
		}
	return Pt_CONTINUE;
	}


int
chgFont( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	PtArg_t arg; unsigned short *ndxs;
	PtSetArg( &arg, Pt_ARG_SEL_INDEXES, &ndxs, 0 );
	PtGetResources( ABW_list_fonts, 1, &arg );
	if ( arg.len ) {
		const char *newfont; int ndx = *ndxs;
		PtSetArg( &arg, Pt_ARG_FONT_NAME, &newfont, 0 );
		PtGetResources( ABW_fontsel, 1, &arg );
		PtListReplaceItemPos( ABW_list_fonts, &newfont, 1, ndx );
		}
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;
	return Pt_CONTINUE;
	}


int
delFont( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	char **list; unsigned len, sel=0, i;
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	fontlist_able( NULL );
	if ( ( list = getlist( &len, &sel ) ) != NULL )
		if	(	len == 0
			||	sel == 0
				)
			free( list );
		else {
			for ( i=sel; i<len; ++i )
				list[ i-1 ] = list[ i ];
			if ( sel == len )
				sel = 0;
			else
				fontlist_able( list[ sel - 1 ] );
			setlist( list, len-1, sel );
			}
	return Pt_CONTINUE;
	}

static void move( int delta ) {
	char **list, *tmp; unsigned len, sel; int dst;
	if ( ( list = getlist( &len, &sel ) ) != NULL )
		if	(	sel == 0
			||	( dst = --sel + delta ) >= len
			||	dst < 0
				)
			free( list );
		else {
			tmp = list[ sel ];
			list[ sel ] = list[ dst ];
			list[ dst ] = tmp;
			setlist( list, len, dst+1 );
	}		}

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

	move( -1 );

	return( Pt_CONTINUE );
	}

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

	move( 1 );

	return( Pt_CONTINUE );
	}

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

	xflags &= ~XF_FDLG;
	custfontset = NULL;

	freecs( newcset );
	newcset = NULL;

	return Pt_CONTINUE;
	}

int setfont( PtWidget_t *wgt, const char *str ) {
	static char *sample = NULL;
	char *fnt;
	if	(	sample == NULL
		&&	( sample = strdup( getstring( wgt ) ) ) == NULL
			)
		return -1;
	if ( ( fnt = PtTerminalFont( str, NULL, NULL ) ) == NULL ) {
		setstring( wgt, "" );
		return -1;
		}
	else {
		PtArg_t args[2];
		PtSetArg( &args[0], Pt_ARG_TEXT_FONT, fnt, 0 );
		PtSetArg( &args[1], Pt_ARG_TEXT_STRING, sample, 0 );
		PtSetResources( wgt, 2, args );
		free( fnt );
		return 0;
	}	}


int
showfont_sel( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{	/* CB_SELECTION callback of combo_font */

	if ( cbinfo->reason_subtype == Pt_LIST_SELECTION_FINAL ) {
		PtListCallback_t const *txt = cbinfo->cbdata;
		setfont( ABW_lbl_font,
			curfontset->fullnames[ 
				curfontset->dflt = txt->item_pos - 1
				]
			);
		}

	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	return Pt_CONTINUE;
	}

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

	PtContainerGiveFocus( widget, cbinfo->event );

	return Pt_CONTINUE;
	}

int
listfont_sel( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	PtListCallback_t const *cb = cbinfo->cbdata;
	const char *item = cb->item;
	fontlist_able( item );
//	if ( item && *item )
	setres( ABW_fontsel, Pt_ARG_FONT_NAME, item );

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

int
numtxtv( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	/* Modify Verify callback for numerical text fields */
	{
	PtTextCallback_t *cb = cbinfo->cbdata;
	unsigned len;
	/* eliminate 'unreferenced' warnings */
	widget = widget, apinfo = apinfo, cbinfo = cbinfo;

	if ( ( len = cb->length ) != 0 ) {
		const char *txt = cb->text;
		while	(	isdigit( *txt++ )
				&&	--len != 0
					);
		if ( len )
			cb->doit = 0;
		}
	return Pt_CONTINUE;
	}

static void setonoff( PtWidget_t *wgt, int newval ) {
	setres( wgt, Pt_ARG_ONOFF_STATE, newval );
	}

int
xgrpsel( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	/* Select the OnOffButton from an exclusive group */
	{
	PtWidget_t *brother;
	/* eliminate 'unreferenced' warnings */
	apinfo = apinfo, cbinfo = cbinfo;

	setonoff( widget, 1 );

	for	(	brother = widget;
			( brother = PtWidgetBrotherInFront( brother ) ) != NULL;
			)
		setonoff( brother, 0 );

	for	(	brother = widget;
			( brother = PtWidgetBrotherBehind( brother ) ) != NULL;
			)
		setonoff( brother, 0 );

	return Pt_CONTINUE;
	}

int
toggle( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	/* Select the OnOffButton from an exclusive group */
	{
	/* eliminate 'unreferenced' warnings */
	apinfo = apinfo, cbinfo = cbinfo;

	setonoff( widget, ! getres( widget, Pt_ARG_ONOFF_STATE ) );

	return Pt_CONTINUE;
	}


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

	xflags |= XF_HDLG;

	return( Pt_CONTINUE );
	}

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

	xflags &= ~XF_HDLG;

	return( Pt_CONTINUE );
	}


int
selcsfile( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
	{
	static PtFileSelectionInfo_t info;
	static const PhPoint_t pos = { 400, 200 };
	if ( info.path[0] == '\0' ) {
		char *p;
		if	(	( p = custfontset->csfile ) == NULL
			&&	( p = getenv("HOME") ) == NULL
				)
			p = "/";
		strcpy( info.path, p );
		strcpy( info.fspec, "*.cs" );
		}

	if	(	PtFileSelection( ABW_cfgfonts,
				&pos, "Choose file", info.path, info.fspec,
				"&Select", "&Cancel", "ndo", &info, 0
				)
		==	0
		&&	info.ret == Pt_FSDIALOG_BTN1
			) {
		PtTerminalCharsets_t *newcs;
		if ( ( newcs = PxTerminalLoadCharsets( info.path, NULL ) ) != NULL ) {
			INC_CS_CTR( newcs );
			freecs( newcset );
			newcset = newcs;
			setcsfilename( info.path );
			}
		else
			ApCreateModule( ABM_cserrdlg, widget, cbinfo );
		}

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

void setcharset( void ) {
	const char *fn;
	PtTerminalCharsets_t *cs;
	if ( ( fn = curfontset->csfile ) == NULL )
		cs = NULL;
	else
		if ( ( cs = curfontset->csets ) == NULL )
			if ( ( cs = PxTerminalLoadCharsets( fn, NULL ) ) != NULL ) {
				INC_CS_CTR( cs );
				curfontset->csets = cs;
				}
			else
				ApCreateModule( ABM_cserrdlg, NULL, NULL );
	if ( cs != NULL && curfontset->csdata == NULL ) {
		curfontset->csdata = PtTerminalCreateCsXlat( cs );
		INC_CSD_CTR( curfontset->csdata );
		}
	setres( ABW_tty, Pt_ARG_TERM_CHARSETS, curfontset->csdata );
	}


