#include <arpbase.h>
#include <arp_proto.h>
#include "Launch.h"
#include "LaunchPriv.h"

struct List *ProcPairList;

/*
 *	NAME
 *		StartProcess -- start a new process.
 *
 *	SYNOPSIS
 *		Child = StartProcess (Name, Entry, Data)
 *
 *		struct Process *StartProcess (char *, CPTR, void *);
 *
 *	FUNCTION
 *		Create a process to start at the entry point specified and
 *		pass it the data.
 *
 *	DESCRIPTION
 *		We check if the process pair list already exists, if not we
 *		create it.  We then allocate a ProcPair structure to hold all
 *		the data needed for the control:
 *
 *			Both processes' structure pointer, the signal bits,
 *			the entry point and data for the child.
 *
 *		We then try to start the child.  If successful, we add the
 *		process pair to the list.
 *
 *		In order not to have too many signal bit allocated, we always
 *		check if the parent has already launched another child which
 *		is still active and use the same signal bit.
 *
 *	INPUT
 *		Name - the name for the new process, does NOT need to be unique.
 *		Entry - the entry point (function) for the enw process.
 *		Data - the data (anything) to be passed as an argument to the
 *			entry point of the new process.
 *
 *	OUTPUT
 *		Proc - the new process' structure pointer.
 *
 *	NOTE
 *		We use arp.library, but it can be adapted easily to 2.04.
 *
 *	HISTORY
 *		1992/09/06	Pierre Baillargeon		Creation
 *
 *	SEE ALSO
 *		KillProcess(), KillProcesses(), WaitProcess(), WaitProcesses()
 */

struct Process *StartProcess (char *Name, CPTR EntryPoint, void *Data, WORD Priority)
{
	struct ProcPair *pp = NULL;
	struct ProcessControlBlock	PCB = { 8192L, 0,
										PRF_CODE | PRF_NOCLI | PRF_SAVEIO,
										0, 0, 0, 0, ChildEntry, 0, 0 };

	Forbid ();

	/*
	 *	Create the list if it does not exist.
	 */

	if (NULL == ProcPairList)
	{
		ProcPairList = (struct List *)AllocMem (sizeof (struct List), MEMF_CLEAR);
		if (NULL == ProcPairList)
		{
			goto Error;
		}
		NewList (ProcPairList);
	}

	/*
	 *	Create and initialize the process pair.
	 */

	pp = (struct ProcPair *)AllocMem (sizeof (struct ProcPair), MEMF_CLEAR);
	if (NULL == pp)
	{
		goto Error;
	}

	pp->pp_Node.ln_Type = 0;
	pp->pp_Node.ln_Pri = 0;
	pp->pp_Parent = (struct Process *)FindTask (NULL);

	/*
	 *	Try to find if this process has already launched another and
	 *	use the same signal bit.
	 */

	if (-1L == (pp->pp_ParentBit = AllocParentSignal ()))
	{
		goto Error;
	}


	/*
	 *	Launch child, if successful add the process pair to the list.
	 */

	pp->pp_ChildEntry = (void (*__saveds)(void *))EntryPoint;
	pp->pp_ChildData = Data;
	PCB.pcb_Pri = Priority;

	if (ASyncRun (Name, NOCMD, &PCB) >= 0L)
	{
		pp->pp_Child = (struct Process *)(((char *)PCB.pcb_WBProcess) - sizeof (struct Task));
		AddHead (ProcPairList, &pp->pp_Node);
		Signal (pp->pp_Child, 1L << pp->pp_Child->pr_MsgPort.mp_SigBit);
		Permit ();
		return (struct Process *)(((char *)PCB.pcb_WBProcess) - sizeof (struct Task));
	}

Error:
	if (pp)
	{
		FreeParentSignal (pp->pp_ParentBit);
		FreeMem (pp, sizeof (struct ProcPair));
	}
	FreeProcPairList ();
	Permit ();
	return NULL;
}
