/*
**	AIFF DataType
**
**	Written by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
**		Public domain
**
** :ts=4
*/

#include "Data.h"

	// Mandatory library routines

struct ClassBase * __asm __saveds	LibInit(register __a0 BPTR LibSegment,register __d0 struct ClassBase *ClassBase,register __a6 struct ExecBase *ExecBase);
struct ClassBase * __asm __saveds	LibOpen(register __a6 struct ClassBase *Base);
BPTR __asm __saveds					LibExpunge(register __a6 struct ClassBase *Base);
BPTR __asm __saveds					LibClose(register __a6 struct ClassBase *Base);
LONG __asm __saveds					LibNull(register __a6 struct ClassBase *Base);

	// The only user-callable routine

Class * __asm __saveds				GetClassEngine(register __a6 struct ClassBase *ClassBase);

	// Library header data, defined in "RomTag.asm"

extern UWORD __far	LibVersion,
					LibRevision;
extern UBYTE __far	LibName[],
					LibID[];

	// Vector initialization table

APTR LibVectors[] =
{
	LibOpen,
	LibClose,
	LibExpunge,
	LibNull,

	GetClassEngine,

	(APTR)-1
};

	// Library initialization table for MakeLibrary()

struct { ULONG DataSize; APTR Table; APTR Data; struct ClassBase * (*Init)(); } __aligned LibInitTab =
{
	sizeof(struct ClassBase),
	LibVectors,
	NULL,
	LibInit
};

	/* AsyncStreamHandler():
	 *
	 *	A special stream handler for use with iffparse.library and the
	 *	asyncio routines.
	 */

STATIC LONG __saveds __asm
AsyncStreamHandler(register __a0 struct Hook *Hook,register __a2 struct IFFHandle *Handle,register __a1 struct IFFStreamCmd *ActionPkt)
{
	struct AsyncFile	*Stream		= (struct AsyncFile *)Handle -> iff_Stream;
	LONG				 Bytes		= ActionPkt -> sc_NBytes;

	switch(ActionPkt -> sc_Command)
	{
		case IFFCMD_READ:

			return(ReadAsync(Stream,ActionPkt -> sc_Buf,Bytes) != Bytes);

		case IFFCMD_WRITE:

			return(WriteAsync(Stream,ActionPkt -> sc_Buf,Bytes) != Bytes);

		case IFFCMD_SEEK:

			return(SeekAsync(Stream,Bytes,MODE_CURRENT) == -1);

		default:

			return(0);
	}
}

	/* LibInit():
	 *
	 *	Initialize the library.
	 */

struct ClassBase * __asm __saveds
LibInit(register __a0 BPTR LibSegment,register __d0 struct ClassBase *ClassBase,register __a6 struct ExecBase *ExecBase)
{
		// Set up the header data

	ClassBase -> LibNode . lib_Node . ln_Type	= NT_LIBRARY;
	ClassBase -> LibNode . lib_Node . ln_Name	= LibName;
	ClassBase -> LibNode . lib_Flags			= LIBF_CHANGED | LIBF_SUMUSED;
	ClassBase -> LibNode . lib_Version			= LibVersion;
	ClassBase -> LibNode . lib_Revision			= LibRevision;
	ClassBase -> LibNode . lib_IdString			= LibID;

		// Remember the segment pointer

	Segment = LibSegment;

		// Remember the exec library base pointer

	SysBase = ExecBase;

		// Initialize the shared data access semaphore

	InitSemaphore(&LockSemaphore);

		// Now take care of the IFFHandle I/O hook.

	AsyncHook . h_Entry = (HOOKFUNC)AsyncStreamHandler;

	return(ClassBase);
}

	/* LibOpen(register __a6 struct ClassBase *ClassBase):
	 *
	 *	Open the library, as called via OpenLibrary()
	 */

struct ClassBase * __asm __saveds
LibOpen(register __a6 struct ClassBase *ClassBase)
{
		// Increment usage count

	ClassBase -> LibNode . lib_OpenCnt++;

		// Prevent delayed expunge

	ClassBase -> LibNode . lib_Flags &= ~LIBF_DELEXP;

		// Is this the first initialization?

	if(ClassBase -> LibNode . lib_OpenCnt == 1)
	{
			// We are going to modify data while in multitasking,
			// so watch out

		ObtainSemaphore(&LockSemaphore);

			// Open libraries & classes

		if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",39))
		{
			if(IntuitionBase = OpenLibrary("intuition.library",39))
			{
				if(UtilityBase = OpenLibrary("utility.library",39))
				{
					if(IFFParseBase = OpenLibrary("iffparse.library",39))
					{
						if(DataTypesBase = OpenLibrary("datatypes.library",39))
						{
							if(SuperClassBase = OpenLibrary("datatypes/sound.datatype",39))
							{
									// Create a new class

								if(SoundClass = MakeClass(ClassBase -> LibNode . lib_Node . ln_Name,SOUNDDTCLASS,NULL,NULL,NULL))
								{
									extern Object * __saveds __asm ClassDispatch(register __a0 Class *class,register __a2 Object *object,register __a1 Msg msg);

										// Link the class dispatcher into it
										// and keep a pointer to the library
										// base

									SoundClass -> cl_Dispatcher . h_Entry	= (HOOKFUNC)ClassDispatch;
									SoundClass -> cl_UserData				= (ULONG)ClassBase;

										// Make the class publicly available

									AddClass(SoundClass);

										// Release the lock

									ReleaseSemaphore(&LockSemaphore);

										// Return the library base pointer

									return(ClassBase);
								}

									// Clean up...

								CloseLibrary(SuperClassBase);

								SuperClassBase = NULL;
							}

							CloseLibrary(DataTypesBase);

							DataTypesBase = NULL;
						}

						CloseLibrary(IFFParseBase);

						IFFParseBase = NULL;
					}

					CloseLibrary(UtilityBase);

					UtilityBase = NULL;
				}

				CloseLibrary(IntuitionBase);

				IntuitionBase = NULL;
			}

			CloseLibrary(DOSBase);

			DOSBase = NULL;
		}

			// Release the lock

		ReleaseSemaphore(&LockSemaphore);

			// No success

		return(NULL);
	}
	else
		return(ClassBase);
}

	/* LibExpunge(register __a6 struct ClassBase *ClassBase):
	 *
	 *	Expunge the library, remove it from memory
	 */

BPTR __asm __saveds
LibExpunge(register __a6 struct ClassBase *ClassBase)
{
		// No more callers have the library open?

	if(!ClassBase -> LibNode . lib_OpenCnt && Segment)
	{
			// Remember the segment pointer, so it can be unloaded

		BPTR TempSegment = Segment;

			// Remove the library from the public list

		Remove(ClassBase);

			// Free the vector table and the library data

		FreeMem((BYTE *)ClassBase - ClassBase -> LibNode . lib_NegSize,ClassBase -> LibNode . lib_NegSize + ClassBase -> LibNode . lib_PosSize);

			// Return the segment pointer

		return(TempSegment);
	}
	else
	{
			// Expunge it later

		ClassBase -> LibNode . lib_Flags |= LIBF_DELEXP;

			// Can't close yet...

		return(NULL);
	}
}

	/* LibClose(register __a6 struct ClassBase *ClassBase):
	 *
	 *	Close the library, as called by CloseLibrary()
	 */

BPTR __asm __saveds
LibClose(register __a6 struct ClassBase *ClassBase)
{
		// Decrement usage count

	if(ClassBase -> LibNode . lib_OpenCnt)
		ClassBase -> LibNode . lib_OpenCnt--;

		// No more users?

	if(!ClassBase -> LibNode . lib_OpenCnt && SysBase)
	{
			// We are going to modify shared data,
			// so watch out

		ObtainSemaphore(&LockSemaphore);

			// Clean up...

		if(SoundClass)
		{
			RemoveClass(SoundClass);

			FreeClass(SoundClass);

			SoundClass = NULL;
		}

		if(SuperClassBase)
		{
			CloseLibrary(SuperClassBase);

			SuperClassBase = NULL;
		}

		if(DataTypesBase)
		{
			CloseLibrary(DataTypesBase);

			DataTypesBase = NULL;
		}

		if(UtilityBase)
		{
			CloseLibrary(UtilityBase);

			UtilityBase = NULL;
		}

		if(IFFParseBase)
		{
			CloseLibrary(IFFParseBase);

			IFFParseBase = NULL;
		}

		if(IntuitionBase)
		{
			CloseLibrary(IntuitionBase);

			IntuitionBase = NULL;
		}

		if(DOSBase)
		{
			CloseLibrary(DOSBase);

			DOSBase = NULL;
		}

			// Release the lock

		ReleaseSemaphore(&LockSemaphore);

			// Can we remove ourselves?

		if(ClassBase -> LibNode . lib_Flags & LIBF_DELEXP)
			return(LibExpunge(ClassBase));
	}

	return(NULL);
}

	/* LibNull(register __a6 struct ClassBase *ClassBase):
	 *
	 *	Mandatory dummy function
	 */

LONG __asm __saveds
LibNull(register __a6 struct ClassBase *ClassBase)
{
	return(NULL);
}

	/* GetClassEngine(register __a6 struct ClassBase *ClassBase):
	 *
	 *	Get access to the class this library implements.
	 */

Class * __asm __saveds
GetClassEngine(register __a6 struct ClassBase *ClassBase)
{
	Class *class;

		// Access shared data

	ObtainSemaphore(&LockSemaphore);

		// Remember the class pointer

	class = SoundClass;

		// Release the lock

	ReleaseSemaphore(&LockSemaphore);

		// Return the pointer

	return(class);
}
