#include "sysinc.h"
#include "mgl/gl.h"
#include <math.h>
#include "mglut.library_rev.h"
#include "interfaces/glut.h"
#include <exec/exec.h>


#include "glut_vectors.c"


struct Library *MiniGLBase;
struct MiniGLIFace *IMiniGL;

static struct Library *__IntuitionBase;
struct IntuitionIFace *__IIntuition;

static struct Device *__TimerBase;
struct TimerIFace *__ITimer;

APTR segList = 0;

struct Library *
libOpen(struct LibraryManagerInterface *Self, ULONG version)
{
	struct Library *libBase = Self->Data.LibBase;

	dprintf("In libOpen\n");

	/* Add up the open count */
	libBase->lib_OpenCnt++;

	/* Clear pending expunge */
	libBase->lib_Flags &= ~LIBF_DELEXP;

	return libBase;
}

APTR 
libExpunge(struct LibraryManagerInterface *Self)
{
	struct Library *libBase = Self->Data.LibBase;

	/* Check if we're still open */
	if (libBase->lib_OpenCnt)
	{
		/* We are, delay the expunge */
		libBase->lib_Flags |= LIBF_DELEXP;
		return 0;
	}
	
	if (IMiniGL)
	{
		IExec->DropInterface((struct Interface *)IMiniGL);
		IMiniGL = 0;
	}
		
	if (MiniGLBase)
	{
		IExec->CloseLibrary(MiniGLBase);
		MiniGLBase = 0;
	}
		
	if (__IIntuition)
	{
		IExec->DropInterface((struct Interface *)__IIntuition);
		__IIntuition = 0;
	}
		
	if (__IntuitionBase)
	{
		IExec->CloseLibrary(__IntuitionBase);
		__IntuitionBase = 0;
	}
	
	if (__ITimer)
	{
		IExec->DropInterface((struct Interface *)__ITimer);
		__ITimer = 0;
	}
	
	/* No one uses us, so really expunge. Start by removing us from the library list */
	IExec->Remove((struct Node *)libBase);

	/* Delete ourselves */
	IExec->DeleteLibrary(libBase);
	IExec->Release();

	/* Finally, return the handle so whoever loaded us into memory can get rid of us */
	return segList;
}

APTR 
libClose(struct LibraryManagerInterface *Self)
{
	struct Library *libBase = Self->Data.LibBase;

	/* Make the close count */
	libBase->lib_OpenCnt--;

	/* If we're still open, do nothing */
	if (libBase->lib_OpenCnt > 0)
		return 0;

	/* Otherwise, if an expunge is pending, execute it now */
	if (libBase->lib_Flags & LIBF_DELEXP)
	{
		return libExpunge(Self);
	}

	return 0;
}

struct Library *
libInit(struct Library *libBase, APTR seglist, struct Interface *exec)
{
	/* Initialize the library base */
	dprintf("Initializing library base %p, IExec = %p\n", libBase, exec);
	libBase->lib_Node.ln_Type = NT_LIBRARY;
	libBase->lib_Node.ln_Pri  = 0;
	libBase->lib_Node.ln_Name = (char *)"mglut.library";
	libBase->lib_Flags        = LIBF_SUMUSED|LIBF_CHANGED;
	libBase->lib_Version      = (int)VERSION;
	libBase->lib_Revision     = (int)REVISION;
	libBase->lib_IdString     = (char *)VSTRING;

	segList = seglist;
	IExec = (struct ExecIFace *)exec;

	SysBase = IExec->Data.LibBase;
	
	DOSBase = IExec->OpenLibrary("dos.library", 0);
	IDOS = (struct DOSIFace *)IExec->GetInterface(DOSBase, "main", 1, NULL);

	MiniGLBase = IExec->OpenLibrary("minigl.library", 0);
	if (!MiniGLBase)
		return NULL;
	
	IMiniGL = (struct MiniGLIFace *)IExec->GetInterface(MiniGLBase, "main", 1,
										 NULL);
	if (!IMiniGL)
	{
		IExec->CloseLibrary(MiniGLBase);
		
		return NULL;
	}
	
	/* These will never fail */
	__IntuitionBase = IExec->OpenLibrary("intuition.library", 0);
	__IIntuition = (struct IntuitionIFace *)IExec->GetInterface(__IntuitionBase, "main", 1, NULL);
	__TimerBase = (struct Device *)IExec->FindName(
				&((struct ExecBase *)SysBase)->DeviceList, "timer.device");
	__ITimer = (struct TimerIFace *)IExec->GetInterface(
								(struct Library *)__TimerBase, "main", 1, NULL);

	dprintf("Done (__ITimer = %p)\n", __ITimer);

	/* Return libBase here to indicate success, or 0 if something failed */
	return libBase;
}

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

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

uint32 glut_Obtain(struct GlutIFace *Self)
{
	return ++Self->Data.RefCount;
}

uint32 glut_Release(struct GlutIFace *Self)
{
	uint32 ref;

	ref = --Self->Data.RefCount;
        
	if (Self->Data.RefCount == 0 && (Self->Data.Flags & IFLF_CLONED))
		Self->Expunge();

	return ref; 
}

void glut_Expunge(struct GlutIFace *Self)
{
	void *base = (void *)GET_INSTANCE(Self);
	
	IExec->FreeVec(base);
}

struct Interface *glut_Clone(struct GlutIFace *Self)
{
	uint8 *base;
	uint32 size = Self->Data.PositiveSize + Self->Data.NegativeSize;
	struct GlutIFace *clone;
	
	dprintf("Positive = %ld, Negative = %ld, total = %ld\n", 
		Self->Data.PositiveSize, Self->Data.NegativeSize, size);
		
	base = (uint8 *)IExec->AllocVec(size, MEMF_PUBLIC|MEMF_CLEAR);
	
	if (!base)
		return 0;
	
	clone = (struct GlutIFace *) (base + Self->Data.NegativeSize);
	
	/* Copy the functions */
	IExec->CopyMem((APTR)Self, (APTR)clone, Self->Data.PositiveSize);

	clone->Data.Flags |= IFLF_CLONED;
	clone->Data.RefCount = 0;
	
	return (struct Interface *)clone;
}

/* Manager interface vectors */
void *manager_vectors[] =
{
	(void *)defaultObtain,
	(void *)defaultRelease,
	(void *)0,
	(void *)0,
	(void *)libOpen,
	(void *)libClose,
	(void *)libExpunge,
	(void *)0,
	(void *)-1,
};

/* "__library" interface tag list */
struct TagItem managerTags[] =
{
	{MIT_Name,             (ULONG)"__library"},
	{MIT_VectorTable,      (ULONG)manager_vectors},
	{MIT_Version,          1},
	{TAG_DONE,             0}
};

struct TagItem mainTags[] =
{
	{MIT_Name,				(ULONG)"main"},
	{MIT_DataSize,			sizeof(struct GLUTcontext_t)},
	{MIT_Flags,				IFLF_PRIVATE},
	{MIT_VectorTable,		(ULONG)main_vectors},
	{MIT_Version,			1},
	{TAG_DONE,				0}
};

uint32 libInterfaces[] =
{
	(uint32)managerTags,
	(uint32)mainTags,
	0
};

struct TagItem libCreateTags[] =
{
	{CLT_DataSize,		(uint32)sizeof(struct Library)},
	{CLT_InitFunc,		(uint32)libInit},
	{CLT_Interfaces,	(uint32)libInterfaces},
	{TAG_DONE,			0},
};
	
struct Resident glut_res __attribute__((used)) =
{
	RTC_MATCHWORD,
	&glut_res,
	&glut_res+1,
	RTF_AUTOINIT|RTF_NATIVE|RTF_COLDSTART,
	(int)VERSION,
	NT_LIBRARY,
	0,
	(char *)"mglut.library",
	(char *)VSTRING,
	libCreateTags
};

void _start(void)
{
}

