#include "library_init.h"

#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/initializers.h>

#include "wrap_mpega.h"

#ifdef BUILD_POWERUP
#include "support_pup.h"
#else
#include "support.h"
#endif

/* Reserved library-function and dummy entrypoint */
LONG LIB_Reserved(void)
{
	return 0;
}

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

ULONG LibVectors[] = {
#ifdef __MORPHOS__
	FUNCARRAY_32BIT_NATIVE,
#endif
	(ULONG) &LIB_Open,
	(ULONG) &LIB_Close,
	(ULONG) &LIB_Expunge,
	(ULONG) &LIB_Reserved,

	(ULONG) &LIB_MPEGA_open,
	(ULONG) &LIB_MPEGA_close,
	(ULONG) &LIB_MPEGA_decode_frame,
	(ULONG) &LIB_MPEGA_seek,
	(ULONG) &LIB_MPEGA_time,
	(ULONG) &LIB_MPEGA_find_sync,
	(ULONG) &LIB_MPEGA_scale,
	0xFFFFFFFF
};

struct LibInitStruct
{
	ULONG	LibSize;
	void	*FuncTable;
	void	*DataTable;
	void	(*InitFunc)(void);
};

struct LibInitStruct LibInitStruct = {
	sizeof(struct PrivateBase),
	LibVectors,
	NULL,
	(void (*)(void)) &LibInit
};

static const char LibVersion[] = "$VER: " LIB_ID;

static const struct Resident RomTag __attribute__((__aligned__(2))) = {
	RTC_MATCHWORD,
	&RomTag,
	&RomTag + 1,
#ifdef __MORPHOS__
	RTF_PPC | RTF_AUTOINIT,
#else
	RTF_AUTOINIT,
#endif
	LIB_VERSION,
	NT_LIBRARY,
	0,
	LIB_NAME,
	&LibVersion[6],
	&LibInitStruct
};


#ifdef __MORPHOS__
ULONG __amigappc__ = 1;
#endif


static void LibCleanup(struct PrivateBase *PrivateBase)
{
#ifdef BUILD_POWERUP
	RemoveSupport(PrivateBase);
#else
	RemoveSupport();
#endif

	if (DOSBase)
	{
		CloseLibrary((struct Library *)DOSBase);
		PrivateBase->pv_DOSBase = DOSBase = NULL;
	}
}

BPTR LibExpunge(struct PrivateBase *PrivateBase)
{
	if ((PrivateBase->pv_Lib.lib_OpenCnt == 0))
	{
		BPTR seglist;

		seglist = PrivateBase->pv_SegList;
		Remove(&PrivateBase->pv_Lib.lib_Node);
		LibCleanup(PrivateBase);

		FreeMem((APTR)((ULONG)(PrivateBase) - (ULONG)(PrivateBase->pv_Lib.lib_NegSize)),
				PrivateBase->pv_Lib.lib_NegSize + PrivateBase->pv_Lib.lib_PosSize);

		return seglist;
	} else PrivateBase->pv_Lib.lib_Flags |= LIBF_DELEXP;

	return 0L;
}

#ifdef __MORPHOS__
struct Library *LibInit(struct PrivateBase *PrivateBase, BPTR SegList, struct ExecBase *sysbase)
#else
struct Library * ASM LibInit(REG(d0, struct PrivateBase *PrivateBase), REG(a0, BPTR SegList), REG(a6, struct ExecBase *sysbase))
#endif
{
	SysBase = sysbase;

	if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
	{
#ifdef BUILD_POWERUP
		if (InitSupport(PrivateBase))
#else
		if (InitSupport())
#endif
		{
			PrivateBase->pv_Lib.lib_Revision     = LIB_REVISION;
			PrivateBase->pv_SegList              = SegList;
			PrivateBase->pv_SysBase              = SysBase;
			PrivateBase->pv_DOSBase              = DOSBase;

			return &PrivateBase->pv_Lib;
		}
	}

	LibCleanup(PrivateBase);
	return NULL;
}

#ifdef __MORPHOS__
struct Library *LIB_Open(void)
{
	struct PrivateBase *PrivateBase = (struct PrivateBase *)REG_A6;
#else
struct Library * ASM LIB_Open(REG(a6, struct PrivateBase *PrivateBase))
{
#endif

	PrivateBase->pv_Lib.lib_Flags &= ~LIBF_DELEXP;
	PrivateBase->pv_Lib.lib_OpenCnt++;

	return &PrivateBase->pv_Lib;
}

#ifdef __MORPHOS__
BPTR LIB_Close(void)
{
	struct PrivateBase *PrivateBase = (struct PrivateBase *)REG_A6;
#else
BPTR ASM LIB_Close(REG(a6, struct PrivateBase *PrivateBase))
{
#endif

	if (PrivateBase->pv_Lib.lib_OpenCnt > 0) PrivateBase->pv_Lib.lib_OpenCnt--;

	if ((PrivateBase->pv_Lib.lib_OpenCnt == 0) && (PrivateBase->pv_Lib.lib_Flags & LIBF_DELEXP))
		return LibExpunge(PrivateBase);

	return 0L;
}

#ifdef __MORPHOS__
BPTR LIB_Expunge(void)
{
	struct PrivateBase *PrivateBase = (struct PrivateBase *)REG_A6;
#else
BPTR ASM LIB_Expunge(REG(a6, struct PrivateBase *PrivateBase))
{
#endif

	return LibExpunge(PrivateBase);
}

#ifdef __MORPHOS__
MPEGA_STREAM *LIB_MPEGA_open(void)
{
	char *stream_name = (char *)REG_A0;
	MPEGA_CTRL *ctrl = (MPEGA_CTRL *)REG_A1;
#else
MPEGA_STREAM * LIB LIB_MPEGA_open(REG(a0, char *stream_name), REG(a1, MPEGA_CTRL *ctrl), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (MPEGA_STREAM *)CallPPC(PrivateBase, PPCLVO_MPEGA_open, stream_name, ctrl);
#else
	return WRAP_MPEGA_open(stream_name, ctrl);
#endif
}

#ifdef __MORPHOS__
void LIB_MPEGA_close(void)
{
	MPEGA_STREAM *mpega_stream = (MPEGA_STREAM *)REG_A0;
#else
void LIB LIB_MPEGA_close(REG(a0, MPEGA_STREAM *mpega_stream), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	CallPPC(PrivateBase, PPCLVO_MPEGA_close, mpega_stream);
#else
	WRAP_MPEGA_close(mpega_stream);
#endif
}

#ifdef __MORPHOS__
LONG LIB_MPEGA_decode_frame(void)
{
	MPEGA_STREAM *mpega_stream = (MPEGA_STREAM *)REG_A0;
	WORD **pcm = (WORD **)REG_A1;
#else
LONG LIB LIB_MPEGA_decode_frame(REG(a0, MPEGA_STREAM *mpega_stream), REG(a1, WORD *pcm[MPEGA_MAX_CHANNELS]), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (LONG)CallPPC(PrivateBase, PPCLVO_MPEGA_decode_frame, mpega_stream, pcm);
#else
	return WRAP_MPEGA_decode_frame(mpega_stream, pcm);
#endif
}

#ifdef __MORPHOS__
LONG LIB_MPEGA_seek(void)
{
	MPEGA_STREAM *mpega_stream = (MPEGA_STREAM *)REG_A0;
	ULONG ms_time_position = (ULONG)REG_D0;
#else
LONG LIB LIB_MPEGA_seek(REG(a0, MPEGA_STREAM *mpega_stream), REG(d0, ULONG ms_time_position), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (LONG)CallPPC(PrivateBase, PPCLVO_MPEGA_seek, mpega_stream, ms_time_position);
#else
	return WRAP_MPEGA_seek(mpega_stream, ms_time_position);
#endif
}

#ifdef __MORPHOS__
LONG LIB_MPEGA_time(void)
{
	MPEGA_STREAM *mpega_stream = (MPEGA_STREAM *)REG_A0;
	ULONG *ms_time_position = (ULONG *)REG_A1;
#else
LONG LIB LIB_MPEGA_time(REG(a0, MPEGA_STREAM *mpega_stream), REG(a1, ULONG *ms_time_position), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (LONG)CallPPC(PrivateBase, PPCLVO_MPEGA_time, mpega_stream, ms_time_position);
#else
	return WRAP_MPEGA_time(mpega_stream, ms_time_position);
#endif
}

#ifdef __MORPHOS__
LONG LIB_MPEGA_find_sync(void)
{
	UBYTE *buffer = (UBYTE *)REG_A0;
	LONG buffer_size = (LONG)REG_D0;
#else
LONG LIB LIB_MPEGA_find_sync(REG(a0, UBYTE *buffer), REG(d0, LONG buffer_size), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (LONG)CallPPC(PrivateBase, PPCLVO_MPEGA_find_sync, buffer, buffer_size);
#else
	return WRAP_MPEGA_find_sync(buffer, buffer_size);
#endif
}

#ifdef __MORPHOS__
LONG LIB_MPEGA_scale(void)
{
	MPEGA_STREAM *mpega_stream = (MPEGA_STREAM *)REG_A0;
	LONG scale_percent = (LONG)REG_D0;
#else
LONG LIB LIB_MPEGA_scale(REG(a0, MPEGA_STREAM *mpega_stream), REG(d0, LONG scale_percent), REG(a6, struct PrivateBase *PrivateBase))
{
#endif
#ifdef BUILD_POWERUP
	return (LONG)CallPPC(PrivateBase, PPCLVO_MPEGA_scale, mpega_stream, scale_percent);
#else
	return WRAP_MPEGA_scale(mpega_stream, scale_percent);
#endif
}
