// =======================================================================
// 68k side library startup and communications dispatcher
// (c)1998 by Jacek Cybularczyk (NOE/VenusArt)
// slightly modified for specific use by Sigbjørn (CISC) Skjæret
// =======================================================================

#include "library_base.h"

#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>

#include <powerup/ppclib/object.h>
#include <powerup/ppclib/interface.h>
#include <powerup/ppclib/message.h>
#include <powerup/ppclib/tasks.h>

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

struct Library *PPCLibBase = NULL;
extern UBYTE _ElfObject;

ULONG InitSupport(struct PrivateBase *PrivateBase)
{
	struct PPCObjectInfo PPCObjectInfo;
	struct TagItem tmpTagList[20];

	if ((PPCLibBase = OpenLibrary("ppc.library", 0)))
	{
		PrivateBase->pv_PPCLibBase = PPCLibBase;

		if ((PrivateBase->pv_ElfObject = PPCLoadObjectTags(PPCELFLOADTAG_ELFADDRESS, (ULONG)&_ElfObject, PPCELFLOADTAG_ELFNAME, (ULONG)PrivateBase->pv_Lib.lib_Node.ln_Name, TAG_DONE)))
		{
			PPCObjectInfo.Address = 0L;
			PPCObjectInfo.Name = "__LibFuncTable";

			PPCGetObjectAttrs(PrivateBase->pv_ElfObject, &PPCObjectInfo, NULL);
			PrivateBase->pv_LibFuncTable = (APTR)PPCObjectInfo.Address;

			tmpTagList[0].ti_Tag = TAG_DONE;
			if ((PrivateBase->pv_ReplyPort = PPCCreatePort(tmpTagList)))
			{
				if ((PrivateBase->pv_StartupMsg = PPCCreateMessage(PrivateBase->pv_ReplyPort, sizeof(struct StartupData))))
				{
					if ((PrivateBase->pv_StartupData = PPCAllocVec(sizeof(struct StartupData), MEMF_ANY)))
					{
						PrivateBase->pv_StartupData->ElfObject = PrivateBase->pv_ElfObject;

						tmpTagList[0].ti_Tag = PPCTASKTAG_STARTUP_MSG;
						tmpTagList[0].ti_Data = (ULONG)PrivateBase->pv_StartupMsg;

						tmpTagList[1].ti_Tag = PPCTASKTAG_STARTUP_MSGDATA;
						tmpTagList[1].ti_Data = (ULONG)PrivateBase->pv_StartupData;

						tmpTagList[2].ti_Tag = PPCTASKTAG_STARTUP_MSGLENGTH;
						tmpTagList[2].ti_Data = sizeof(struct StartupData);

						tmpTagList[3].ti_Tag = PPCTASKTAG_STARTUP_MSGID;
						tmpTagList[3].ti_Data = 0;

						tmpTagList[4].ti_Tag = PPCTASKTAG_MSGPORT;
						tmpTagList[4].ti_Data = TRUE;

						tmpTagList[5].ti_Tag = PPCTASKTAG_NAME;
						tmpTagList[5].ti_Data = (ULONG)PrivateBase->pv_Lib.lib_Node.ln_Name;

						tmpTagList[6].ti_Tag = PPCTASKTAG_STACKSIZE;
						tmpTagList[6].ti_Data = 1024*100;

						tmpTagList[7].ti_Tag = PPCTASKTAG_INPUTHANDLE;
						tmpTagList[7].ti_Data = (ULONG)Input();
                    
						tmpTagList[8].ti_Tag = PPCTASKTAG_OUTPUTHANDLE;
						tmpTagList[8].ti_Data = (ULONG)Output();

						tmpTagList[9].ti_Tag = NP_CloseInput;
						tmpTagList[9].ti_Data = FALSE;

						tmpTagList[10].ti_Tag = NP_CloseOutput;
						tmpTagList[10].ti_Data = FALSE;

						tmpTagList[11].ti_Tag = TAG_DONE;

						if ((PrivateBase->pv_Task = PPCCreateTask(PrivateBase->pv_ElfObject, tmpTagList)))
						{
							if ((PrivateBase->pv_PPCPort = (APTR)PPCGetTaskAttrsTags(PrivateBase->pv_Task, PPCTASKINFOTAG_MSGPORT, 0, TAG_END)))
							{
								if ((PrivateBase->pv_CallPPCMsg = PPCCreateMessage(PrivateBase->pv_ReplyPort, sizeof(struct CallPPCData))))
								{
									if ((PrivateBase->pv_CallPPCData = PPCAllocVec(sizeof(struct CallPPCData), MEMF_ANY)))
									{
										PrivateBase->pv_CallPPCData->Type = MSGTYPE_INIT;

										PPCSendMessage(PrivateBase->pv_PPCPort, PrivateBase->pv_CallPPCMsg, PrivateBase->pv_CallPPCData, sizeof(struct CallPPCData), 0x12345678);
										PPCWaitPort(PrivateBase->pv_ReplyPort);
										PPCGetMessage(PrivateBase->pv_ReplyPort);

										Delay(1);
										return PrivateBase->pv_CallPPCData->Return;
									}

									PPCDeleteMessage(PrivateBase->pv_CallPPCMsg);
								}
							}

							/* TODO: Kill still active task */
						}

						PPCFreeVec(PrivateBase->pv_StartupData);
					}

					PPCDeleteMessage(PrivateBase->pv_StartupMsg);
				}

				PPCDeletePort(PrivateBase->pv_ReplyPort);
			}

			PPCUnLoadObject(PrivateBase->pv_ElfObject);
		}

		CloseLibrary(PPCLibBase);
		PrivateBase->pv_PPCLibBase = PPCLibBase = NULL;
	}

	return 0;
}

void RemoveSupport(struct PrivateBase *PrivateBase)
{
	if (PPCLibBase)
	{
		if (PrivateBase->pv_CallPPCData)
		{
			PrivateBase->pv_CallPPCData->Type = MSGTYPE_REMOVE;
			PPCSendMessage(PrivateBase->pv_PPCPort, PrivateBase->pv_CallPPCMsg, PrivateBase->pv_CallPPCData, sizeof(struct CallPPCData), 0x12345678);

#if 0
			while (PPCGetMessage(PrivateBase->pv_ReplyPort) != PrivateBase->pv_StartupMsg)
			{
				/* This deadlocks .. why? */
				PPCWaitPort(PrivateBase->pv_ReplyPort);
			}
#else
			Delay(1);
#endif

			PPCFreeVec(PrivateBase->pv_CallPPCData);
		}

		if (PrivateBase->pv_CallPPCMsg)
			PPCDeleteMessage(PrivateBase->pv_CallPPCMsg);

		if (PrivateBase->pv_StartupData)
			PPCFreeVec(PrivateBase->pv_StartupData);

		if (PrivateBase->pv_StartupMsg)
			PPCDeleteMessage(PrivateBase->pv_StartupMsg);

		if (PrivateBase->pv_ReplyPort)
			PPCDeletePort(PrivateBase->pv_ReplyPort);

		if (PrivateBase->pv_ElfObject)
			PPCUnLoadObject(PrivateBase->pv_ElfObject);

		if (PrivateBase->pv_PPCLibBase)
			CloseLibrary(PrivateBase->pv_PPCLibBase);

		PrivateBase->pv_PPCLibBase = PPCLibBase = NULL;
	}
}

ULONG CallPPC(struct PrivateBase *PrivateBase, ULONG PPCLVO, ...)
{
	ULONG *Args;
	LONG i;
	APTR CallPPCMsg;
	struct CallPPCData *CallPPCData;
	APTR ReplyPort;
	ULONG retval;
	struct TagItem tmpTagList[20];

	Args = &PPCLVO;
	Args++;

	tmpTagList[0].ti_Tag = TAG_DONE;
	ReplyPort = PPCCreatePort(tmpTagList);
	CallPPCMsg = PPCCreateMessage(ReplyPort, sizeof(struct CallPPCData));
	CallPPCData = PPCAllocVec(sizeof(struct CallPPCData), MEMF_ANY);

	CallPPCData->Type = MSGTYPE_CALL;
	CallPPCData->PPCLVO = PPCLVO;
	for(i=0; i<ARGS_NUMBER; i++)
		CallPPCData->Args[i] = Args[i];

	PPCCacheClearE(NULL, 0xffffffff, CACRF_ClearD);

	PPCSendMessage(PrivateBase->pv_PPCPort, CallPPCMsg, CallPPCData, sizeof(struct CallPPCData), 0x12345678);

	PPCWaitPort(ReplyPort);
	PPCGetMessage(ReplyPort);

	retval = CallPPCData->Return;

	PPCFreeVec(CallPPCData);
	PPCDeleteMessage(CallPPCMsg);
	PPCDeletePort(ReplyPort);

	return retval;
}
