/* TN_UADE.tnplug - an UADE plugin for TuneNet

   Copyright (C) 2012 Fredrik Wikstrom <fredrik@a500.org>

   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 2.1 of the License, or (at
   your option) any later version.

   This library is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
   for more details.
*/

#include "TN_UADE.tnplug_rev.h"
#include "uade_tnplug.h"
#include <stdlib.h>
#include "eagleplayer.h"

#define LIBNAME PLUGINNAME

const char USED verstag[] = VERSTAG;

int _start () {
	return RETURN_FAIL;
}

struct ExecIFace *IExec;
struct DOSIFace *IDOS;
struct UtilityIFace *IUtility;
struct Interface *INewlib;
struct XpkIFace *IXpk;
struct XfdIFace *IXfd;

struct eagleplayerstore *playerstore = NULL;

static uint32 libObtain (struct Interface *Self);
static uint32 libRelease (struct Interface *Self);
static struct TNPlugBase *libOpen (struct Interface *Self, uint32 version);
static BPTR libClose (struct Interface *Self);
static BPTR libExpunge (struct Interface *Self);

static CONST_APTR lib_manager_vectors[] = {
	(APTR)libObtain,
	(APTR)libRelease,
	NULL,
	NULL,
	(APTR)libOpen,
	(APTR)libClose,
	(APTR)libExpunge,
	NULL,
	(APTR)-1,
};

static const struct TagItem lib_managerTags[] = {
	{ MIT_Name,			(uint32)"__library"			},
	{ MIT_VectorTable,	(uint32)lib_manager_vectors	},
	{ MIT_Version,		1							},
	{ TAG_END }
};

static CONST_APTR lib_main_vectors[] = {
	(APTR)libObtain,
	(APTR)libRelease,
	NULL,
	NULL,
	(APTR)TNPlug_AnnouncePlayer,
	(APTR)TNPlug_InitPlayer,
	(APTR)TNPlug_OpenPlayer,
	(APTR)TNPlug_ClosePlayer,
	(APTR)TNPlug_DecodeFramePlayer,
	(APTR)TNPlug_ExitPlayer,
	(APTR)TNPlug_TestPlayer,
	(APTR)TNPlug_SeekPlayer,
	(APTR)TNPlug_DoNotify,
	(APTR)-1
};

static const struct TagItem lib_mainTags[] = {
	{ MIT_Name,			(uint32)"main"				},
	{ MIT_VectorTable,	(uint32)lib_main_vectors	},
	{ MIT_Version,		1							},
	{ TAG_END }
};

static CONST_APTR libInterfaces[] = {
	lib_managerTags,
	lib_mainTags,
	NULL
};

static struct TNPlugBase *libInit (struct TNPlugBase *tnpb, BPTR seglist,
	struct ExecIFace *ISys);

static const struct TagItem libCreateTags[] = {
	{ CLT_DataSize,		(uint32)sizeof(struct TNPlugBase)	},
	{ CLT_InitFunc,		(uint32)libInit						},
	{ CLT_Interfaces,	(uint32)libInterfaces				},
	{ TAG_END }
};

const struct Resident USED lib_res =
{
    RTC_MATCHWORD,
    (struct Resident *)&lib_res,
    (APTR)(&lib_res + 1),
    RTF_NATIVE|RTF_AUTOINIT, /* Add RTF_COLDSTART if you want to be resident */
    VERSION,
    NT_LIBRARY, /* Make this NT_DEVICE if needed */
    0, /* PRI, usually not needed unless you're resident */
    LIBNAME,
    VSTRING,
    (APTR)libCreateTags
};

static BOOL OpenLibs (void);
static void CloseLibs (void);
static void free_playerstore (void);

static struct TNPlugBase *libInit (struct TNPlugBase *tnpb, BPTR seglist,
	struct ExecIFace *ISys)
{
	tnpb->tnpb_LibNode.lib_Node.ln_Type = NT_LIBRARY;
	tnpb->tnpb_LibNode.lib_Node.ln_Pri  = 0;
	tnpb->tnpb_LibNode.lib_Node.ln_Name = LIBNAME;
	tnpb->tnpb_LibNode.lib_Flags        = LIBF_SUMUSED|LIBF_CHANGED;
	tnpb->tnpb_LibNode.lib_Version      = VERSION;
	tnpb->tnpb_LibNode.lib_Revision     = REVISION;
	tnpb->tnpb_LibNode.lib_IdString     = VSTRING;
	tnpb->tnpb_SegList = seglist;
	IExec = ISys;

	if (OpenLibs()) {
		playerstore = read_eagleplayer_conf("UADE:eagleplayer.conf");
		if (playerstore) {
			return tnpb;
		}
		free_playerstore();
	}
	IExec->DeleteLibrary(&tnpb->tnpb_LibNode);
	return NULL;
}

static uint32 libObtain (struct Interface *Self) {
	return ++Self->Data.RefCount;
}

static uint32 libRelease (struct Interface *Self) {
	return --Self->Data.RefCount;
}

static struct TNPlugBase *libOpen (struct Interface *Self, uint32 version) {
	struct TNPlugBase *tnpb = (struct TNPlugBase *)Self->Data.LibBase;

	tnpb->tnpb_LibNode.lib_OpenCnt++;
	tnpb->tnpb_LibNode.lib_Flags &= ~LIBF_DELEXP;

	return tnpb;
}

static BPTR libExpunge (struct Interface *Self);

static BPTR libClose (struct Interface *Self) {
	struct TNPlugBase *tnpb = (struct TNPlugBase *)Self->Data.LibBase;

	tnpb->tnpb_LibNode.lib_OpenCnt--;

	if (tnpb->tnpb_LibNode.lib_OpenCnt) {
		return 0;
	}

	if (tnpb->tnpb_LibNode.lib_Flags & LIBF_DELEXP) {
		return libExpunge(Self);
	} else {
		return 0;
	}
}

static BPTR libExpunge (struct Interface *Self) {
	struct TNPlugBase *tnpb = (struct TNPlugBase *)Self->Data.LibBase;
	BPTR result = 0;

	if (tnpb->tnpb_LibNode.lib_OpenCnt == 0) {
		IExec->Remove(&tnpb->tnpb_LibNode.lib_Node);
		result = tnpb->tnpb_SegList;

		free_playerstore();
		CloseLibs();

		IExec->Remove(&tnpb->tnpb_LibNode.lib_Node);
		IExec->DeleteLibrary(&tnpb->tnpb_LibNode);
	} else {
		tnpb->tnpb_LibNode.lib_Flags |= LIBF_DELEXP;
	}

	return result;
}

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)))
	{
		IXpk = OpenInterface("xpkmaster.library", 5, "main", 1);
		IXfd = OpenInterface("xfdmaster.library", 39, "main", 1);
		return TRUE;
	}
	CloseLibs();
	return FALSE;
}

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

static void free_playerstore (void) {
	if (playerstore) {
		free(playerstore->players);
		free(playerstore->map);
		free(playerstore);
		playerstore = NULL;
	}
}

