/*
**	Rexx.c
**
**	Copyright (C) 1994,95,96,97 by Bernardo Innocenti
**
**	ARexx interface handling routines
*/

#include <exec/memory.h>
#include <rexx/rxslib.h>
#include <rexx/storage.h>
#include <rexx/errors.h>

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

#include "XModulePriv.h"
#include "Gui.h"



struct RexxCmd
{
	const STRPTR Cmd;
	const STRPTR Template;
	LONG (*Func)(struct RexxMsg *, LONG *);
	LONG Pad;	/* Makes each entry 16 bytes long for faster array access */
};


/* Local function prototypes */

static LONG ExecRexxCmd			(struct RexxMsg *msg, struct RexxCmd *cmd, const UBYTE *rexxargs);

static LONG RexxActivate		(struct RexxMsg *msg, LONG *args);
static LONG RexxClear			(struct RexxMsg *msg, LONG *args);
static LONG RexxClose			(struct RexxMsg *msg, LONG *args);
static LONG RexxColumn			(struct RexxMsg *msg, LONG *args);
static LONG RexxCopy			(struct RexxMsg *msg, LONG *args);
static LONG RexxCursor			(struct RexxMsg *msg, LONG *args);
static LONG RexxCut				(struct RexxMsg *msg, LONG *args);
static LONG RexxDeactivate		(struct RexxMsg *msg, LONG *args);
static LONG RexxErase			(struct RexxMsg *msg, LONG *args);
static LONG RexxGotoBookmark	(struct RexxMsg *msg, LONG *args);
static LONG RexxHelp			(struct RexxMsg *msg, LONG *args);
static LONG RexxIconify			(struct RexxMsg *msg, LONG *args);
static LONG RexxLine			(struct RexxMsg *msg, LONG *args);
static LONG RexxLockGui			(struct RexxMsg *msg, LONG *args);
static LONG RexxNew				(struct RexxMsg *msg, LONG *args);
static LONG RexxOpen			(struct RexxMsg *msg, LONG *args);
static LONG RexxOptimize		(struct RexxMsg *msg, LONG *args);
static LONG RexxPaste			(struct RexxMsg *msg, LONG *args);
static LONG RexxPrint			(struct RexxMsg *msg, LONG *args);
static LONG RexxQuit			(struct RexxMsg *msg, LONG *args);
static LONG RexxRequestFile		(struct RexxMsg *msg, LONG *args);
static LONG RexxRequestResponse	(struct RexxMsg *msg, LONG *args);
static LONG RexxRequestNotify	(struct RexxMsg *msg, LONG *args);
static LONG RexxSave			(struct RexxMsg *msg, LONG *args);
static LONG RexxSaveInstrument	(struct RexxMsg *msg, LONG *args);
static LONG RexxScreenToBack	(struct RexxMsg *msg, LONG *args);
static LONG RexxScreenToFront	(struct RexxMsg *msg, LONG *args);
static LONG RexxSelectInstrument(struct RexxMsg *msg, LONG *args);
static LONG RexxSetBookmark		(struct RexxMsg *msg, LONG *args);
static LONG RexxShowMessage		(struct RexxMsg *msg, LONG *args);
static LONG RexxUnLockGui		(struct RexxMsg *msg, LONG *args);
static LONG RexxVersion			(struct RexxMsg *msg, LONG *args);


static struct Library *RexxSysBase = NULL;

XDEF struct MsgPort *PubPort = NULL;
XDEF ULONG PubPortSig = 0L;
XDEF UBYTE PubPortName[16];	/* ARexx host name */


static struct RexxCmd RexxCmds[] =
{
	{ "ACTIVATE",		NULL,				RexxActivate },
	{ "CLEAR",			"FORCE/S",			RexxClear },
	{ "CLOSE",			"FORCE/S",			RexxClose },
	{ "COLUMN",			"/N/A",				RexxColumn },
	{ "COPY",			NULL,				RexxCopy },
	{ "CURSOR",			"UP/S,DOWN/S,LEFT/S,RIGHT/S",	RexxCursor },
	{ "CUT",			NULL,				RexxCut },
	{ "DEACTIVATE",		NULL,				RexxDeactivate },
	{ "ERASE",			"FORCE/S",			RexxErase },
	{ "GOTOBOOKMARK",	NULL,				RexxGotoBookmark },
	{ "HELP",			"COMMAND,PROMPT/S",	RexxHelp },
	{ "ICONIFY",		NULL,				RexxIconify },
	{ "LINE",			"/N/A",				RexxLine },
	{ "LOCKGUI",		NULL,				RexxLockGui },
	{ "NEW",			"PORTNAME/K",		RexxNew },
	{ "OPEN",			"FILENAME,FORCE/S",	RexxOpen },
	{ "OPTIMIZE",		NULL,				RexxOptimize },
	{ "PASTE",			NULL,				RexxPaste },
	{ "PRINT",			"PROMPT/S",			RexxPrint },
	{ "QUIT",			"FORCE/S",			RexxQuit },
	{ "REQUESTFILE",	"TITLE/K,PATH/K,FILE/K,PATTERN/K", RexxRequestFile },
	{ "REQUESTRESPONSE","TITLE/K,PROMPT/K", RexxRequestResponse },
	{ "REQUESTNOTIFY",	"PROMPT/K",			RexxRequestNotify },
	{ "SAVE",			"NAME,FORMAT",		RexxSave },
	{ "SAVEINSTRUMENT",	"NAME",				RexxSaveInstrument },
	{ "SCREENTOBACK",	NULL,				RexxScreenToBack },
	{ "SCREENTOFRONT",	NULL,				RexxScreenToFront },
	{ "SELECTINSTRUMENT","/N/A",			RexxSelectInstrument },
	{ "SETBOOKMARK",	NULL,				RexxSetBookmark },
	{ "SHOWMESSAGE",	"MSG/A",			RexxShowMessage },
	{ "UNLOCKGUI",		NULL,				RexxUnLockGui },
	{ "VERSION",		NULL,				RexxVersion }
};

#define COMMAND_CNT (sizeof (RexxCmds) / sizeof (struct RexxCmd))


GLOBALCALL void HandleRexxMsg (void)

/* Arexx command handler */
{
	struct RexxMsg *msg;
	LONG	 compare, high, low, mid, skip, cmdlen;
	STRPTR	 arg0;
	UBYTE	 cmd[32];

	while (msg = (struct RexxMsg *) GetMsg (PubPort))
	{
		if (IsRexxMsg (msg))
		{
			msg->rm_Result1 = RETURN_FAIL;

			/* Find command name length */
			arg0 = ARG0(msg);
			cmdlen = 0;

			while (*arg0 && (*arg0 != ' ') && (*arg0 != '\t')
				&& (*arg0 != '\n') && (cmdlen < 31))
			{
				cmd[cmdlen] = *arg0 & ~(1 << 5);	/* Fast toupper() */
				cmdlen++;
				arg0++;
			}
			cmd[cmdlen] = '\0';

			skip = 0;
			low = 0;
			high = COMMAND_CNT - 1;

			/* Perform an optimized binary serch to find the ARexx
			 * command in the ARexx commands array.
			 */
			do
			{
				/* Search optimization. Skip first characters until they
				 * are different.
				 * We must test low != high because otherwise this
				 * loop would go past the end of the string, as the
				 * two strings we compare are identical).
				 */
				if (low != high)
					while ((RexxCmds[low].Cmd[skip] == RexxCmds[high].Cmd[skip])
						&& (RexxCmds[low].Cmd[skip] == cmd[skip]))
						skip++;

				mid = (low + high) >> 1;
				compare = strcmp (RexxCmds[mid].Cmd + skip, cmd + skip);

				if (compare > 0)
					high = mid - 1;
				else if (compare < 0)
					low = mid + 1;
				else
				{
					msg->rm_Result1 = ExecRexxCmd (msg, &RexxCmds[mid],
						ARG0(msg) + cmdlen);
					break; /* Exit from loop */
				}
			}
			while (low <= high);
		}

		ReplyMsg ((struct Message *)msg);
	}
}



static LONG ExecRexxCmd (struct RexxMsg *msg, struct RexxCmd *cmd, const UBYTE *rexxargs)
{
	struct RDArgs *rdargs;
	UBYTE *argbuf;
	ULONG arglen = strlen (rexxargs) + 1;	/* Space for newline */
	LONG rc = RC_ERROR;
	LONG argarray[6] = { 0L };			/* Max 6 arguments allowed! */

	ShowRequesters = FALSE;

	if (!cmd->Template)
		rc = (cmd->Func)(msg, NULL);	/* Call command directly */
	else if (argbuf = AllocVec (arglen, MEMF_ANY))
	{
		/* Copy arguments to temporary buffer.
		 * ReadArgs() also requires a newline.
		 */
		strcpy (argbuf, rexxargs);
		argbuf[arglen-1] = '\n';

		if (rdargs = AllocDosObject (DOS_RDARGS, NULL))
		{
			rdargs->RDA_Source.CS_Buffer = argbuf;
			rdargs->RDA_Source.CS_Length = arglen;
			rdargs->RDA_Flags |= RDAF_NOPROMPT;

			if (ReadArgs ((volatile STRPTR)cmd->Template, argarray, rdargs))
			{
				/* Call Command server */
				rc = (cmd->Func)(msg, argarray);

				FreeArgs (rdargs);
			}
			FreeDosObject (DOS_RDARGS, rdargs);
		}
		FreeVec (argbuf);
	}

	ShowRequesters = TRUE;

	return rc;
}



GLOBALCALL LONG CreateRexxPort (void)

/* Setup public port for ARexx host */
{
	ULONG i = 0;

	if (!PubPortName[0]) return RETURN_FAIL;

	if (!(RexxSysBase = OpenLibrary (RXSNAME, 36L)))
		return RETURN_FAIL;

	if (!(PubPort = CreateMsgPort ())) return ERROR_NO_FREE_STORE;

	PubPortSig = 1 << PubPort->mp_SigBit;
	Signals |= PubPortSig;

	Forbid();
	while (FindPort (PubPortName))
		SPrintf (PubPortName, "XMODULE.%ld", ++i);

	PubPort->mp_Node.ln_Name = PubPortName;
	PubPort->mp_Node.ln_Pri = 1;
	AddPort (PubPort);
	Permit();

	return RETURN_OK;
}


GLOBALCALL void DeleteRexxPort (void)
{
	if (PubPort)
	{
		RemPort (PubPort);
		KillMsgPort (PubPort); PubPort = NULL;
	}

	if (RexxSysBase)
		{ CloseLibrary (RexxSysBase); RexxSysBase = NULL; }
}

/************************/
/* Rexx Command servers */
/************************/

static LONG RexxActivate		(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxClear			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxClose			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxColumn			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxCopy			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxCursor			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxCut				(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}

static LONG RexxDeactivate		(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxErase			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxGotoBookmark	(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxHelp			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxIconify			(struct RexxMsg *msg, LONG *args)
{
	Iconify();
	return RETURN_FAIL;
}



static LONG RexxLine			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxLockGui		(struct RexxMsg *msg, LONG *args)
{
	LockWindows();

	return RETURN_OK;
}



static LONG RexxNew				(struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	if (si = xmCreateSong (
		SNGA_ReadyToUse,	TRUE,
		XMSNG_AddToList,	-1,
		XMSNG_Active,		TRUE,
		TAG_DONE))
	{
		ReleaseSemaphore (&si->Lock);
		return RETURN_OK;
	}

	return RETURN_FAIL;
}



static LONG RexxOpen (struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	LockWindows();

	if (args[0])
	{
		if (si = xmLoadModule ((STRPTR)args[0],
			XMSNG_OldSong,		NULL,
			XMSNG_AddToList,	TRUE,
			XMSNG_Active,		TRUE,
			TAG_DONE))
			ReleaseSemaphore (&si->Lock);
	}
	else
	{
		UBYTE filename[PATHNAME_MAX];

		filename[0] = '\0';

		if (FileRequest (FREQ_LOADMOD, filename))
		{
			if (si = xmLoadModule (filename,
				XMSNG_OldSong,		NULL,
				XMSNG_AddToList,	TRUE,
				XMSNG_Active,		TRUE,
				TAG_DONE))
				ReleaseSemaphore (&si->Lock);
		}
	}

	UnlockWindows();

	return LastErr;
}



static LONG RexxOptimize (struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		xmProcessSong (si, NULL,
			XMSNG_Optimize,	XMOF_DEFAULT,
			TAG_DONE);

		UpdateSongInfo();

		ReleaseSemaphore (&si->Lock);
	}

	return RETURN_OK;
}



static LONG RexxPaste			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxPrint			(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxQuit (struct RexxMsg *msg, LONG *args)
{
	Quit = 1;
	if (args[0]) GuiSwitches.AskExit = FALSE;

	return RETURN_OK;
}



static LONG RexxRequestFile		(struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxRequestResponse	(struct RexxMsg *msg, LONG *args)
{
	return ShowRequestStr ((STRPTR)args[0], (STRPTR)args[1], NULL);
}



static LONG RexxRequestNotify	(struct RexxMsg *msg, LONG *args)
{
	ShowRequestStr ((STRPTR)args[0], NULL, NULL);
	return RETURN_OK;
}



static LONG RexxSave (struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_SHARED))
	{
		if (args[0])
			SetAttrs (si,
				SNGA_Path, (STRPTR)args[0],
				TAG_DONE);

		if (args[1])
		{
			struct XMHook *saver;

			/* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */

			/* Try to get the shared semaphore */
			if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock))
				/* Check if we can get the exclusive version */
				if (!AttemptSemaphore (&XModuleBase->xm_BaseLock))
					/* Oh well, wait for the shared lock */
					ObtainSemaphoreShared (&XModuleBase->xm_BaseLock);

			if (saver = (struct XMHook *)FindName ((struct List *)&XModuleBase->xm_Loaders, (STRPTR)args[1]))
				LastErr = xmSaveModuleA (si, si->Path, saver, NULL);
			else
				LastErr = ERROR_OBJECT_NOT_FOUND;

			ReleaseSemaphore (&XModuleBase->xm_BaseLock);
		}
		else
			LastErr = xmSaveModuleA (si, si->Path, NULL, NULL);

		ReleaseSemaphore (&si->Lock);
	}

	return LastErr;
}



static LONG RexxSaveInstrument (struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_SHARED))
	{
		struct Instrument *instr;

		if (instr = si->Instr[si->CurrentInst])
		{
			if (args[0])
				LastErr = SaveInstrument (instr, (STRPTR)args[0]);
			else
				LastErr = SaveInstrument (instr, instr->Name);
		}
		else LastErr = ERROR_OBJECT_NOT_FOUND;

		ReleaseSemaphore (&si->Lock);
	}

	return LastErr;
}



static LONG RexxSelectInstrument (struct RexxMsg *msg, LONG *args)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_SHARED))
	{
		if (*((ULONG *)args[0]) >= MAXINSTRUMENTS)
			return RETURN_FAIL;

		si->CurrentInst = *((ULONG *)args[0]);

		ReleaseSemaphore (&si->Lock);

		UpdateInstrInfo();
	}

	return RETURN_OK;
}



static LONG RexxScreenToBack (struct RexxMsg *msg, LONG *args)
{
	if (Scr) ScreenToBack (Scr);

	return RETURN_OK;
}



static LONG RexxScreenToFront (struct RexxMsg *msg, LONG *args)
{
	if (Scr)
	{
		ScreenToFront (Scr);
		if (ThisTask->pr_WindowPtr) ActivateWindow (ThisTask->pr_WindowPtr);
	}

	return RETURN_OK;
}



static LONG RexxSetBookmark (struct RexxMsg *msg, LONG *args)
{
	return RETURN_FAIL;
}



static LONG RexxShowMessage (struct RexxMsg *msg, LONG *args)
{
	ShowString ((STRPTR)args[0], NULL);

	return RETURN_OK;
}



static LONG RexxUnLockGui (struct RexxMsg *msg, LONG *args)
{
	UnlockWindows();
	return RETURN_OK;
}



static LONG RexxVersion (struct RexxMsg *msg, LONG *args)
{
	UBYTE RexxVer[8];

	SPrintf (RexxVer, "%ld.%ld", VERSION, REVISION);

//	SetRexxVar ((struct Message *)msg, "RESULT", RexxVer, strlen (RexxVer));
	msg->rm_Result2=(LONG)CreateArgstring (RexxVer, (LONG)strlen(RexxVer));

	return RETURN_OK;
}

