#include <proto/exec.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/initializers.h>
#include <proto/locale.h>

#include <emul/emulinterface.h>
#include <emul/emulregs.h>
#include <ppcinline/exec.h>
#include <public/quark/quark.h>
#include <public/proto/quark/syscall_protos.h>

#include "xpkmaster.h"
#include "texts.h"
#include "version.h"

struct Library *LibInit(struct Library *xpkLib, BPTR seglist, struct ExecBase *sysbase);
struct Library *LIB_Open(void);
BPTR LIB_Close(void);
BPTR LIB_Expunge(void);

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

struct DosLibrary *		DOSBase		= 0;
struct IntuitionBase *		IntuitionBase	= 0;
struct UtilityBase *		UtilityBase	= 0;
struct Library *		GadToolsBase	= 0;
struct LocaleBase *		LocaleBase	= 0;
struct Library *		XpkBase		= 0;
struct ExecBase *		SysBase		= 0;
static struct Catalog * 	Catalog		= 0;
static BPTR			SegList		= 0;

ULONG LibVectors[] = {
	FUNCARRAY_32BIT_NATIVE,
	(ULONG) &LIB_Open,
	(ULONG) &LIB_Close,
	(ULONG) &LIB_Expunge,
	(ULONG) &LIB_Reserved,
	(ULONG) &LIB_Reserved,	/* This is no typo */

	(ULONG) &LIBXpkExamine,
	(ULONG) &LIBXpkPack,
	(ULONG) &LIBXpkUnpack,
	(ULONG) &LIBXpkOpen,
	(ULONG) &LIBXpkRead,
	(ULONG) &LIBXpkWrite,
	(ULONG) &LIBXpkSeek,
	(ULONG) &LIBXpkClose,
	(ULONG) &LIBXpkQuery,
	(ULONG) &LIBXpkAllocObject,
	(ULONG) &LIBXpkFreeObject,
	(ULONG) &LIBXpkPrintFault,
	(ULONG) &LIBXpkFault,
	(ULONG) &LIBXpkPassRequest,
	0xFFFFFFFF
};

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

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

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

static const struct Resident RomTag = {
	RTC_MATCHWORD,
	&RomTag,
	&RomTag + 1,
	RTF_PPC | RTF_AUTOINIT,
	VERSION,
	NT_LIBRARY,
	0,
	LIBNAME,
	&LibVersion[6],
	&LibInitStruct
};


ULONG __amigappc__ = 1;

static void LocaleStrings(STRPTR * string, ULONG start, ULONG count)
{
  while(count--)
  {
    *string = GetCatalogStr(Catalog, start++, *string);
    ++string;
  }
}

static void LibCleanup(void)
{
  if(LocaleBase)
  {
    if(Catalog) CloseCatalog(Catalog);
    CloseLibrary((struct Library *) LocaleBase);
  }
  if(GadToolsBase)
    CloseLibrary(GadToolsBase);
  if(UtilityBase)
    CloseLibrary((struct Library *) UtilityBase);
  if(IntuitionBase)
    CloseLibrary((struct Library *) IntuitionBase);
  if(DOSBase)
    CloseLibrary((struct Library *) DOSBase);
}

BPTR LibExpunge(struct Library *xpkLib)
{
	if ((xpkLib->lib_OpenCnt == 0))
	{
		Remove(&xpkLib->lib_Node);
		LibCleanup();

		FreeMem((APTR)((ULONG)(xpkLib) - (ULONG)(xpkLib->lib_NegSize)),
				xpkLib->lib_NegSize + xpkLib->lib_PosSize);

		return SegList;
	} else xpkLib->lib_Flags |= LIBF_DELEXP;

	return 0L;
}

struct Library *LibInit(struct Library *xpkLib, BPTR seglist, struct ExecBase *sysbase)
{
	SysBase = sysbase;
	SegList = seglist;

	if((DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
	{
	  if((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)))
	  {
	    if((UtilityBase = (struct UtilityBase *) OpenLibrary("utility.library", 37)))
	    {
	      if((GadToolsBase = OpenLibrary("gadtools.library", 37)))
	      {
	        if((LocaleBase = (struct LocaleBase *) OpenLibrary("locale.library", 38)))
	        {
	          if((Catalog = OpenCatalog(0, "xpkmaster.catalog", OC_Version, 2, TAG_DONE)))
	          {
	            LocaleStrings(strings, LOCALE_STRINGSTART, LOCALE_STRINGCNT);
	            LocaleStrings(XpkErrs, LOCALE_ERRSTRINGSTART, LOCALE_ERRSTRINGCNT);
	          }
	        }

	        xpkLib->lib_Revision = REVISION;

	        return (XpkBase = xpkLib);
	      }
	    }
	  }
	}

	LibCleanup();
	return NULL;
}

struct Library *LIB_Open(void)
{
	struct Library *xpkLib = (struct Library *)REG_A6;

	xpkLib->lib_Flags &= ~LIBF_DELEXP;
	xpkLib->lib_OpenCnt++;

	return xpkLib;
}

BPTR LIB_Close(void)
{
	struct Library *xpkLib = (struct Library *)REG_A6;

	if (xpkLib->lib_OpenCnt > 0) xpkLib->lib_OpenCnt--;

	if ((xpkLib->lib_OpenCnt == 0) && (xpkLib->lib_Flags & LIBF_DELEXP))
		return LibExpunge(xpkLib);

	return 0L;
}

BPTR LIB_Expunge(void)
{
	struct Library *xpkLib = (struct Library *)REG_A6;

	return LibExpunge(xpkLib);
}

