#include "uade_tnplug.h"
#include <string.h>
#include "amifilemagic.h"
#include "eagleplayer.h"
#include "support.h"

#define MAX_SUFFIX_LENGTH 16

const char *get_prefix (char *prefix, const char *name) {
	const char *t = strchr(name, '.');
	if (t) {
		int len = t - name;
		if (len < MAX_SUFFIX_LENGTH) {
			memcpy(prefix, name, len);
			prefix[len] = 0;
			return prefix;
		}
	}
	return NULL;
}

const char *get_postfix (const char *name) {
	const char *t = strrchr(name, '.');
	if (t) {
		const char *postfix = t + 1;
		if (strlen(postfix) < MAX_SUFFIX_LENGTH) {
			return postfix;
		}
	}
	return NULL;
}

extern struct eagleplayerstore *playerstore;

static inline BOOL IsUADE (uint8 *testbuffer, int32 testsize,
	const char *testme, int32 totalsize)
{
	char ext[16] = "";
	struct eagleplayer *namecandidate = NULL;
	struct eagleplayer *contentcandidate = NULL;
	if (!IsXPK(testbuffer, testsize) && !IsXFD(testbuffer, testsize)) {
		if (testbuffer && testsize > 0) {
			uint8 buf[8192];
			if (testsize > sizeof(buf)) testsize = sizeof(buf);
			memcpy(buf, testbuffer, testsize);
			memset(buf, 0xff, sizeof(buf) - testsize);
			uade_filemagic(testbuffer, testsize, ext, totalsize, testme, 0);
		}
		if (strcmp(ext, "reject") == 0 || strcmp(ext, "packed") == 0) {
			return FALSE;
		}
	}
	if (testme) {
		char prefix[MAX_SUFFIX_LENGTH];
		const char *name = xbasename(testme);
		if (get_prefix(prefix, name))
			namecandidate = get_eagleplayer(prefix, playerstore);
		if (!namecandidate) {
			const char *postfix = get_postfix(name);
			if (postfix) {
				namecandidate = get_eagleplayer(postfix, playerstore);
			}
		}
	}
	if (ext[0]) {
		contentcandidate = get_eagleplayer(ext, playerstore);
	}
	if (namecandidate || contentcandidate) {
		return TRUE;
	}
	return FALSE;
}

int32 TNPlug_TestPlayer (struct TNPlugIFace *Self, uint8 *testme, uint8 *testbuffer,
	uint32 totalsize, uint32 testsize)
{
	int32 res = 0;
	dbug(("testme: %08lx\ntestbuffer: %08lx\ntotalsize: %ld\ntestsize: %ld\n",
		testme, testbuffer, totalsize, testsize));
	if (IsUADE(testbuffer, testsize, (const char *)testme, totalsize) ||
		IsPT36(testbuffer, testsize))
	{
		res = PLM_File;
	}
	return res;
}

static inline BOOL CreateDir (CONST_STRPTR dirname) {
	BPTR lock;
	BOOL res = FALSE;
	if ((lock = IDOS->Lock(dirname, ACCESS_READ)) ||
		(lock = IDOS->CreateDir(dirname)))
	{
		res = TRUE;
		IDOS->UnLock(lock);
	}
	return res;
}

BOOL TNPlug_OpenPlayer (struct TNPlugIFace *Self, uint8 *openme, struct TuneNet *inTuneNet) {
	int32 res = FALSE;

	if (inTuneNet->playmode == PLM_File) {
		struct TNDecHandle *handle;
		int32 compression;
		BPTR player_seglist;

		handle = IExec->AllocVecTags(sizeof(*handle),
			AVT_Type, MEMF_SHARED,
			AVT_ClearWithValue, 0,
			TAG_END);
		inTuneNet->handle = handle;
		if (!handle) goto error;

		compression = IsCompressed((CONST_STRPTR)openme);
		if (compression) {
			TEXT dir[32];
			CONST_STRPTR file;
			BOOL res;
			IUtility->SNPrintf(dir, sizeof(dir), "T:TN_UADE-%08lx-TMP", handle);
			file = IDOS->FilePart((CONST_STRPTR)openme);
			if (!file || !file[0]) goto error;
			handle->filename = IUtility->ASPrintf("%s/%s", dir, file);
			if (!handle->filename) goto error;
			handle->tempfile = TRUE;
			if (!CreateDir(dir)) goto error;
			switch (compression) {
				case FORMAT_XPK:
					res = UnpackXPK((CONST_STRPTR)openme, handle->filename);
					break;
				case FORMAT_XFD:
					res = UnpackXFD((CONST_STRPTR)openme, handle->filename);
					break;
				case FORMAT_PT36:
					res = UnpackPT36((CONST_STRPTR)openme, handle->filename);
					break;
				default:
					res = FALSE;
					break;
			}
			if (!res) {
				dbug(("OpenPlayer: Decompression failed.\n"));
				goto error;
			}
		} else {
			handle->filename = IUtility->ASPrintf("%s", openme);
			if (!handle->filename) goto error;
		}

		handle->pcm = IExec->AllocVecTags(AUDIO_BUFFER_SIZE,
			AVT_Type, MEMF_SHARED,
			TAG_END);
		if (!handle->pcm) goto error;
		inTuneNet->pcm[0] = &handle->pcm[0];
		inTuneNet->pcm[1] = &handle->pcm[1];

		handle->main_port = IExec->AllocSysObjectTags(ASOT_PORT,
			ASOPORT_Signal, SIGB_CHILD,
			TAG_END);
		if (!handle->main_port) goto error;

		handle->start_msg = IExec->AllocSysObjectTags(ASOT_MESSAGE,
			ASOMSG_Name, PLUGINNAME" Start Message",
			ASOMSG_Size, sizeof(struct StartMessage),
			TAG_END);
		if (!handle->start_msg) goto error;

		handle->adata_msg = IExec->AllocSysObjectTags(ASOT_MESSAGE,
			ASOMSG_Name, PLUGINNAME" Data Message",
			ASOMSG_Size, sizeof(struct AudioDataMessage),
			TAG_END);
		if (!handle->adata_msg) goto error;

		handle->death_msg = IExec->AllocSysObjectTags(ASOT_MESSAGE,
			ASOMSG_Name, PLUGINNAME" Death Message",
			ASOMSG_Size, sizeof(struct DeathMessage),
			TAG_END);
		if (!handle->death_msg) goto error;

		player_seglist = IDOS->LoadSeg("PROGDIR:Plugins/data/"PLAYERNAME);
		if (!player_seglist) goto error;

		handle->death_msg->dm_Msg.mn_ReplyPort = handle->main_port;
		handle->player_proc = IDOS->CreateNewProcTags(
			NP_Name,        PLUGINNAME" Player Process",
			NP_Seglist,     player_seglist,
			NP_FreeSeglist, TRUE,
			NP_Priority,    5,
			NP_NotifyOnDeathMessage, handle->death_msg,
			TAG_END);
		if (!handle->player_proc) {
			IDOS->UnLoadSeg(player_seglist);
			goto error;
		}

		handle->start_msg->sm_Msg.mn_ReplyPort = handle->main_port;
		handle->start_msg->sm_Handle = handle;
		dbug(("OpenPlayer: Sending Start Message.\n"));
		IExec->PutMsg(IDOS->GetProcMsgPort(handle->player_proc), (struct Message *)handle->start_msg);
		IExec->WaitPort(handle->main_port);
		if (IExec->GetMsg(handle->main_port) == (struct Message *)handle->death_msg) {
			dbug(("OpenPlayer: Received Death Message.\n"));
			handle->player_proc = NULL;
			goto error;
		}
		dbug(("OpenPlayer: Received Start Message.\n"));

		res = TRUE;
error:
		if (res) {
			dbug(("success\n"));
			inTuneNet->DirectRender = ARender_InternalBuffer;

			inTuneNet->current_subsong = handle->cur_subsong;
			inTuneNet->max_subsongs = handle->num_subsongs;
			
			inTuneNet->dec_frequency = handle->frequency;
			inTuneNet->dec_channels = 2;
			inTuneNet->bitrate = 0;
			inTuneNet->mix_lr = TN_STEREO_50PER;

			inTuneNet->Tune[0] = 0;

			inTuneNet->ms_duration = 0;

			inTuneNet->ST_Name[0] = 0;
			inTuneNet->ST_Url[0] = 0;
			inTuneNet->ST_Note[0] = 0;

			inTuneNet->songEOF = TRUE;
		} else {
			TNPlug_ClosePlayer(Self, inTuneNet);
		}
	}

	return res;
}

void TNPlug_ClosePlayer (struct TNPlugIFace *Self, struct TuneNet *inTuneNet) {
	struct TNDecHandle *handle = inTuneNet->handle;
	if (handle) {
		if (handle->player_proc) {
			handle->main_port->mp_SigTask = IExec->FindTask(NULL);
			dbug(("ClosePlayer: Sending Exit Signal.\n"));
			IExec->Signal((struct Task *)handle->player_proc, handle->end_sig);
			IExec->WaitPort(handle->main_port);
			IExec->GetMsg(handle->main_port);
			dbug(("ClosePlayer: Received Death Message.\n"));
			handle->player_proc = NULL;
		}
		IExec->FreeSysObject(ASOT_MESSAGE, handle->start_msg);
		IExec->FreeSysObject(ASOT_MESSAGE, handle->adata_msg);
		IExec->FreeSysObject(ASOT_MESSAGE, handle->death_msg);
		IExec->FreeSysObject(ASOT_PORT, handle->main_port);
		IExec->FreeVec(handle->pcm);
		if (handle->tempfile) {
			IDOS->DeleteFile(handle->filename);
			IDOS->PathPart(handle->filename)[0] = 0;
			IDOS->DeleteFile(handle->filename);
		}
		IExec->FreeVec(handle->filename);
		IExec->FreeVec(handle);
		inTuneNet->handle = NULL;
	}
	inTuneNet->pcm[0] = inTuneNet->pcm[1] = NULL;
}
