#ifdef KICK_13
#include <libraries/arpbase.h>
#include <clib/arp_protos.h>
#else
extern struct Library *DOSBase;
#endif
#include "Launch.h"
#include "LaunchPriv.h"

struct List *ProcPairList;

/*
 *	NAME
 *		StartProcess -- start a new process.
 *
 *	SYNOPSIS
 *		Child = StartProcess (Name, Entry, Data, Priority)
 *
 *		struct Process *StartProcess (char *, void *, void *, WORD);
 *
 *	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
 *		1992/10/25	Pierre Baillargeon		Update
 *					Adapted to Kickstart 2.0.
 *
 *	SEE ALSO
 *		KillProcess(), KillProcesses(), WaitProcess(), WaitProcesses()
 */

struct Process *StartProcess (char *Name, void *EntryPoint, void *Data, WORD Priority)
{
	struct ProcPair *pp = NULL;
	struct Process *p = NULL;
#ifdef KICK_13
	struct ProcessControlBlock	PCB = { 8192L, 0,
										PRF_CODE | PRF_NOCLI | PRF_SAVEIO,
										0, 0, 0, 0, ChildEntry, 0, 0 };
	PCB.pcb_Pri = Priority;
#else
	struct TagItem Tags[5] = { {NP_Entry, ChildEntry}, { NP_StackSize, 8192 },
		{ NP_Name, NULL }, {NP_Priority, 0}, { TAG_DONE, 0 } };

	Tags[2].ti_Data = (ULONG)Name;
	Tags[3].ti_Data = (ULONG)Priority;
#endif

	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;

#ifdef KICK_13
	if (ASyncRun (Name, NOCMD, &PCB) >= 0L)
	{
		p = (struct Process *)(((char *)PCB.pcb_WBProcess) - sizeof (struct Task));
#else
	if (NULL != (p = CreateNewProc (Tags)))
	{
#endif
		pp->pp_Child = p;
		AddHead (ProcPairList, &pp->pp_Node);
		Signal (p, 1L << p->pr_MsgPort.mp_SigBit);
		Permit ();
		return p;
	}

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