/****************************************************************************
*
* $RCSfile: LibraryObject.c $
* $Revision: 1.0 $
* $Date: 1997/03/15 06:42:51 $
* $Author: ssolie $
*
*****************************************************************************
*
* Copyright (c) 1997 Software Evolution.  All Rights Reserved.
*
*****************************************************************************
*
* LibraryObject.c -- LibraryObject module
*
* This file contains the functions required to maintain LibraryObject's.
*
* The user is presented with a simple interface in order to use a
* LibraryObject as follows:
*
* 	openLibraries() - Open a set of libraries
*	closeLibraries() - Close a set of libraries
*/
#include "LibraryObject.h"
#include <exec/memory.h>

#include <proto/exec.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>

#include "Debug.h"

/*** Local data types ***/
struct LibraryClass {
	struct Library **libs;	/* array of library pointers */
	UWORD num;				/* number of library pointers */
	UWORD max;				/* maximum number of library pointers */
};


/*** Local constants ***/
const UWORD LIBS_PER_ALLOC	= 2;	/* array expansion factor */
const STRPTR UTILITY_LIB	= "utility.library";
const STRPTR INTUITION_LIB	= "intuition.library";
const STRPTR GRAPHICS_LIB	= "graphics.library";
const STRPTR LOCALE_LIB		= "locale.library";


/*** Local global variables ***/
STATIC struct Library *UtilityBase = NULL;


/*** Local function prototypes ***/
STATIC LibraryObject newLibraryObject(VOID);
STATIC VOID deleteLibraryObject(LibraryObject this);
STATIC struct Library *openLibraryObject(LibraryObject this, STRPTR name,
	ULONG version);



/****** SERL/openLibraries **************************************************
*
*   NAME
*       openLibraries -- Open multiple libraries
*
*   SYNOPSIS
*       lib_obj = openLibraries(tags)
*
*       LibraryObject openLibraries(struct TagItem *);
*
*   FUNCTION
*       This function opens multiple libraries.  If any of the required
*       libraries cannot be opened, the call will fail.
*
*       Control is provided though a standard tags interface.
*
*   TAGS
*       LIB_LibVersion (ULONG) (LIBRARY_MINIMUM)
*       Minimum version of the libraries to follow.
*
*       LIB_LibStatus (ULONG) (LIB_REQUIRED)
*       Status of the libraries to follow.  The possible values are
*       LIB_REQUIRED and LIB_OPTIONAL.
*
*       LIB_LibName (STRPTR)
*       Name of the library to be opened.  Must be followed by LIB_LibBase.
*
*       LIB_LibBase (struct Library **)
*       Pointer to the base of the library to be opened.  The library
*       pointer will be set to the value returned by the OpenLibrary()
*       function.  Must be preceeded by LIB_LibName.
*
*       LIB_Intuition (struct Library **)
*       Pointer to the "intuition.library" base pointer.
*
*       LIB_Graphics (struct Library **)
*       Pointer to the "graphics.library" base pointer.
*
*       LIB_Locale (struct Library **)
*       Pointer to the "locale.library" base pointer.
*
*   INPUTS
*       tags - Array of TagItem's describing what libraries to open.
*
*   RESULT
*       Private library object or NULL on error.
*
*   EXAMPLE
*       struct TagItem tags[] = {
*               {LIB_LibVersion, 37},
*               {LIB_LibStatus, LIB_REQUIRED},
*               {LIB_Intuition, (ULONG)&IntuitionBase},
*               {LIB_LibStatus, LIB_OPTIONAL},
*               {LIB_LibName, "mylib.library"},
*               {LIB_LibBase, (ULONG)&MyLibBase},
*               {TAG_DONE}
*       };
*
*       lib_obj = openLibraries(tags);
*       if ( lib_obj != NULL )  {
*               ...do stuff...
*               closeLibraries(lib_obj);
*       }
*
*   SEE ALSO
*       SERL/closeLibraries()
*
*****************************************************************************
*
*/
LibraryObject openLibraries(struct TagItem tags[])
{
	struct TagItem *tag_state, *tag;
	struct Library **base_pointer;
	struct Library *lib_base;
	LibraryObject lib_obj;
	STRPTR name;
	ULONG version;
	UWORD status;

	D(bug("openLibraries(%08lx)\n", tags));

	if ( tags == NULL )
		return(NULL);

	/*** Create object and set defaults ***/
	lib_obj = newLibraryObject();
	if ( lib_obj == NULL )
		return(NULL);

	version = LIBRARY_MINIMUM;
	status  = LIB_REQUIRED;

	UtilityBase = openLibraryObject(lib_obj, UTILITY_LIB, version);
	if ( UtilityBase == NULL )  {
		closeLibraries(lib_obj);
		return(NULL);
	}

	/*** Sequentially parse the list of tags ***/
	tag_state = tags;
	while ( (tag = NextTagItem(&tag_state)) != NULL )  {
		D(bug("{tag=%08lx, data=%08lx}\n", tag->ti_Tag, tag->ti_Data));

		base_pointer = NULL;
		switch ( tag->ti_Tag )  {
			case LIB_LibVersion:
				version = tag->ti_Data;
				break;
			case LIB_LibStatus:
				status = (UWORD)tag->ti_Data;
				break;
			case LIB_LibName:
				name = (STRPTR)tag->ti_Data;
				tag = NextTagItem(&tag_state);
				if ( tag != NULL && tag->ti_Tag == LIB_LibBase )
					base_pointer = (struct Library**)tag->ti_Data;
				break;
			case LIB_Intuition:
				base_pointer = (struct Library**)tag->ti_Data;
				name = INTUITION_LIB;
				break;
			case LIB_Graphics:
				base_pointer = (struct Library**)tag->ti_Data;
				name = GRAPHICS_LIB;
				break;
			case LIB_Locale:
				base_pointer = (struct Library**)tag->ti_Data;
				name = LOCALE_LIB;
				break;
			default:
				break;
		}

		if ( base_pointer != NULL )  {
			lib_base = openLibraryObject(lib_obj, name, version);
			if ( lib_base == NULL && status == LIB_REQUIRED )  {
				closeLibraries(lib_obj);
				return(NULL);
			}

			*base_pointer = lib_base;
		}
	}

	return(lib_obj);
}


/****** SERL/closeLibraries *************************************************
*
*   NAME
*       closeLibraries -- Close multiple libraries
*
*   SYNOPSIS
*       closeLibraries(lib_obj)
*
*       VOID closeLibraries(LibraryObject lib_obj);
*
*   FUNCTION
*       This function closes multiple libraries.  The libraries are
*       closed using the supplied LibraryObject.
*
*   INPUTS
*       lib_obj - LibraryObject or NULL.
*
*   SEE ALSO
*       SERL/openLibraries()
*
*****************************************************************************
*
*/
VOID closeLibraries(LibraryObject this)
{
	UWORD i;

	D(bug("closeLibraries(%lx)\n", this));

	if ( this != NULL )  {
		for ( i = 0; i < this->num; i++ )
			CloseLibrary(this->libs[i]);
	}

	deleteLibraryObject(this);
}


/*
 * newLibraryObject -- Create a LibraryObject
 *
 * This function is used to create a LibraryObject.  Returns a LibraryObject
 * if successful or NULL on error.
 */
STATIC LibraryObject newLibraryObject(VOID)
{
	LibraryObject this;

	D(bug("newLibraryObject()\n"));

	this = AllocVec(sizeof(struct LibraryClass), MEMF_ANY);
	if ( this != NULL )  {
		this->num = 0;
		this->max = LIBS_PER_ALLOC;
		this->libs = AllocVec((ULONG)this->max * sizeof(struct Library*),
			MEMF_CLEAR);
	}

	return(this);
}


/*
 * deleteLibraryObject -- Delete a LibraryObject
 *
 * This function is used to delete a LibraryObject and free any resources.
 * Safe to call with a NULL.
 */
STATIC VOID deleteLibraryObject(LibraryObject this)
{
	D(bug("deleteLibraryObject(%08lx)\n", this));

	if ( this != NULL )  {
		FreeVec(this->libs);
		FreeVec(this);
	}
}


/*
 * openLibraryObject -- Open a LibraryObject library
 *
 * This function will open a system library within the context of a
 * LibraryObject.  The LibraryObject will automatically close any
 * libraries that were opened.  Returns a pointer to the newly opened
 * system library if successful or NULL on error.
 */
STATIC struct Library *openLibraryObject(LibraryObject this, STRPTR name,
	ULONG version)
{
	struct Library **new_libs;
	struct Library *library;
	UWORD new_max;

	/*** Ensure there is space for the resource tracking ***/
	if ( this->num == this->max )  {
		new_max = this->max + LIBS_PER_ALLOC;
		new_libs = AllocVec((ULONG)new_max * sizeof(struct Library*),
			MEMF_CLEAR);
		if ( new_libs == NULL )
			return(NULL);

		CopyMem(this->libs, new_libs,
			(ULONG)this->max * sizeof(struct Library*));
		FreeVec(this->libs);
		this->libs = new_libs;
		this->max = new_max;
	}

	/*** Attempt to open the library and return the result ***/
	library = OpenLibrary(name, version);
	if ( library != NULL )  {
		this->libs[this->num] = library;
		this->num++;
	}

	return(library);
}
