/*
 * This sample program demonstrates the use of the Selectric protocol with
 * BoxKite under TOS/MiNT/MultiTOS/MagiC/MagiC Mac
 * (c) Harald Becker, 12.5.1994. Compiles with Pure C and DEFAULT.PRJ.
 *
 * You are free to use this code as a whole or in part in your own programs
 * 
 * HB 061095:
 * Extended to demonstrate the use of the message callback in BoxKite 1.7x
 * A small window is opened behind the file selector which will be redrawn by
 * alling the message handler. It can be moved, if the operating system
 * allows the user to move bottomed windows.
 *
 * HB 131095:
 * Extended to demonstrate all three possible possible communication methods.
 * Choose a method by setting the #defined constant 'RETURN' before compiling.
 *
 * HB 200196:
 * The binding of fsel_boxinput() was changed. You have to pass the global[]
 * array explicitly now.
 */
#include <stdio.h>
#include <aes.h>
#include <tos.h>
#include <string.h>

/*
 * This constant defines the method the sample program uses to communicate 
 * with BoxKite. Possible values are:
 *
 *     1: Return names in an array of string pointers.
 *     2: Return names in a space-separated string.
 *     3: The caller will request names one by one.
 */
#define RETURN 1

/*
 * This struct is filled by BoxKite. It is identical to the TOS DTA, but
 * allows for longer names
 */
typedef struct
{	char			d_reserved[21];
	unsigned char	d_attrib;
	unsigned int	d_time;
	unsigned int	d_date;
	unsigned long	d_length;
	char			d_fname[34];
} XDTA;

/*
 * The Selectric communications structure.
 */
typedef struct
{	unsigned long	id;			/* Selectric ID (SLCT)		*/
	unsigned int	version;	/* version (BCD-Format)		*/
	struct
	{	unsigned           : 7;    /* reserved              */
		unsigned pthsav    : 1;
		unsigned stdest    : 1;
		unsigned           : 1;
		unsigned numsrt    : 1;   /* numeric sort           */
		unsigned lower     : 1;
		unsigned dclick    : 1;   /* open folders on double click    */
		unsigned hidden    : 1;   /* show hidden files      */
		unsigned onoff     : 1;   /* BoxKite  ON/OFF        */
	} config;
	int	sort;			/* sort-mode (neg. = rev.)	*/
	int	num_ext;		/* number of extensions		*/
	char *(*ext)[];		/* preset extensions		*/
	int	num_paths;		/* number of paths		*/
	char *(*paths)[];		/* preset paths			*/
	int	comm;			/* communication word		*/
	int	in_count;		/* input counter		*/
	void *in_ptr;		/* input pointer		*/
	int	out_count;		/* output counter		*/
	void *out_ptr;		/* output pointer		*/
	int	cdecl	(*get_first)(XDTA *dta, int attrib);
	int	cdecl 	(*get_next)(XDTA *dta);
	int	cdecl	(*release_dir)(void);
} SLCT_STR;

typedef struct
{	long	id;
	long	value;
} COOKIE;

long Supexec(long (*codeptr)());

SLCT_STR *slct_cookie;

/*
 * Prototype for the message callback
 */
typedef void cdecl (* FSEL_CALLBACK)(int *msg);

/*
 * Prototype for the extended binding
 */
int cdecl fsel_boxinput(char *path, char *name, int *button, char *label, FSEL_CALLBACK callback, int *global);

/*
 * Space for file names delivered by BoxKite
 */
#if RETURN == 1
	char name1[34], name2[34], name3[34];
	char *nameptrs[] = { name1, name2, name3 };
#elif RETURN == 2
	char stringbuf[256];
#else
	int	cdecl (*p_get_first)(XDTA *dta, int attrib);
	int	cdecl (*p_get_next)(XDTA *dta);
	int	cdecl (*p_release_dir)(void);
#endif

int name_count;		/* Name count */

int window;			/* Window handle */
OBJECT contents = 	/* This is displayed in the window */
	{ -1, -1, -1, G_BOXCHAR, 0, 0, 0x2aff11f0, 1, 1, 1, 1 };

int xdesk, ydesk, wdesk, hdesk;

/*
 * Get a pointer to the cookie jar. Call by Supexec().
 */
long get_cookiejar(void)
{	return *((long *)0x05a0l);
}

/*
 * Scan the cookie jar for a certain cookie and get its value.
 */
long get_cookie(long id)
{	long sav;
	COOKIE *cookiejar;
	int	i = 0;

	cookiejar = (COOKIE *)Supexec(get_cookiejar);

	if ( cookiejar )
	{	while ( cookiejar[i].id )
		{	if ( cookiejar[i].id == id )
				return cookiejar[i].value;
			i++;
		}
	}
	return 0L;
}

/*
 * Checks the FSEL cookie for a Selectric-compatible file selector. In case
 * of success the communications structure is set up for the desired return
 * method. Call this by Supexec(), since it accesses memory belonging to a
 * different process.
 */
long setup_selector(void)
{	if ( slct_cookie && slct_cookie->id == 'SLCT' && slct_cookie->version >= 0x102 )
	{
#if RETURN == 1	
		slct_cookie->comm = 1;
		slct_cookie->out_count = 3;
		slct_cookie->out_ptr = nameptrs;
#elif RETURN == 2
		slct_cookie->comm = 3;
		slct_cookie->out_count = 5;
		slct_cookie->out_ptr = stringbuf;
#else
		slct_cookie->comm = 9;
		p_get_first = slct_cookie->get_first;
		p_get_next = slct_cookie->get_next;
		p_release_dir = slct_cookie->release_dir;

#endif
		return 1;
	}
	return 0;
}

/*
 * Get the name count from the FSEL struct.
 */
long get_out_count(void)
{	name_count = slct_cookie->out_count;
	return 0;
}

/*
 * Get the current drive and path.
 */
void getpath(char *p)
{	int drv;

	drv = Dgetdrv();
	*p++ = drv + 'A';
	*p++ = ':';
	Dgetpath(p, drv);
	p += strlen(p) - 1;
	if ( *p != '\\' )
	{	p++;
		*p++ = '\\';
		*p = 0;
	}
}

/* 
 * The next four functions manage and draw the background window.
 * The method ought to be widely known by now ;-)
 */
int min(int a, int b)
{	return ( a < b ? a : b );
}

int max(int a, int b)
{	return ( a > b ? a : b );
}

int rc_intersect(GRECT *p1, GRECT *p2)
{	int tx, ty, tw, th;

	tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);
	th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);
	tx = max(p2->g_x, p1->g_x);
	ty = max(p2->g_y, p1->g_y);

	p2->g_x = tx;
	p2->g_y = ty;
	p2->g_w = tw - tx;
	p2->g_h = th - ty;

	return( (tw > tx) && (th > ty) );
}

void window_redraw(int handle, int x, int y, int w, int h)
{	GRECT r1, r2, world;

	wind_update(BEG_UPDATE);

	r2.g_x = x;
	r2.g_y = y;
	r2.g_w = w;
	r2.g_h = h;

	world.g_x = xdesk;
	world.g_y = ydesk;
	world.g_w = wdesk;
	world.g_h = hdesk;

	wind_get(handle, WF_FIRSTXYWH, &r1.g_x, &r1.g_y, &r1.g_w, &r1.g_h);
	while ( r1.g_w && r1.g_h )
	{	if ( rc_intersect(&world, &r1) && rc_intersect(&r2, &r1) )
			objc_draw(&contents, ROOT, MAX_DEPTH, r1.g_x, r1.g_y, r1.g_w, r1.g_h);
		wind_get(handle, WF_NEXTXYWH, &r1.g_x, &r1.g_y, &r1.g_w, &r1.g_h);
	}
	wind_update(END_UPDATE);
}

/* 
 * The entry point of this function is the callback adress passed to 
 * BoxKite.
 * This example shows that the message handler does not have to be
 * limited to WM_REDRAW messages. It is a good idea to handle WM_MOVED
 * messages, since there are operating systems which allow the user to move
 * bottomed windows. However, messages which would open new windows or top 
 * existing ones ought to be ignored or saved and processed after the file 
 * selector has returned.
 * BoxKite will not pass WM_TOPPED messages to the message callback.
 */
void cdecl message_handler(int *msg)
{	switch ( msg[0] )
	{	case WM_REDRAW:
			wind_get(msg[3], WF_WORKXYWH, &contents.ob_x, &contents.ob_y, &contents.ob_width, &contents.ob_height);
			window_redraw(msg[3], msg[4], msg[5], msg[6], msg[7]);
			break;
		case WM_MOVED:
			wind_set(msg[3], WF_CURRXYWH, msg[4], msg[5], msg[6], msg[7]);
			break;
	}
}

/*
 * This is a new binding for the standard GEM function 'fsel_exinput'.
 * It uses the same AES function number and passes the callback adress
 * as an additional parameter in addrin[]. Other file selectors should 
 * simply ignore this.
 * 
 * An assembler version of this binding is provided in BOXINPUT.S 
 * and BOXINPUT.O (as an object file in DRI format). This version does 
 * not assume the availability of a generic GEM trap function (such as 
 * '_crystal' in Pure C, see below) in your programming language.
 *
 * BoxKite requires information from the main application's 'global' array.
 * Most development packages provide bindings which pass this array
 * automagically. To make this binding independent from specific
 * programming languages, you have to pass it explicitly here.
 */
int cdecl fsel_boxinput(char *path, char *name, int *button, char *label, FSEL_CALLBACK callback, int *global)
{	void *aespb[6], *addrin[6], *addrout[6];
	int contrl[5], intin[16], intout[7];

	aespb[0] = contrl;
	aespb[1] = global;
	aespb[2] = intin;
	aespb[3] = intout;
	aespb[4] = addrin;
	aespb[5] = addrout;

	contrl[0] = 91;
	contrl[1] = 0;
	contrl[2] = 2;
	contrl[3] = 4;
	contrl[4] = 0;

	addrin[0] = path;
	addrin[1] = name;
	addrin[2] = label;
	addrin[3] = callback;

	_crystal((AESPB *)aespb);

	*button = intout[1];
	return intout[0];
}

int main(void)
{	XDTA mydta;
	char path[130], name[34], outbuf[300];
	int exbtn, rv, d, i;

	appl_init();
	wind_get(0, WF_WORKXYWH, &xdesk, &ydesk, &wdesk, &hdesk);

	slct_cookie = (SLCT_STR *)get_cookie('FSEL');

	if ( !Supexec(setup_selector) )
	{	form_alert(1, "[1][No compatible file selector|installed.][   Ok   ]");
		appl_exit();
		return 0;
	}

	Pdomain(1);			/* Tell the OS that we know about long names */

	/*
	 * Open a window in the background to demonstrate that the message
	 * callback is working.
	 */
	window = wind_create(NAME | MOVER, xdesk, ydesk, wdesk, hdesk);
	if ( window < 0 )
	{	form_alert(1, "[1][The window is locked!][   Ok   ]");
		appl_exit();
		return 0;
	}
	wind_set(window, WF_NAME, "Yet another window");
	wind_open(window, xdesk + 50, ydesk + 50, 300, 200);
	window_redraw(window, xdesk, ydesk, wdesk, hdesk);

	getpath(path);
	strcat(path, "*.*");
	*name = 0;
#if RETURN == 1
	do
	{	Supexec(setup_selector);
		rv = fsel_boxinput(path, name, &exbtn, "BoxKite", &message_handler, _GemParBlk.global);

		if ( rv && exbtn )
		{	Supexec(get_out_count);
			for ( i = 0; i < name_count; i++ )
			{	sprintf(outbuf, "[1][File No. %d:|%s][   Ok   ]", i + 1, nameptrs[i]);
				form_alert(1, outbuf);
			}
		}
	}
	while ( rv && exbtn );	
#elif RETURN == 2
	do
	{	Supexec(setup_selector);
		rv = fsel_boxinput(path, name, &exbtn, "BoxKite", &message_handler, _GemParBlk.global);

		if ( rv && exbtn )
		{	sprintf(outbuf, "[1][%s][   Ok   ]", stringbuf);
			form_alert(1, outbuf);
		}
	}
	while ( rv && exbtn );
#else
	do
	{	Supexec(setup_selector);
		rv = fsel_boxinput(path, name, &exbtn, "BoxKite", &message_handler, _GemParBlk.global);
		if ( rv && exbtn )
		{	wind_update(BEG_UPDATE);
			d = p_get_first(&mydta, 0xff);
			i = 1;
			while( !d )
			{	sprintf(outbuf, "[1][File No. %d:|%s %02x][   Ok   ]", i, mydta.d_fname, mydta.d_attrib);
				form_alert(1, outbuf);
				d = p_get_next(&mydta);
				i++;
			}
			p_release_dir();
			wind_update(END_UPDATE);
		}
	}
	while ( rv && exbtn );
#endif
	wind_close(window);
	wind_delete(window);
	appl_exit();
	return 0;
}
