/*
**	rxcontrol.c - control ARexx features
**	Copyright © 1994-95 Michael Letowski
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <dos/rdargs.h>
#include <rexx/rxslib.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>

#include <support/types.h>
#include <support/dos.h>

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

#include "rxcontrol.rev.h"

#define DOS_NAME					"dos.library"
#define DOS_VERN					37L
#define REXX_NAME					"rexxsyslib.library"
#define REXX_VERN					36L
#define INT_NAME					"intuition.library"
#define INT_VERN					37L
#define ICON_NAME					"icon.library"
#define ICON_VERN					37L

#define RXPORT_NAME				"REXX"

#define TEMPLATE					"RXC=CANCEL/S,HI=HALT/S,SUSPEND/S,RESUME/S,"\
													"TS=TRACESTART/S,TE=TRACEEND/S,"\
													"TCO=CONOPEN/S,TCC=CONCLOSE/S,"\
													"QUIET/S"

/* Options array indices (for WB arguments reading) */
#define OPT_RXC					0
#define OPT_HI					1
#define OPT_SUSPEND			2
#define OPT_RESUME			3
#define OPT_TS					4
#define OPT_TE					5
#define OPT_TCO					6
#define OPT_TCC					7
#define OPT_QUIET				8
#define OPT_COUNT				9

/* Messages */
#define MSG_UNABLE_OPEN		"Unable to open '%s'\n"
#define MSG_NOT_ACTIVE		"ARexx server not active\n"
#define MSG_IS_SUSPENDED	"Execution is suspended\n"
#define MSG_WILL_CLOSE		"RexxMaster will close\n"
#define MSG_STATUS				"ARexx state:\n"\
													"%sTracing is %s\n"\
													"%sTrace console is %s\n%s%s%s%s"
/* Other texts */
#define MSG_OK						"OK"
#define MSG_ON						"ON"
#define MSG_OFF						"OFF"
#define MSG_OPEN					"open"
#define MSG_CLOSED				"closed"
#define REQUEST_MSG				" Request"
#define INFO_MSG					" Information"
#define SPC_TAB						"  "

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

struct Options
{
	LONG opt_RXC;
	LONG opt_HI;
	LONG opt_Suspend;
	LONG opt_Resume;
	LONG opt_TS;
	LONG opt_TE;
	LONG opt_TCO;
	LONG opt_TCC;
	LONG opt_Quiet;
};	/* Options */

struct States
{
	STRPTR state_Tab1;	STRPTR state_Tracing;
	STRPTR state_Tab2;	STRPTR state_Console;
	STRPTR state_Tab3;	STRPTR state_Suspended;
	STRPTR state_Tab4;	STRPTR state_WillClose;
};	/* States */

struct Switch
{
	STRPTR sw_Name;
	ULONG sw_Index;
};	/* Switch */

STATIC LONG CLIMode(struct ExecBase *SysBase, struct DosLibrary *DOSBase);
STATIC LONG WBMode(struct ExecBase *SysBase, struct DosLibrary *DOSBase);
STATIC LONG DoOptions(struct MsgPort *port, struct Options *opts,
											struct ExecBase *SysBase, struct DosLibrary *DOSBase,
											struct RxsLib *RexxSysBase);
STATIC VOID GetOptions(struct States *stat, STRPTR tab, struct RxsLib *rsb);

LONG RXControl(VOID)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct DosLibrary *DOSBase;

	LONG RC=RETURN_FAIL;

	/* Open DOS */
	unless(DOSBase=(struct DosLibrary *)OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_DOS);

	RC=FromWB ? WBMode(SysBase,DOSBase) : CLIMode(SysBase,DOSBase);

	/* Exceptions */
	catch(NO_DOS,	CloseLibrary((struct Library *)DOSBase));
	return(RC);
}	/* RXControl */

/* CLI mode work */
STATIC LONG CLIMode(struct ExecBase *SysBase, struct DosLibrary *DOSBase)
{
	struct RxsLib *RexxSysBase;

	struct Options Opts;
	struct States Stat;
	struct RDArgs *Args;
	struct MsgPort *Port;
	LONG RC=RETURN_FAIL;

	/* Open rexxsyslib.library */
	unless(RexxSysBase=(struct RxsLib *)OpenLibrary(REXX_NAME,REXX_VERN))
		throw2(CauseIoErr(ERROR_INVALID_RESIDENT_LIBRARY,NULL),	NO_REXX);

	/* Read arguments */
	clear(&Opts);																	/* Clear options buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),NULL),	NO_ARGS);
	/* Check args */
	if(Opts.opt_Suspend && Opts.opt_Resume ||
			Opts.opt_TS && Opts.opt_TE || Opts.opt_TCO && Opts.opt_TCC)
		throw2(CauseIoErr(ERROR_TOO_MANY_ARGS,PROG_NAME),	BAD_ARGS);

	/* Check for REXX port */
	Forbid();
	unless(Port=FindPort(RXPORT_NAME))
	{
		Permit();
		PutStr(MSG_NOT_ACTIVE);
		throw(NOT_ACTIVE);
	}
	RC=DoOptions(Port,&Opts,SysBase,DOSBase,RexxSysBase);
	Permit();

	GetOptions(&Stat,"\t",RexxSysBase);
	unless(Opts.opt_Quiet)
		VPrintf(MSG_STATUS,&Stat);

	/* Exceptions */
	catch(NOT_ACTIVE,	);
	catch(BAD_ARGS,		);
	catch(NO_ARGS,		FreeArgs(Args));
	catch(NO_REXX,		CloseLibrary((struct Library *)RexxSysBase));
	return(RC);
}	/* CLIMode */

/* Workbench mode stuff */
STATIC LONG WBMode(struct ExecBase *SysBase, struct DosLibrary *DOSBase)
{
	struct IntuitionBase *IntuitionBase;
	struct RxsLib *RexxSysBase;
	struct Library *IconBase;

	STATIC CONST struct EasyStruct NoLibReq=			/* No library requster */
	{
		sizeof(struct EasyStruct),0,
		PROJ_NAME REQUEST_MSG,
		MSG_UNABLE_OPEN,
		MSG_OK
	};	/* NoLibReq */
	STATIC CONST struct EasyStruct NotActiveReq=	/* No REXX requester definition */
	{
		sizeof(struct EasyStruct),0,
		PROJ_NAME REQUEST_MSG,
		MSG_NOT_ACTIVE,
		MSG_OK
	};	/* NotActiveReq */
	STATIC CONST struct EasyStruct InfoReq=				/* Informations requester */
	{
		sizeof(struct EasyStruct),0,
		PROJ_NAME INFO_MSG,
		MSG_STATUS,
		MSG_OK
	};	/* InfoReq */
	STATIC CONST struct Switch Switches[]=				/* ToolTypes switches */
	{
		{"CANCEL",			OPT_RXC},			{"RXC",	OPT_RXC},
		{"HALT",				OPT_HI},			{"HI",	OPT_HI},
		{"SUSPEND",			OPT_SUSPEND},
		{"RESUME",			OPT_RESUME},
		{"TRACESTART",	OPT_TS},			{"TS",	OPT_TS},
		{"TRACEEND",		OPT_TE},			{"TE",	OPT_TE},
		{"CONOPEN",			OPT_TCO},			{"TCO",	OPT_TCO},
		{"CONCLOSE",		OPT_TCC},			{"TCC",	OPT_TCC},
		{"QUIET",				OPT_QUIET}
	};	/* Switches */

	struct Options Opts;
	struct States Stat;
	struct MsgPort *Port;
	struct WBStartup *WBMsg;
	struct WBArg *Args;
	struct DiskObject *Object;
	STRPTR *ToolArray;
	BPTR OldDir;
	ULONG I,J;
	LONG RC=RETURN_FAIL;

	WaitPort(&ThisProcessS->pr_MsgPort);
	WBMsg=(struct WBStartup *)GetMsg(&ThisProcessS->pr_MsgPort);

	/* Open libraries */
	unless(IntuitionBase=(struct IntuitionBase *)OpenLibrary(INT_NAME,INT_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_INT);
	unless(IconBase=OpenLibrary(ICON_NAME,ICON_VERN))
		throw2(EasyRequest(NULL,&NoLibReq,NULL,ICON_NAME),	NO_ICON);
	unless(RexxSysBase=(struct RxsLib *)OpenLibrary(REXX_NAME,REXX_VERN))
		throw2(EasyRequest(NULL,&NoLibReq,NULL,REXX_NAME),	NO_REXX);

	/* Read arguments */
	clear(&Opts);																	/* Clear options buffer */
	for(I=0, Args=WBMsg->sm_ArgList; I<WBMsg->sm_NumArgs; I++, Args++)
		if(Args->wa_Lock)														/* Lock exists */
		{
			OldDir=CurrentDir(Args->wa_Lock);					/* CD to icon directory */
			if(Args->wa_Name && (Object=GetDiskObject(Args->wa_Name)))
			{
				ToolArray=(STRPTR *)Object->do_ToolTypes;
				for(J=0; J<elems(Switches); J++)
					if(FindToolType(ToolArray,Switches[J].sw_Name))
						((LONG *)&Opts)[Switches[J].sw_Index]=TRUE;
				FreeDiskObject(Object);
			}
			CurrentDir(OldDir);												/* CD back */
		}
	/* Check args */
	if(Opts.opt_Suspend && Opts.opt_Resume ||
			Opts.opt_TS && Opts.opt_TE || Opts.opt_TCO && Opts.opt_TCC)
		throw2(SetIoErr(ERROR_TOO_MANY_ARGS),	BAD_ARGS);

	/* Check for REXX port */
	Forbid();																			/* Make sure a port doesn't disappear */
	unless(Port=FindPort(RXPORT_NAME))
	{
		Permit();
		EasyRequestArgs(NULL,&NotActiveReq,NULL,NULL);
		throw(NOT_ACTIVE);
	}
	RC=DoOptions(Port,&Opts,SysBase,DOSBase,RexxSysBase);
	Permit();																			/* We are done */

	GetOptions(&Stat,SPC_TAB,RexxSysBase);
	unless(Opts.opt_Quiet)
		EasyRequestArgs(NULL,&InfoReq,NULL,&Stat);

	/* Exceptions */
	catch(NOT_ACTIVE,	);
	catch(BAD_ARGS,		);
	catch(NO_REXX,		CloseLibrary((struct Library *)RexxSysBase));
	catch(NO_ICON,		CloseLibrary(IconBase));
	catch(NO_INT,			CloseLibrary((struct Library *)IntuitionBase));
	ReplyMsg((struct Message *)WBMsg);						/* Reply to Workbench message */
	return(RC);
}	/* WBMode */

STATIC LONG DoOptions(struct MsgPort *port, struct Options *opts,
											struct ExecBase *SysBase, struct DosLibrary *DOSBase,
											struct RxsLib *RexxSysBase)
{
	struct RexxMsg *RXMsg;
	LONG RC=RETURN_OK;														/* Everything looks OK so far */

	/* RXC */
	if(opts->opt_RXC)															/* Shut down REXX */
		if(RXMsg=CreateRexxMsg(NULL,NULL,NULL))			/* Create message */
		{
			RXMsg->rm_Action=RXCLOSE | RXFF_NONRET;		/* Set action */
			PutMsg(port,(struct Message *)RXMsg);			/* Send message */
		}
		else																				/* No message */
		{
			SetIoErr(ERROR_NO_FREE_STORE);						/* Set error code */
			RC=RETURN_ERROR;													/* And mark it */
		}

	/* HI */
	if(opts->opt_HI)															/* Interrupt tasks */
	{
		bset(RexxSysBase->rl_Flags,RLFB_HALT);			/* Set interrupt request */
		Signal(port->mp_SigTask,flag(port->mp_SigBit));
	}

	/* Suspend/Resume */
	if(opts->opt_Suspend)													/* Suspension requested */
	{
		bset(RexxSysBase->rl_Flags,RLFB_SUSP);			/* Set suspension request */
		Signal(port->mp_SigTask,flag(port->mp_SigBit));
	}
	if(opts->opt_Resume)													/* Resumption requested */
	{
		bclr(RexxSysBase->rl_Flags,RLFB_SUSP);			/* Set reumption request */
		Signal(port->mp_SigTask,flag(port->mp_SigBit));
	}

	/* TE/TS */																		/* Tracing enable/disable */
	if(opts->opt_TE)		bclr(RexxSysBase->rl_Flags,RLFB_TRACE);
	if(opts->opt_TS)		bset(RexxSysBase->rl_Flags,RLFB_TRACE);

	/* TCO/TCC */
	if(opts->opt_TCO)															/* Open console */
		if(RXMsg=CreateRexxMsg(NULL,NULL,NULL))			/* Create message */
		{
			RXMsg->rm_Action=RXTCOPN | RXFF_NONRET;		/* Set open message */
			PutMsg(port,(struct Message *)RXMsg);			/* Send message */
		}
		else																				/* No message */
		{
			SetIoErr(ERROR_NO_FREE_STORE);						/* Set error code */
			RC=RETURN_ERROR;													/* Mark error */
		}
	if(opts->opt_TCC)															/* Close console */
		if(RXMsg=CreateRexxMsg(NULL,NULL,NULL))			/* Create message */
		{
			RXMsg->rm_Action=RXTCCLS | RXFF_NONRET;		/* Set open message */
			PutMsg(port,(struct Message *)RXMsg);			/* Send message */
		}
		else																				/* No message */
		{
			SetIoErr(ERROR_NO_FREE_STORE);						/* Set error code */
			RC=RETURN_ERROR;													/* Mark error */
		}

	return(RC);
}	/* DoOptions */

STATIC VOID GetOptions(struct States *stat, STRPTR tab, struct RxsLib *rsb)
{
	clear(stat);																	/* Set defaults */
	stat->state_Tab1=stat->state_Tab2=tab;
	stat->state_Tracing=btst(rsb->rl_Flags,RLFB_TRACE) ? MSG_ON : MSG_OFF;
	stat->state_Console=rsb->rl_TraceFH ? MSG_OPEN : MSG_CLOSED;
	if(btst(rsb->rl_Flags,RLFB_SUSP))
	{
		stat->state_Suspended=MSG_IS_SUSPENDED;
		stat->state_Tab3=tab;
	}
	if(btst(rsb->rl_Flags,RLFB_CLOSE))
	{
		stat->state_WillClose=MSG_WILL_CLOSE;
		stat->state_Tab4=tab;
	}
}	/* GetOptions */
