/* -------------------------------------------------------------------------- *\
   CALCSUB.CPP
   Copyright © 1997 by Jarno van der Linden
   jarno@kcbbs.gen.nz

   Calculation subtask for AmHelios.
   Subtask handling taken from MUI Subtask.c example

   This program is Freeware, and all usual Freeware rules apply.

   24 Aug 1997: Project started
\* -------------------------------------------------------------------------- */

/* -------------------------------- Includes -------------------------------- */
#include "CalcSub.h"

#include <dos/dosextens.h>
#include <dos/dostags.h>

#include <proto/exec.h>
#include <proto/dos.h>

/* ------------------------------ Definitions ------------------------------- */
#define STC_STARTUP  -2
#define STC_SHUTDOWN -1
#define STC_START 0
#define STC_STOP  1
#define STC_SUSPEND 2
#define STC_RESUME  3

#define CALCSUB_SNAPSHOT 16

/* --------------------------------- Macros --------------------------------- */

/* -------------------------------- Typedefs -------------------------------- */

/* -------------------------------- Structs --------------------------------- */

/* ------------------------------ Proto Types ------------------------------- */

/* -------------------------------- Globals --------------------------------- */

/* ---------------------------------- Code ---------------------------------- */
SubTask::SubTask(char *name)
{
	SpawnSubTask(name);
}


SubTask::~SubTask()
{
	KillSubTask();
}


void SubTask::Start()
{
	SendSubTaskMsg(STC_START,NULL);
}


void SubTask::Stop()
{
	SendSubTaskMsg(STC_STOP,NULL);
}


void SubTask::Suspend()
{
	SendSubTaskMsg(STC_SUSPEND,NULL);
}


void SubTask::Resume()
{
	SendSubTaskMsg(STC_RESUME,NULL);
}


LONG SubTask::SendSubTaskMsg(WORD command,APTR params)
{
	message.stm_Message.mn_ReplyPort = reply;
	message.stm_Message.mn_Length    = sizeof(struct SubTaskMsg);
	message.stm_Command              = command;
	message.stm_Parameter            = params;
	message.stm_Result               = 0;

	PutMsg(command==STC_STARTUP ? &((struct Process *)task)->pr_MsgPort : port,(struct Message *)&message);
	WaitPort(reply);
	GetMsg(reply);

	return(message.stm_Result);
}


void SubTask::SpawnSubTask(char *name)
{
	if (reply = CreateMsgPort())
	{
		if (task = (struct Task *)CreateNewProcTags(NP_Entry,Main,NP_Name,name,NP_Priority,-2,TAG_DONE))
		{
			if (SendSubTaskMsg(STC_STARTUP,this))
			{
				return;
			}
		}
		DeleteMsgPort(reply);
	}
}


void SubTask::KillSubTask()
{
	SendSubTaskMsg(STC_SHUTDOWN,NULL);
	DeleteMsgPort(reply);
}


/* the following functions are called from the sub task */

void SubTask::ExitSubTask(SubTask *thisst,struct SubTaskMsg *stm)
{
	/*
	** We reply after a Forbid() to make sure we're really gone
	** when the main task continues.
	*/

	if (thisst->port)
		DeleteMsgPort(thisst->port);

	Forbid();
	stm->stm_Result = FALSE;
	ReplyMsg((struct Message *)stm);
}

SubTask *SubTask::InitSubTask(void)
{
	struct Task *me = FindTask(NULL);
	SubTask *thisst;
	struct SubTaskMsg *stm;

	/*
	** Wait for our startup message from the SpawnSubTask() function.
	*/

	WaitPort(&((struct Process *)me)->pr_MsgPort);
	stm  = (struct SubTaskMsg *)GetMsg(&((struct Process *)me)->pr_MsgPort);
	thisst   = (SubTask *)stm->stm_Parameter;

	if (thisst->port = CreateMsgPort())
	{
		/*
		** Reply startup message, everything ok.
		** Note that if the initialization fails, the code falls
		** through and replies the startup message with a stm_Result
		** of 0 after a Forbid(). This tells SpawnSubTask() that the
		** sub task failed to run.
		*/

		stm->stm_Result = TRUE;
		ReplyMsg((struct Message *)stm);
		return(thisst);
	}
	else
	{
		ExitSubTask(thisst,stm);
		return(NULL);
	}
}


void __asm __saveds SubTask::Main(void)
{
	SubTask *thisst;

	if(thisst = InitSubTask())
	{
		BOOL running = TRUE;
		BOOL worktodo = FALSE;
		BOOL suspended = FALSE;
		struct SubTaskMsg *stm;

		for(;;)
		{
			while(stm = (struct SubTaskMsg *)GetMsg(thisst->port))
			{
				switch(stm->stm_Command)
				{
					case STC_SHUTDOWN:
						running = FALSE;
						break;
					case STC_START:
						worktodo = TRUE;
						thisst->StartFunc();
						break;
					case STC_STOP:
						worktodo = FALSE;
						thisst->StopFunc();
						break;
					case STC_SUSPEND:
						suspended = TRUE;
						break;
					case STC_RESUME:
						suspended = FALSE;
						break;
					default:
						stm->stm_Result = thisst->HandleMessage(stm->stm_Command,stm->stm_Parameter);
						break;
				}
				if(!running)
					break;

				ReplyMsg((struct Message *)stm);
			}
			if(!running)
				break;

			if((worktodo) && (!suspended))
			{
				worktodo = thisst->SubFunc();
				if(!worktodo)
					thisst->StopFunc();
			}
			else
			{
				WaitPort(thisst->port);
			}
		}
		if(worktodo)
			thisst->StopFunc();

		ExitSubTask(thisst,stm);
	}
}






CalcSub::CalcSub(char *name,RadEqnSolve *radiosity_arg,Environ *env_arg)
	: SubTask(name)
{
#if (defined(_HEMI_CUBE) || defined(_CUBIC_TETRA))
	radiosity = (ProgRad *)radiosity_arg;
#elif defined(_RAY_CAST)
	radiosity = (RayRad *)radiosity_arg;
#else
	radiosity = radiosity_arg;
#endif

	env = env_arg;
}


void CalcSub::Snapshot()
{
	SendSubTaskMsg(CALCSUB_SNAPSHOT,NULL);
}


void CalcSub::StartFunc()
{
	radiosity->Open(env);
}


BOOL CalcSub::SubFunc()
{
	return !(radiosity->Calculate());
}


void CalcSub::StopFunc()
{
	radiosity->Close();
}


void CalcSub::SnapshotFunc()
{
	radiosity->Snapshot();
}


LONG CalcSub::HandleMessage(WORD command,APTR parameter)
{
	switch(command)
	{
		case CALCSUB_SNAPSHOT:
			SnapshotFunc();
			break;
	}

	return 0;
}
