#include "TN_UADE.player_rev.h"
#include "uade_player.h"
#include <string.h>

const TEXT USED verstag[] = VERSTAG;

struct ExecIFace *IExec;
struct DOSIFace *IDOS;
struct UtilityIFace *IUtility;
struct Interface *INewlib;

static BOOL OpenLibs (void);
static void CloseLibs (void);
static BOOL PlayerMain (struct StartMessage *start_msg);

int32 _start (STRPTR argstr, int32 argstrlen, struct ExecBase *SysBase) {
	int32 rc = RETURN_FAIL;
	struct Process *proc;
	struct MsgPort *port;
	struct StartMessage *start_msg;

	IExec = (struct ExecIFace *)SysBase->MainInterface;

	proc = (struct Process *)IExec->FindTask(NULL);
	port = &proc->pr_MsgPort;
	dbug(("Player: Waiting for Start Message.\n"));
	IExec->WaitPort(port);
	start_msg = (struct StartMessage *)IExec->GetMsg(port);

	if (OpenLibs()) {
		if (PlayerMain(start_msg)) {
			rc = RETURN_OK;
		}
		CloseLibs();
	}
	return rc;
}

static APTR OpenInterface (CONST_STRPTR lib_name, int32 lib_vers,
	CONST_STRPTR int_name, int32 int_vers)
{
	struct Library *library;
	struct Interface *interface;
	library = IExec->OpenLibrary(lib_name, lib_vers);
	if (library) {
		interface = IExec->GetInterface(library, int_name, int_vers, NULL);
		if (interface) {
			return interface;
		}
		IExec->CloseLibrary(library);
	}
	return NULL;
}

static void CloseInterface (APTR interface) {
	if (interface) {
		struct Library *library = ((struct Interface *)interface)->Data.LibBase;
		IExec->DropInterface(interface);
		IExec->CloseLibrary(library);
	}
}

static BOOL OpenLibs (void) {
	if ((IDOS = OpenInterface("dos.library", 52, "main", 1)) &&
		(IUtility = OpenInterface("utility.library", 52, "main", 1)) &&
		(INewlib = OpenInterface("newlib.library", 52, "main", 1)))
	{
		return TRUE;
	}
	CloseLibs();
	return FALSE;
}

static void CloseLibs (void) {
	CloseInterface(INewlib);
	CloseInterface(IUtility);
	CloseInterface(IDOS);
}

static BOOL PlayerMain (struct StartMessage *start_msg) {
	BOOL res = FALSE;
	struct TNDecHandle *handle = start_msg->sm_Handle;
	struct AudioDataMessage *adata_msg = handle->adata_msg;
	struct PlayerState ps;
	const int framesize = 4;

	ps.handle = handle;
	if (InitPlayer(&ps) && PlayerFunc(&ps)) {
		BOOL done = FALSE;
		uint32 sigs;

		handle->data_sig = 1 << IExec->AllocSignal(-1);
		handle->subsong_sig = 1 << IExec->AllocSignal(-1);
		handle->end_sig = 1 << IExec->AllocSignal(-1);
		IExec->ReplyMsg(&start_msg->sm_Msg);

		while (!done) {
			sigs = IExec->Wait(handle->data_sig | handle->subsong_sig | handle->end_sig);
			if (sigs == handle->data_sig) {
				if (ps.playbytes == 0) {
					PlayerFunc(&ps);
				}
				adata_msg->adm_NumPCMFrames = ps.playbytes / framesize;
				adata_msg->adm_SongEndType = 0;
				if (ps.playbytes != 0) {
					ps.playbytes = 0;
				} else {
					if (ps.next_song) {
						if (handle->cur_subsong < handle->num_subsongs)
							adata_msg->adm_SongEndType = TN_TUNE_END_NEXT_SUB;
						else
							adata_msg->adm_SongEndType = TN_TUNE_END;
						ps.subsong_end = ps.song_end_trigger = ps.next_song = 0;
						ps.what_was_left = ps.left = ps.tailbytes = 0;
					}
				}
				IExec->PutMsg(handle->main_port, &adata_msg->adm_Msg);
			} else if (sigs == handle->subsong_sig) {
				struct uade_song *us = ps.state->song;
				handle->cur_subsong = handle->jump_subsong;
				us->cur_subsong = us->min_subsong + handle->jump_subsong - 2;
				ps.subsong_end = 1;
				ps.jump_sub = 1;
				ps.playbytes = 0;
				IExec->Signal(handle->main_port->mp_SigTask, 1 << handle->main_port->mp_SigBit);
			} else if (sigs == handle->end_sig) {
				dbug(("Player: Received Exit Signal.\n"));
				done = TRUE;
			} else {
				/* Should never happen */
				dbug(("Player: Received Mixed Signals: %08lx\n", sigs));
				done = TRUE;
			}
		}
		ExitPlayer(&ps);
		res = TRUE;
	}

	return res;
}

