/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */

/*
 *  PLACE.C
 *
 *  PlaceRexxCommand()
 *
 *  Send an AREXX command according to arguments and RexxHostName
 */

#include "rexx/rexx.h"

void ClearDiceRexxPort(MsgPort *);
void RexxReply(void *, long, char *);

static MsgPort RexxPort;		    /*	master port	*/
static char    *RexxPortName = "REXX";      /*  master port name    */

static short	GlobalFlag;
static short	MasterPortValid;

static List	RexxWIPList;
static List	RexxRIPList;
static List	RexxAuxPortList;
static short	RexxPortInitialized;

short	RexxSigBit;	/*  master signal   */

__autoinit
static void
__dice_rexx_init()
{
    /*
     *	initialize the list that tracks rexx requests sent
     */

    RexxWIPList.lh_Head = (Node *)&RexxWIPList.lh_Tail;
    RexxWIPList.lh_TailPred = (Node *)&RexxWIPList.lh_Head;

    /*
     *	initialize the list that tracks rexx requests received
     */

    RexxRIPList.lh_Head = (Node *)&RexxRIPList.lh_Tail;
    RexxRIPList.lh_TailPred = (Node *)&RexxRIPList.lh_Head;

    /*
     *	initialize the auxillary port list and add our master node to
     *	it.
     */

    RexxAuxPortList.lh_Head = (Node *)&RexxAuxPortList.lh_Tail;
    RexxAuxPortList.lh_TailPred = (Node *)&RexxAuxPortList.lh_Head;

    /*
     *	Get our master signal and create master port
     */

    RexxSigBit = AllocSignal(-1);

    if (RexxHostName)
	CreateDiceRexxPort(NULL, RexxHostName);

    RexxPortInitialized = 1;
}

/*
 *  The exit code can be called at any point... there might be multiple
 *  messages queued, in the queue, or in processing.  We must do the
 *  following items in order:
 *
 *	(1) prevent any further messages from being received
 *	(2) return any messages queued-in or in processing
 *	(3) wait for any outgoing messages to be returned
 */

__autoexit
static void
__dice_rexx_exit()
{
    if (RexxPortInitialized) {
	RexxPortNode *rp;

	/*
	 *  prevent further messages from being received
	 */

	for (rp = (RexxPortNode *)RexxAuxPortList.lh_Head; rp->rp_Node.ln_Succ; rp = (RexxPortNode *)rp->rp_Node.ln_Succ) {
	    if (rp->rp_IsPublic) {
		RemPort(rp->rp_MsgPort);
		rp->rp_IsPublic = 0;
	    }
	}

	/*
	 *  return any messages in processing or queued-in
	 */

	for (rp = (RexxPortNode *)RexxAuxPortList.lh_Head; rp->rp_Node.ln_Succ; rp = (RexxPortNode *)rp->rp_Node.ln_Succ)
	    ClearDiceRexxPort(rp->rp_MsgPort);

	/*
	 *  wait for replies from rexx commands we have sent.  Since we
	 *  have cleared all other messages, no lockout conditions can
	 *  occur at this point.
	 */

	{
	    RexxIPNode *rip;

	    while (rip = (RexxIPNode *)RemHead(&RexxWIPList)) {
		if (rip->rip_Returned == 0) {
		    while (((Message *)rip->rip_RexxMsg)->mn_Node.ln_Type != NT_REPLYMSG)
			Wait(1 << RexxSigBit);
		    Forbid();
		    Remove(&rip->rip_RexxMsg->rm_Node.mn_Node);
		    Permit();
		    rip->rip_Returned = 1;
		}
		ClearRexxMsg(rip->rip_RexxMsg, 1);
		DeleteRexxMsg(rip->rip_RexxMsg);
	    }
	}

	/*
	 *  Delete the ports.  Since we have cleared rp_IsPublic it will
	 *  not try to RemPort() them again.
	 */

	while ((rp = (RexxPortNode *)RexxAuxPortList.lh_Head) != (RexxPortNode *)&RexxAuxPortList.lh_Tail)
	    DeleteDiceRexxPort(rp->rp_MsgPort);

	/*
	 *  Free our master signal
	 */

	FreeSignal(RexxSigBit);
	RexxPortInitialized = 0;
    }
}

long
PlaceRexxCommandDirect(port, remoteName, arg, pres, pec)
MsgPort *port;
char *remoteName;
char *arg;
char **pres;
long *pec;
{
    char *rpn = RexxPortName;
    long r;

    RexxPortName = remoteName;
    r = PlaceRexxCommand(port, arg, pres, pec);
    RexxPortName = rpn;
    return(r);
}

long
PlaceRexxCommand(port, arg, pres, pec)
MsgPort *port;
char *arg;
char **pres;
long *pec;
{
    RexxIPNode rip;
    long rc = -2;

    if (port == NULL) {
	if (MasterPortValid == 0)
	    return(-1);
	port = &RexxPort;
    }

    if (pres)
	*pres = NULL;
    if (pec)
	*pec = 0;


    if (RexxSysBase) {
	rc = -1;

	if (rip.rip_RexxMsg = CreateRexxMsg(port, ((RexxHostName) ? RexxHostName : "UNKNOWN"), port->mp_Node.ln_Name)) {
	    MsgPort *arexxPort;

	    rip.rip_RexxMsg->rm_Node.mn_Node.ln_Name = "REXX";
	    rip.rip_RexxMsg->rm_Action = RXCOMM;
	    if (pres) rip.rip_RexxMsg->rm_Action |= RXFF_RESULT;
	    ARG0(rip.rip_RexxMsg) = CreateArgstring(arg,strlen(arg));

	    AddHead(&RexxWIPList, &rip.rip_Node);
	    rip.rip_Returned = 0;
	    rip.rip_RexxPort = port;

	    Forbid();
	    if (arexxPort = (MsgPort *)FindPort(RexxPortName)) {
		PutMsg(arexxPort, (Message *)rip.rip_RexxMsg);
		Permit();

		while (rip.rip_Returned == 0) {
		    RexxPortNode *rp;

		    /*
		     *	Since this is the only wait on this signal there
		     *	can be no lockout condition.
		     */

		    Wait(1 << RexxSigBit);
		    for (rp = (RexxPortNode *)RexxAuxPortList.lh_Head; rp->rp_Node.ln_Succ; rp = (RexxPortNode *)(rp->rp_Node.ln_Succ))
			ProcessRexxCommands(rp->rp_MsgPort);
		}
	    } else {
		Permit();
	    }
	    Remove(&rip.rip_Node);

	    /*
	     *	Process result / return code here if rip.rip_Returned == 0
	     *	then we were not able to dispatch the message
	     */

	    if (rip.rip_Returned) {
		if ((rc = rip.rip_RexxMsg->rm_Result1) == 0) {
		    if (rip.rip_RexxMsg->rm_Result2) {
			if (pres)
			    *pres = strdup((char *)rip.rip_RexxMsg->rm_Result2);
			DeleteArgstring((UBYTE *)rip.rip_RexxMsg->rm_Result2);
		    }
		} else {
		    if (pec)
			*pec = (long)rip.rip_RexxMsg->rm_Result2;
		}
	    }
	    ClearRexxMsg(rip.rip_RexxMsg, 1);
	    DeleteRexxMsg(rip.rip_RexxMsg);
	}
    }
    return(rc);
}

void
ProcessRexxCommands(port)
MsgPort *port;
{
    RexxMsg *msg;

    if (port == NULL) {
	RexxPortNode *rp;

	for (rp = (RexxPortNode *)RexxAuxPortList.lh_Head; rp->rp_Node.ln_Succ; rp = (RexxPortNode *)(rp->rp_Node.ln_Succ))
	    ProcessRexxCommands(rp->rp_MsgPort);
	return;
    }

    while (msg = (struct RexxMsg *)GetMsg(port)) {
	if ((msg->rm_Node.mn_Node.ln_Type == NT_MESSAGE) && IsRexxMsg(msg)) {
	    RexxIPNode rip;
	    char *str = NULL;
	    long rc;

	    rip.rip_RexxMsg = msg;
	    rip.rip_RexxPort = port;
	    AddTail(&RexxRIPList, &rip.rip_Node);
	    rc = DoRexxCommand(msg, port, ARG0(msg), &str);
	    Remove(&rip.rip_Node);
	    RexxReply(msg, rc, str);
	} else {
	    RexxIPNode *rip;

	    for (rip = (RexxIPNode *)RexxWIPList.lh_Head; rip->rip_Node.ln_Succ; rip = (RexxIPNode *)rip->rip_Node.ln_Succ) {
		if (msg == rip->rip_RexxMsg) {
		    rip->rip_Returned = 1;
		    break;
		}
	    }
	}
    }
}

/*
 *  Create a global DICE rexx port
 */

short
CreateGlobalDiceRexxPort(port, name)
MsgPort *port;
char *name;
{
    short r;

    GlobalFlag = 1;
    r = CreateDiceRexxPort(port, name);
    GlobalFlag = 0;

    return(r);
}

/*
 *  Create an application DICE rexx port
 */

short
CreateDiceRexxPort(port, name)
MsgPort *port;
char *name;
{
    RexxPortNode *rp;
    short pno;
    short isPublic;
    short allocSize;

    if (port == NULL) {
	port = &RexxPort;
	MasterPortValid = 1;
    }

    if (name == NULL) {
	name = "PRIVATE-REXX-PORT";
	isPublic = 0;
    } else {
	isPublic = 1;
    }
    allocSize = sizeof(RexxPortNode) + strlen(name) + 4;

    rp = AllocMem(allocSize, MEMF_PUBLIC|MEMF_CLEAR);

    rp->rp_Node.ln_Type = allocSize;
    rp->rp_MsgPort = port;
    rp->rp_IsPublic = isPublic;
    AddTail(&RexxAuxPortList, &rp->rp_Node);

    port->mp_Node.ln_Name = (char *)(rp + 1);

    port->mp_Node.ln_Type = NT_MSGPORT;
    port->mp_Node.ln_Pri = 1;
    port->mp_Flags = PA_SIGNAL;
    port->mp_SigBit = RexxSigBit;
    port->mp_SigTask = FindTask(NULL);
    port->mp_MsgList.lh_Head = (Node *)&port->mp_MsgList.lh_Tail;
    port->mp_MsgList.lh_TailPred = (Node *)&port->mp_MsgList.lh_Head;

    /*
     *	Give the port a real name according to AREXX semantics,
     *	portname.NN (01-99)
     */

    Forbid();

    if (GlobalFlag || isPublic == 0) {
	strcpy(port->mp_Node.ln_Name, name);
	if (isPublic && FindPort(port->mp_Node.ln_Name))
	    pno = -1;
	else
	    pno = 0;
    } else {
	for (pno = 1; pno < 100; ++pno) {
	    char buf[4];
	    strcpy(buf, ".00");
	    buf[1] += pno / 10;
	    buf[2] += pno % 10;
	    strcpy(port->mp_Node.ln_Name, name);
	    strcat(port->mp_Node.ln_Name, buf);
/*	    sprintf(port->mp_Node.ln_Name, "%s.%02d", name, pno); */
	    if (FindPort(port->mp_Node.ln_Name) == NULL)
		break;
	}
	if (pno == 100)
	    pno = -1;
    }

    if (isPublic)
	AddPort(port);

    if (pno < 0)
	DeleteDiceRexxPort(port);
    else if (port == (MsgPort *)&RexxPort && RexxHostName == NULL)
	RexxHostName = port->mp_Node.ln_Name;

    Permit();

    return(pno);
}

/*
 *  Delete a DICE rexx port.  Note that there can be no outgoing messages
 *  in-progress for this port for this command to work properly.
 */

void
DeleteDiceRexxPort(port)
MsgPort *port;
{
    /*
     *	check if valid port, remove RexxPortNode if found
     */

    {
	RexxPortNode *rp;

	for (rp = (RexxPortNode *)RexxAuxPortList.lh_Head; rp->rp_Node.ln_Succ; rp = (RexxPortNode *)rp->rp_Node.ln_Succ) {
	    if (rp->rp_MsgPort == port) {
		Remove(&rp->rp_Node);
		if (rp->rp_IsPublic)
		    RemPort(port);
		FreeMem(rp, rp->rp_Node.ln_Type);
		break;
	    }
	}
	if (rp == NULL)
	    return;
    }

    ClearDiceRexxPort(port);

    /*
     *	clear port info
     */

    clrmem(port, sizeof(MsgPort));

    if (port == (MsgPort *)&RexxPort)
	MasterPortValid = 0;
}

void
ClearDiceRexxPort(port)
MsgPort *port;
{
    /*
     *	kill any received messages that are in progress
     */

    {
	RexxIPNode *rip;
	RexxIPNode *rip_next;

	for (rip = (RexxIPNode *)RexxRIPList.lh_Head; rip->rip_Node.ln_Succ; rip = rip_next) {
	    rip_next = (RexxIPNode *)rip->rip_Node.ln_Succ;
	    if (rip->rip_RexxPort == port) {
		Remove(&rip->rip_Node);
		RexxReply(rip->rip_RexxMsg, 30, NULL);
	    }
	}
    }


    /*
     *	clear out any messages pending on the port
     */

    {
	RexxMsg *msg;

	while (msg = (RexxMsg *)GetMsg(port)) {
	    if ((msg->rm_Node.mn_Node.ln_Type == NT_MESSAGE) && IsRexxMsg(msg)) {
		RexxReply(msg, 30, NULL);
	    } else {
		RexxIPNode *rip;

		for (rip = (RexxIPNode *)RexxWIPList.lh_Head; rip->rip_Node.ln_Succ; rip = (RexxIPNode *)rip->rip_Node.ln_Succ) {
		    if (msg == rip->rip_RexxMsg) {
			rip->rip_Returned = 1;
			break;
		    }
		}
	    }
	}
    }
}

void
RexxReply(vmsg, res1, str)
void *vmsg;
long res1;
char *str;
{
    RexxMsg *msg = vmsg;

    if (msg->rm_Result1 = res1) {
	msg->rm_Result2 = NULL;
    } else {
	if (str)
	    msg->rm_Result2 = (long)CreateArgstring(str, strlen(str));
	else
	    msg->rm_Result2 = NULL;
    }
    ReplyMsg((Message *)msg);
}

/*
 *  Obtain the fully qualified name of an application port (name.xx) and
 *  return an integer.	Returns -1 if the port does not contain a .xx
 *  extension.
 */

int
GetDiceRexxPortSlot(port, pname)
MsgPort *port;
char **pname;
{
    int slotNo = -1;
    char *ptr;

    if (port == NULL)
	port = &RexxPort;
    if (ptr = strrchr(port->mp_Node.ln_Name, '.')) {
	char *tmp;

	slotNo = strtol(ptr + 1, &tmp, 10);  /* must be base 10 re: '.01'... */
	if (tmp == ptr)
	    slotNo = -1;
    }
    if (pname)
	*pname = port->mp_Node.ln_Name;
    return(slotNo);
}
