/* mod_arexx.c
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * written by David N. Junod
 *
 * AREXX message handling routines
 *
 */

#include "mod.h"

/* variables required for ARexx */
struct ARexxInfo
{
    struct MsgPort *rxport;	/* ARexx message port */
    UBYTE *portname;		/* ARexx message port name */
    UBYTE *extens;		/* ARexx macro name extension */
    BOOL astatus;		/* ARexx status */

    /* user call-back functions */
    VOID (*ufunc[2])(struct AppInfo *, struct Message *, struct MsgHandler *);
};

/* User call-back function pointers */
#define ARX_MACRO_ERROR	0
#define ARX_MACRO_GOOD	1

/* ARexx message handling routines */
BOOL open_arexx (struct AppInfo *, struct MsgHandler * );
BOOL handle_arexx (struct AppInfo *, struct MsgHandler * );
BOOL close_arexx (struct AppInfo *, struct MsgHandler * );
BOOL shutdown_arexx (struct AppInfo *, struct MsgHandler * );

BOOL send_rexx_command (UBYTE *, struct AppInfo *, struct MsgHandler *);
VOID execute_command (struct RexxMsg *,struct AppInfo *,struct MsgHandler *);
VOID reply_rexx_command (struct RexxMsg *,struct AppInfo *,struct MsgHandler *);

/* ARexx Functions Prototypes */
struct RexxMsg *CreateRexxMsg (struct MsgPort *, UBYTE *, UBYTE *);
STRPTR CreateArgstring (UBYTE *, int);
VOID DeleteRexxMsg (struct RexxMsg *);
VOID DeleteArgstring (UBYTE *);
BOOL IsRexxMsg (struct Message *);

/* Required for ARexx processing */
struct RxsLib *RexxSysBase = NULL;

struct MsgHandler *setup_arexx (struct AppInfo * ai, UBYTE * pname,
				UBYTE * extens, BOOL immed)
{
    UBYTE *portname = "                        ";
    register WORD cntr = 1;
    BOOL exist = TRUE, retval = FALSE;
    struct MsgHandler *mh = NULL;
    struct ARexxInfo *md = NULL;

    if (md = (struct ARexxInfo *)
	AllocMem (sizeof (struct ARexxInfo), MEMF_CLEAR|MEMF_PUBLIC))
    {
	md->portname = pname;
	md->extens = extens;
	if (RexxSysBase = (struct RsxLib *)
	    OpenLibrary ("rexxsyslib.library", 0L))
	{

	    /* forbid multi-tasking while we try to get a port */
	    Forbid ();

	    /* get an unique port name */
	    while (exist)
	    {
		/* create a name with our base name and a number */
		sprintf (portname, "%s_%d", md->portname, cntr);

		/* see if someone has already taken this name */
		if (!FindPort (portname))
		    exist = FALSE;
		cntr++;
	    }

	    /* allocate the port */
	    if (md->rxport = CreatePort (portname, 0L))
		retval = TRUE;

	    /* permit multi-tasking again */
	    Permit ();

	    if (mh = (struct MsgHandler *)
		AllocMem (sizeof (struct MsgHandler), MEMF_CLEAR|MEMF_PUBLIC))
	    {
		mh->mh_Node.ln_Type = MH_HANDLER_T;
		mh->mh_Node.ln_Pri  = MH_HANDLER_P;
		mh->mh_Node.ln_Name = "AREXX";
		mh->mh_SigBits = (1L << md->rxport->mp_SigBit);
		mh->mh_Func[MH_OPEN]     = open_arexx;
		mh->mh_Func[MH_HANDLE]   = handle_arexx;
		mh->mh_Func[MH_CLOSE]    = close_arexx;
		mh->mh_Func[MH_SHUTDOWN] = shutdown_arexx;
		mh->mh_Data = md;

		/* open immediately? */
		if (immed)
		{
		    if (open_arexx (ai, mh))
			return (mh);
		}
		else
		    return (mh);

		FreeMem (mh, sizeof (struct MsgHandler));
	    }
	    else
		NotifyUser (NULL, "Not enough memory");

	    /* make a nice clean failure path */
	    DeletePort (md->rxport);
	    CloseLibrary ((struct Library *) RexxSysBase);
	    md->rxport = NULL;
	    RexxSysBase = NULL;
	}
	else
	    NotifyUser (NULL, "Could not open rexxsyslib.library");

	FreeMem (md, sizeof (struct ARexxInfo));
    }
    return (mh);
}

BOOL open_arexx (struct AppInfo * ai, struct MsgHandler * mh)
{
    ((struct ARexxInfo *)mh->mh_Data)->astatus = AR_EXECUTE;
    return (TRUE);
}

/* ARexx message processing */
BOOL handle_arexx (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct RexxMsg *rmsg;	/* incoming ARexx messages */
    struct ARexxInfo * md = mh->mh_Data;

    while (rmsg = (struct RexxMsg *) GetMsg (md->rxport))
    {
	if (rmsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
	{
	    /* This is a reply to a previous message */
	    if (rmsg->rm_Result1)
	    {
		/* returned an error message, perform user call-back */
		if (md->ufunc[ARX_MACRO_ERROR])
		    (*md->ufunc[ARX_MACRO_ERROR])
			(ai, (struct Message *) rmsg, mh);
	    }
	    else
	    {
		/* successful return, perform user call-back */
		if (md->ufunc[ARX_MACRO_GOOD])
		    (*md->ufunc[ARX_MACRO_GOOD])
			(ai, (struct Message *) rmsg, mh);
	    }
	    /* delete the argument that we originally sent */
	    DeleteArgstring (rmsg->rm_Args[0]);

	    /* delete the extended message */
	    DeleteRexxMsg (rmsg);

	    /* decrement the count of outstanding messages */
	    ai->numcmds--;
	}
	else
	    /* We have received a command/function message */
	    execute_command (rmsg, ai, mh);
    }
    return (TRUE);
}

BOOL close_arexx (struct AppInfo * ai, struct MsgHandler * mh)
{
    ((struct ARexxInfo *)mh->mh_Data)->astatus = AR_SUSPEND;
    return (TRUE);
}

BOOL shutdown_arexx (struct AppInfo * ai, struct MsgHandler * mh)
{
    struct ARexxInfo * md = mh->mh_Data;

    if (mh)
    {
	if (md->rxport)
	    DeletePort (md->rxport);

	Remove ((struct Node *) mh);
	FreeMem (mh, sizeof (struct MsgHandler));
    }
    if (RexxSysBase)
	CloseLibrary ((struct Library *) RexxSysBase);
    RexxSysBase = NULL;
    return (TRUE);
}

BOOL send_rexx_command (UBYTE * buff, struct AppInfo * ai,
			struct MsgHandler * mh)
{
    struct MsgPort *rexxport;	/* this will be rexx's port */
    struct RexxMsg *rexx_command_message;	/* this is the message */
    struct ARexxInfo * md = mh->mh_Data;

    /* lock things temporarily */
    Forbid ();

    /* if ARexx is not active, just return FALSE */
    if (!(rexxport = FindPort (RXSDIR)))
    {
	Permit ();
	return (FALSE);
    }

    /* allocate a message packet for our command */
    /* note that this is a very important call.  Much flexibility is */
    /* available to you here by using multiple host port names, etc. */
    if (!(rexx_command_message = CreateRexxMsg (md->rxport,
						md->extens,
						md->rxport->mp_Node.ln_Name)))
    {
	Permit ();
	return (FALSE);
    }

    /* create an argument string and install it in the message */
    if (!(rexx_command_message->rm_Args[0] =
	  CreateArgstring (buff, strlen (buff))))
    {
	DeleteRexxMsg (rexx_command_message);
	Permit ();
	return (FALSE);
    }

    /* indicate that message is a command */
    rexx_command_message->rm_Action = RXCOMM;

    /* send the message */
    PutMsg (rexxport, (struct Message *) rexx_command_message);

    /* increment the number of outstanding messages */
    ai->numcmds++;

    Permit ();
    return (TRUE);
}

VOID execute_command (struct RexxMsg * rmsg, struct AppInfo * ai,
		      struct MsgHandler * mh)
{
    struct ARexxInfo * md = mh->mh_Data;
    UBYTE *cmd = ARG0 (rmsg);

    /* preset the ARexx error return values */
    ai->pri_ret = 5L;
    ai->sec_ret = 0L;
    ai->textrtn = NULL;

    /* Don't perform function if we are suspended */
    if (md->astatus == AR_EXECUTE)
	PerfFunc (ai, (struct Message *) rmsg, cmd);
    else
	ai->pri_ret = 100L;

    /* reply to the message */
    reply_rexx_command (rmsg, ai, mh);
}

/* Replys to a ARexx message */
VOID reply_rexx_command (struct RexxMsg * rmsg, struct AppInfo * ai,
			 struct MsgHandler * mh)
{
    /* set an error code */
    if (ai->pri_ret == 0 && (rmsg->rm_Action & 1L << RXFB_RESULT))
    {
	ai->sec_ret = ai->textrtn ?
	(LONG) CreateArgstring (ai->textrtn, strlen (ai->textrtn))
	: (LONG) NULL;
    }
    rmsg->rm_Result1 = ai->pri_ret;
    rmsg->rm_Result2 = ai->sec_ret;
    ReplyMsg ((struct Message *) rmsg);
}
