/* Primitive test program for LoadIPCPort facility */
/* ...simply displays a request for a port in its console window */

#include "IPCPorts.h"
#include "IPC.h"

#include "exec/memory.h"
#include "exec/tasks.h"
#include "libraries/DOS.h"

#define  SOL(s)  ((LONG)sizeof(s))

#define IPPL MAKE_ID('I','P','P','L')
#define PORT MAKE_ID('P','O','R','T')


struct IPCPort *brokerport; /* actually IPCBasePort -- for convenience */
struct IPCMessage *imsg=NULL;

void baditem(struct IPCItem *, ULONG);
void outputstr(char *);

struct Task * FindTask(char *);

struct IPCPort * ServeIPCBase();
void ShutIPCBase();
void LeaveIPCBase();
void Cleanup();


ULONG bportsig = 0;  /* signal masks for port */

int active = TRUE;


void _main()
{
    ULONG sigset;

    brokerport = ServeIPCBase();
    if (!brokerport) _exit(11);
    bportsig = 1<<brokerport->ipp_Port.mp_SigBit;


    do {
        while ( procimsg() ) ;    /* loop */
        if (active) {
            sigset = Wait(bportsig | SIGBREAKF_CTRL_C);
            if (sigset & SIGBREAKF_CTRL_C) {
                active = FALSE;
                ShutIPCBase(); /* not really necessary... */
                continue; /* so we clear out any messages that sneak in */
            }
        }
    } while (active);
    outputstr("Broker terminating...\n");

    Cleanup();
}


void Cleanup()
{
    LeaveIPCBase();
}


procimsg()
{
    struct IPCItem *item;
    if (!(imsg = (struct IPCMessage *) GetMsg(brokerport))) return FALSE;
    item = imsg->ipc_Items;
    if (imsg->ipc_Id == IPPL && item->ii_Id == PORT
        && loadport(item->ii_Ptr)) /* everything OK */;
    else imsg->ipc_Flags |= IPC_NOTKNOWN;
    ReplyMsg(imsg);
    return TRUE;
}


void baditem(item, extraflags)
    struct IPCItem *item;
    ULONG extraflags;
{
    imsg->ipc_Flags |= IPC_CHECKITEM;
    item->ii_Flags |= IPC_NOTKNOWN | extraflags;
}

void outputstr(str) char *str;
{
    Write(Output(), str, strlen(str));
}


/*
 *  loadport(portptr)
 *
 *  -- actually initiates the loading procedure (here just a skeleton).
 *      returns TRUE if successful, otherwise FALSE.
 */

loadport(port) struct IPCPort *port;
{
    outputstr("Please load server for port '");
    outputstr(port->ipp_Name);
    outputstr("' -- Thanks\n");
    port->ipp_Flags |= IPP_LOADING;
    return TRUE; /* -- doesn't know how to fail yet... */
}


/****************************************************************/

/*
 *  The following procedures are simple conversions (the first is
 *  a direct copy) of the corresponding ones in IPC.c.
 *  The standard IPC module itself is not needed.
 */

struct IPCBasePort *IPCBasePort = NULL;


struct IPCBasePort * OpenIPCBase()
{
    char * pname = "IPC_Base_Port";
    struct MsgPort * port;

    if (IPCBasePort) return IPCBasePort;
    Forbid();
    if (!(IPCBasePort = (struct IPCBasePort *)FindPort(pname))) {
        IPCBasePort = (struct IPCBasePort *)
            AllocMem(SOL(struct IPCBasePort), MEMF_CLEAR | MEMF_PUBLIC);
        if (IPCBasePort) {
            port = & IPCBasePort->ipcb_Port.ipp_Port;
            port->mp_Node.ln_Name = IPCBasePort->ipcb_Port.ipp_Name;
            port->mp_Node.ln_Type = NT_MSGPORT;
            AddPort(port);
            NewList(&(IPCBasePort->ipcb_PortList));
            strcpy(IPCBasePort->ipcb_Port.ipp_Name, pname);
        }
    }
    Permit();
    return IPCBasePort;
}



struct IPCPort * ServeIPCBase()
{
    struct IPCPort * port;

    port = (struct IPCPort *)OpenIPCBase();
    if (port) {
        Forbid();
        if ((port->ipp_Flags & (IPP_SERVED))
         || (port->ipp_Port.mp_SigBit = AllocSignal(-1L)) == -1L) {
            port = NULL;
        }
        else {
            port->ipp_Port.mp_Flags = PA_SIGNAL;
            port->ipp_Port.mp_SigTask = FindTask(NULL);
            port->ipp_Flags = IPP_SERVED; /* all other bits cleared */
        }
        Permit();
    }
    return port;
}

void ShutIPCBase()
{
    struct IPCPort *port;
    if (!IPCBasePort) return;
    port = &IPCBasePort->ipcb_Port;
    port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
    port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
    port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
}


void LeaveIPCBase()
{
    struct IPCPort *port;
    if (!IPCBasePort) return;
    port = &IPCBasePort->ipcb_Port;
    FreeSignal(port->ipp_Port.mp_SigBit);
    port->ipp_Port.mp_SigTask = NULL;
    port->ipp_Flags = 0;
}


