/*
**	requestchoice.c - request information from user
**	$VER: requestchoice.c 42.2 (27.12.96)
**	Copyright © 1996 Michal Letowski
**
**	42.1 (4.7.96) - initial version
**	42.2 (27.12.96)
**		! an invisible window was left on public screens - fixed
**		+ now reacts to break signal
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <utility/tagitem.h>
#include <support/types.h>
#include <support/exec.h>
#include <support/dos.h>

#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include "requestchoice.rev.h"


/*
**	Constants
*/
#define DOS_NAME					"dos.library"
#define DOS_VERN					37L
#define INT_NAME					"intuition.library"
#define INT_VERN					37L

#define TEMPLATE					"TITLE/A,BODY/A,GADGETS/A/M,PUBSCREEN/K,TIMEOUT/K/N"

#define DELAY_AMOUNT			10

#define MICROS_PER_SECOND	1000000

#define MicroTime(s,m)		(CurrentTime(&(s),&(m)), (s)*MICROS_PER_SECOND+(m))


/*
**	Private structures
*/
struct Options
{
	STRPTR  opt_Title;														/* TITLE/A */
	STRPTR  opt_Body;															/* BODY/A */
	STRPTR *opt_Gadgets;													/* GADGETS/A */
	STRPTR  opt_PubScreen;												/* PUBSCREEN/K */
	LONG   *opt_Timeout;													/* TIMEOUT/K/N */
};	/* Options */


/*
**	Global data
*/
STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);


/*
**	Level 1 private functions prototypes
*/
STATIC STRPTR CreateGadgetString(STRPTR *gadgets);


/*
**	Public functions
*/
/****** C/RequestChoice *****************************************************
*
*   NAME
*       RequestChoice -- create and handle a decision requester. (V39)
*
*   SYNOPSIS
*       RequestChoice Title Body Button1 Button2 ... ButtonN
*       [PUBSCREEN=PubScreenName] [TIMEOUT=TimeoutValue]
*
*   TEMPLATE
*       RequestChoice "TITLE/A,BODY/A,GADGETS/A/M,PUBSCREEN/K,TIMEOUT/K/N"
*
*   FUNCTION
*       Opens a requester window on a given public screen with a choice of
*       one or more buttons. Chosen button number is printed. Buttons are
*       numbered from left to right, with the leftmost button being number
*       1. The rightmost button has a number 0 assigned to it. If there is
*       only one button, it is treated as 0.
*       A requester can cancel itself after a specified amount of time. In
*       such case 0 is returned and printed.
*
*   INPUTS
*       TITLE     - a title of the requester window.
*       BODY      - a text to appear inside requester window.
*       GADGETS   - one or more strings representing button texts.
*       PUBSCREEN - name of the public screen the requester should appear on.
*                   If the screen is unavailable, the requester will appear
*                   on default public screen - in most cases Workbench.
*       TIMEOUT   - a value given in seconds after which a requester will
*                   cancel itself.
*
*   RESULT
*       RETURN_FAIL  - if 'dos.library' or 'intuition.library' couldn't be
*                      opened.
*       RETURN_ERROR - if command line arguments couldn't be processed or
*                      there wasn't enough memory.
*       RETURN_WARN  - if a given public screen couldn't be found or there
*                      was a problem building a requster.
*       RETURN_OK    - if everything went OK.
*
*   EXAMPLE
*       RequestChoice "Test1" "Choose OK or Cancel" "OK" "Cancel"
*           ; Puts a requester with 'Test' title, 'Choose OK or Cancel' text
*           ; inside requester window and two buttons named 'OK' and
*           ; 'Cancel'. If a user presses 'OK', '1' will be printed. If a
*           ; user presses 'Cancel', '0' will be printed.
*       RequestChoice "Test2" "Select number" "1" "2" "3" "4" "5"
*                     PUBSCREEN="DOPUS.1" TIMEOUT=10
*           ; Puts a requester with five choices buttons on Directory Opus'
*           ; screen. If a user doesn't press any button within ten seconds,
*           ; a requester will cancel itself and '0' will be printed.
*
*   NOTES
*       This version of the command differs from original in that it will
*       create a requester even if a given public screen doesn't exist.
*
*   BUGS
*       Timeouts are precise to about 1/3 of a second.
*
*   SEE ALSO
*       intuition.library/EasyRequestArgs().
*
*****************************************************************************
*
*/
LONG RequestChoice(VOID)
{
	STATIC CONST struct TagItem WinTags[]=
	{
		{WA_Left,						0},
		{WA_Top,						0},
		{WA_Width,					16},
		{WA_Height,					16},
		{WA_Backdrop,				TRUE},
		{WA_Borderless,			TRUE},
		{WA_NoCareRefresh,	TRUE},
		{WA_RMBTrap,				TRUE},
		{TAG_DONE,					0}
	};	/* WinTags */

	struct ExecBase *SysBase=INITSYSBASE;
	struct Library *DOSBase;
	struct Library *IntuitionBase;

	struct Options Opts;
	struct EasyStruct ES;
	struct RDArgs *Args;
	struct Window *Win=NULL,*Req;
	STRPTR GadBuf;
	ULONG Secs,Mics,Time;
	LONG ReqRes,RC=RETURN_FAIL;
	
	/* Open DOS */
	unless(DOSBase=OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_DOS);

	/* Open Intuition */
	unless(IntuitionBase=OpenLibrary(INT_NAME,INT_VERN))
		throw2(CauseIoErr(ERROR_INVALID_RESIDENT_LIBRARY,PROG_NAME),	NO_INT);

	RC=RETURN_ERROR;															/* Working a bit... */

	/* Read arguments */
	clear(&Opts);																	/* Clear out buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_ARGS);

	/* Merge gadgets strings into one big string */
	unless(GadBuf=CreateGadgetString(Opts.opt_Gadgets))
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_MEM);

	RC=RETURN_OK;																	/* Everything seems to be ok... */

	/* Prepare EasyRequest structure */
	ES.es_StructSize=sizeof(ES);
	ES.es_Flags=0;
	ES.es_Title=Opts.opt_Title;
	ES.es_TextFormat="%s";
	ES.es_GadgetFormat="%s";

	/* If public screen given, create a dummy window on it */
	if(Opts.opt_PubScreen)
		unless(Win=OpenWindowTags(NULL,	WA_PubScreenName,	Opts.opt_PubScreen,
																		TAG_MORE,					WinTags))
			RC=RETURN_WARN;														/* Warning if no public screen */

	/* Create requester */
	Req=BuildEasyRequest(Win,&ES,NULL,Opts.opt_Body,GadBuf);
	if(Win)
		CloseWindow(Win);														/* Close window (if any) */

	/* Handle requester */
	if((ULONG)Req>1)															/* Created successfully... */
	{
		/* Handle requester */
		if(Opts.opt_Timeout)
			Time=MicroTime(Secs,Mics)+
						clamp(*Opts.opt_Timeout,0,MAXINT/MICROS_PER_SECOND)*MICROS_PER_SECOND;
		forever
		{
			ReqRes=SysReqHandler(Req,NULL,FALSE);			/* Handle requester input */
			if(ReqRes>=0 || CheckSignal(SIGBREAKF_CTRL_C))
				break;																	/* Out of loop if gadget chosen or break */
			if(Opts.opt_Timeout && MicroTime(Secs,Mics)>=Time)
				break;																	/* Out of loop if timeout */
			Delay(DELAY_AMOUNT);											/* Wait a moment */
		}	/* forever */
		FreeSysRequest(Req);												/* Remove requester */
	}	/* if */
	else																					/* Created successfully */
	{
		ReqRes=(LONG)Req;														/* Set result */
		RC=RETURN_WARN;															/* So warn user */
	}	/* else */

	/* Print result */
	Printf("%ld\n",ReqRes<0 ? 0 : ReqRes);				/* 0 if timeout, else gadget code  */

	/* Exceptions */
	catch(BAD_ARGS,	);
	catch(NO_MEM,		FreeVec(GadBuf));
	catch(NO_ARGS,	FreeArgs(Args));
	catch(NO_INT,		CloseLibrary(IntuitionBase));
	catch(NO_DOS,		CloseLibrary(DOSBase));
	return(RC);
}	/* RequestChoice */


/*
**	Level 1 private functions
*/
STATIC STRPTR CreateGadgetString(STRPTR *gadgets)
{
	struct ExecBase *SysBase=INITSYSBASE;

	STRPTR *ArgPtr,CurArg,GadBuf,CurBuf;
	LONG GadLen=0;
	
	/* Process all gadgets strings */
	ArgPtr=gadgets;
	while(CurArg=*ArgPtr++)													/* For each gadget string */
		GadLen+=strlen(CurArg)+1;											/* Add length of current string */
	GadLen++;																				/* NULL terminator, GadLen>0 */

	/* Allocate memory for gadgets string */
	if(GadBuf=AllocVec(GadLen,MEMF_PUBCLR))					/* Try to allocate memory for merged strings */
	{																								/* OK - merge strings */
		CurBuf=GadBuf;
		ArgPtr=gadgets;																/* First gadgets string */
		while(CurArg=*ArgPtr++)												/* For each gadget string */
		{
			GadLen=strlen(CurArg);											/* Find length of gadget string */
			MemCopy(CurBuf,CurArg,GadLen);							/* Copy gadget string to buffer */
			CurBuf+=GadLen;															/* Advance buffer */
			*CurBuf++='|';															/* Add separator */
		}
		*(CurBuf-1)='\0';															/* Change last '|' into NULL */
	}

	return(GadBuf);
}	/* CreateGadgetString */
