/*
** $PROJECT: class library template
**
** $VER: classbase.c 40.7 (11.09.97)
**
** $AUTHOR: Stefan Ruppert , Windthorststrasse 5 , 65439 Floersheim , GERMANY
**
** $COPYRIGHT: (C) Copyright 1996,1997 by Stefan Ruppert, All Rights Reserved !
**
** $EMAIL: ruppert@amigaworld.com
**
** $HISTORY:
**
** 11.09.97 : 040.007 : added global __UtilityBase and SysBase for GNU/C link libraries
** 19.08.97 : 040.006 : UserClassBaseOpen() and UserClassBaseClose() added
** 04.08.97 : 040.005 : added USE_VERSTAG support
** 05.07.97 : 040.004 : added missing entry() function to avoid crashes,
**                      if the library is executed
** 20.06.97 : 040.003 : added gcc support from Gunther Nikl
** 09.11.96 : 040.002 : changed to new ClassLibrary structure from the DevCD
** 15.09.96 : 040.001 : + now uses semaphore for shared data as in AIFFdtc
**                        from Olaf Barthel
**                      + OpenLibrary() now in LibOpen() (suggestion from
**                        Stefan Becker)
**                      + added ROMTag and removed last asm file.
*/

/* disable debug output */
#undef DEBUG

/*FS*//* ------------------------------- includes ------------------------------- */

#include "classbase.h"

#include <exec/initializers.h>
#include <exec/resident.h>
/*FE*/

/*FS*//* ------------------------------ prototypes ------------------------------ */

LibCall struct Library *LibInit (REGD0 struct ClassBase *cb GREG(d0), REGA0 BPTR seglist GREG(a0), REGA6 struct Library * sysbase GREG(a6));
LibCall LONG LibOpen (REGA6 struct ClassBase *cb GREG(a6));
LibCall LONG LibClose (REGA6 struct ClassBase *cb GREG(a6));
LibCall LONG LibExpunge (REGA6 struct ClassBase *cb GREG(a6));
LibCall LONG LibReserved (REGA6 struct ClassBase *cb GREG(a6));
LibCall Class *ObtainEngine (REGA6 struct ClassBase *cb GREG(a6));

static Class *initClass(struct ClassBase *cb);
static BOOL OpenLibraries(struct ClassBase *cb);
static void CloseLibraries(struct ClassBase *cb);

extern ClassCall ULONG dispatcher(REGA0 Class *cl GREG(a0),REGA2 Object *obj GREG(a1),REGA1 Msg msg GREG(a1));

#ifdef USERCLASSBASEINIT
extern BOOL UserClassBaseInit(struct ClassBase *cb);
extern void UserClassBaseExpunge(struct ClassBase *cb);
#endif
#ifdef USERCLASSBASEOPEN
extern BOOL UserClassBaseOpen(struct ClassBase *cb);
extern void UserClassBaseClose(struct ClassBase *cb);
#endif
/*FE*/

/*FS*//* --------------------------------- data --------------------------------- */

/* libraries shouldn't started */
LONG entry(void)
{
   return 0;
}

static const ALIGNED UBYTE LibName[] = NAME;
static const ALIGNED UBYTE LibId[]   = VSTRING;

static const APTR LibFuncTable[] =
{
   LibOpen,
   LibClose,
   LibExpunge,
   LibReserved,
   ObtainEngine,

   USERCLASSBASEFUNCTABLE

   (APTR) -1
};

static const ALIGNED ULONG LibInitTable[4] = {
   (ULONG) sizeof(struct ClassBase),
   (ULONG) LibFuncTable,
   (ULONG) NULL,
   (ULONG) LibInit
   };

static const ALIGNED struct Resident romtag =
{
   RTC_MATCHWORD,
   (APTR) &romtag,
   (APTR) (&romtag + 1),
   RTF_AUTOINIT,
   VERSION,
   NT_LIBRARY,
   0,
   (char *) LibName,
   (char *) LibId,
   (ULONG *) LibInitTable
};

#ifdef USE_VERSTAG
#ifndef LIBVERSTAG
#error "You specified USE_VERSTAG, but never defined LIBVERSTAG"
#else
static const STRPTR verstag = LIBVERSTAG;
#endif
#endif

#ifdef __GNUC__
struct Library *__UtilityBase;
#undef SysBase
struct ExecBase *SysBase;
#define SysBase cb->cb_SysBase
#endif
/*FE*/

/* ---------------------------- implementation ---------------------------- */

/*FS*/ static Class *initClass(struct ClassBase *cb)
{
   Class *cl;

   if((cl = MakeClass(CLASSNAME,SUPERCLASSNAME,NULL,INSTANCESIZE,0)) != NULL)
   {
      DB(("dispatcher : %lx,size : %ld\n",dispatcher,INSTANCESIZE));

      cl->cl_Dispatcher.h_Entry = (HOOKFUNC) dispatcher;
      cl->cl_UserData = (ULONG) cb;

      AddClass(cl);
   }

   DB(("class : %lx\n",cl));

   return(cl);
}
/*FE*/
/*FS*/ static BOOL OpenLibraries(struct ClassBase *cb)
{
   /* system libraries must be opened correctly, because a pre 3.0 check
    * was done in LibInit()
    */

   IntuitionBase = OpenLibrary("intuition.library",39);
   UtilityBase   = OpenLibrary("utility.library",  39);

#ifdef __GNUC__
   /* support for libnix */
   __UtilityBase = UtilityBase;
#endif

#ifdef USE_DOSLIB
   DOSBase       = OpenLibrary("dos.library",      39);
#endif
#ifdef USE_GFXLIB
   GfxBase       = OpenLibrary("graphics.library", 39);
#endif

#ifdef USE_DATATYPESLIB
   if((DataTypesBase = OpenLibrary("datatypes.library",39)) == NULL)
      return FALSE;
#endif

#ifdef USE_IFFPARSELIB
   if((IFFParseBase  = OpenLibrary("iffparse.library", 39)) == NULL)
      return FALSE;
#endif

#ifdef USE_LOCALELIB
   if((LocaleBase  = OpenLibrary("locale.library", 38)) == NULL)
      return FALSE;
#endif

   return TRUE;
}
/*FE*/
/*FS*/ static void CloseLibraries(struct ClassBase *cb)
{
   CloseLibrary((struct Library *) UtilityBase);
   UtilityBase   = NULL;
   CloseLibrary((struct Library *) IntuitionBase);
   IntuitionBase = NULL;

#ifdef USE_DOSLIB
   CloseLibrary((struct Library *) DOSBase);
   DOSBase       = NULL;
#endif

#ifdef USE_GFXLIB
   CloseLibrary((struct Library *) GfxBase);
   GfxBase       = NULL;
#endif

#ifdef USE_DATATYPESLIB
   CloseLibrary((struct Library *) DataTypesBase);
   DataTypesBase = NULL;
#endif

#ifdef USE_IFFPARSELIB
   CloseLibrary((struct Library *) IFFParseBase);
   IFFParseBase  = NULL;
#endif

#ifdef USE_LOCALEBASE
   CloseLibrary((struct Library *) LocaleBase);
   LocaleBase = NULL;
#endif
}
/*FE*/

/*FS*/ LibCall Class *ObtainEngine (REGA6 struct ClassBase *cb GREG(a6))
{
   /* here we need no semaphore operations, because this function
    * can only be called between a OpenLibrary()/CloseLibrary()
    * call.
    */

   return cb->cb_Lib.cl_Class;
}
/*FE*/

/*FS*/ LibCall struct Library *LibInit (REGD0 struct ClassBase *cb GREG(d0), REGA0 BPTR seglist GREG(a0), REGA6 struct Library * sysbase GREG(a6))
{
   if(sysbase->lib_Version >= 39)
   {
#ifdef __GNUC__
#undef SysBase
      SysBase = (struct ExecBase *) sysbase;
#define SysBase cb->cb_SysBase
#endif
      cb->cb_SegList = seglist;
      cb->cb_SysBase = sysbase;
      cb->cb_Lib.cl_Lib.lib_Revision = REVISION;
      cb->cb_Lib.cl_Pad = FALSE;

      InitSemaphore(&cb->cb_Lock);

      return (struct Library *) cb;
   }

   FreeMem((APTR)((ULONG)(cb) - (ULONG)(cb->cb_Lib.cl_Lib.lib_NegSize)), cb->cb_Lib.cl_Lib.lib_NegSize + cb->cb_Lib.cl_Lib.lib_PosSize);

   return NULL;
}
/*FE*/
/*FS*/LibCall LONG LibOpen (REGA6 struct ClassBase *cb GREG(a6))
{
   struct SignalSemaphore *lock= &cb->cb_Lock;
   LONG retval = (LONG) cb;

   ObtainSemaphore(lock);

   /* clear delayed expunge flag */
   cb->cb_Lib.cl_Lib.lib_Flags &= ~LIBF_DELEXP;

   if(++cb->cb_Lib.cl_Lib.lib_OpenCnt == 1)
   {
      /* only open libraries directly after LibInit */
      if(cb->cb_Lib.cl_Pad == FALSE && !OpenLibraries(cb))
	 retval = (LONG) NULL;
      else
      {
#ifdef SUPERCLASSLIBRARY
      /* open superclass library */
      if((cb->cb_SuperClassBase = OpenLibrary(SUPERCLASSLIBRARY,SUPERCLASSLIBRARYVERSION)) != NULL)
      {
#endif
	 cb->cb_Lib.cl_Class = initClass(cb);

#ifdef USERCLASSBASEINIT
	 /* call user classbase init function */
	 if(cb->cb_Lib.cl_Class != NULL)
	 {
	    if(cb->cb_Lib.cl_Pad == FALSE)
	    {
	       if(!UserClassBaseInit(cb))
	       {
		  CloseLibraries(cb);
		  FreeClass(cb->cb_Lib.cl_Class);
		  cb->cb_Lib.cl_Class = NULL;
	       }
	    }
	 }
#endif
#ifdef USERCLASSBASEOPEN
	 /* call user classbase open function */
	 if(cb->cb_Lib.cl_Class != NULL)
	 {
	    if(!UserClassBaseOpen(cb))
	    {
	       CloseLibraries(cb);
	       FreeClass(cb->cb_Lib.cl_Class);
	       cb->cb_Lib.cl_Class = NULL;
	    }
	 }
#endif

#ifdef SUPERCLASSLIBRARY
	 if(cb->cb_Lib.cl_Class == NULL)
	 {
	    CloseLibrary(SuperClassBase);
	    SuperClassBase = NULL;
	 }
      } else
      {
	 CloseLibraries(cb);
	 retval = (LONG) NULL;
      }
#endif
      }
   }

   if(retval == (LONG) NULL)
      cb->cb_Lib.cl_Lib.lib_OpenCnt--;
   else
   {
      /* first LibOpen after LibInit finished */
      if(cb->cb_Lib.cl_Pad == FALSE)
	 cb->cb_Lib.cl_Pad = TRUE;
   }

   ReleaseSemaphore(lock);

   DB(("return = 0x%lx\n",retval));

   return retval;
}
/*FE*/
/*FS*/LibCall LONG LibClose (REGA6 struct ClassBase *cb GREG(a6))
{
   LONG retval = NULL;

   DB(("cnt = %ld, class = 0x%lx\n",cb->cb_Lib.cl_Lib.lib_OpenCnt,cb->cb_Lib.cl_Class));

   if(cb->cb_Lib.cl_Lib.lib_OpenCnt > 0)
   {
      if(--cb->cb_Lib.cl_Lib.lib_OpenCnt == 0)
      {
	 ObtainSemaphore(&cb->cb_Lock);

	 if(cb->cb_Lib.cl_Class != NULL && FreeClass(cb->cb_Lib.cl_Class))
	    cb->cb_Lib.cl_Class = NULL;

#ifdef SUPERCLASSLIBRARY
	 if(cb->cb_Lib.cl_Class == NULL)
	 {
	    CloseLibrary(SuperClassBase);
	    SuperClassBase = NULL;
	 }
#endif

#ifdef USERCLASSBASEOPEN
	 if(cb->cb_Lib.cl_Class == NULL)
	 {
	    UserClassBaseClose(cb);
	 }
#endif

	 if(cb->cb_Lib.cl_Class == NULL && (cb->cb_Lib.cl_Lib.lib_Flags & LIBF_DELEXP))
	 {
#ifdef USERCLASSBASEINIT
	    UserClassBaseExpunge(cb);
#endif
	    CloseLibraries(cb);
	 }

	 ReleaseSemaphore(&cb->cb_Lock);
      }
   }

   DB(("cnt = %ld, class = 0x%lx\n",cb->cb_Lib.cl_Lib.lib_OpenCnt,cb->cb_Lib.cl_Class));

   if(cb->cb_Lib.cl_Lib.lib_Flags & LIBF_DELEXP)
      retval = LibExpunge (cb);

   DB(("return = 0x%lx\n",retval));

   return retval;
}
/*FE*/
/*FS*/LibCall LONG LibExpunge(REGA6 struct ClassBase *cb GREG(a6))
{
   BPTR seg = NULL;

   DB(("cnt = %ld, class = 0x%lx\n",cb->cb_Lib.cl_Lib.lib_OpenCnt,cb->cb_Lib.cl_Class));

   if(cb->cb_Lib.cl_Lib.lib_OpenCnt == 0)
   {
      seg = cb->cb_SegList;

      Remove((struct Node *) cb);

      FreeMem((APTR)((ULONG)(cb) - (ULONG)(cb->cb_Lib.cl_Lib.lib_NegSize)), cb->cb_Lib.cl_Lib.lib_NegSize + cb->cb_Lib.cl_Lib.lib_PosSize);
   } else
   {
      cb->cb_Lib.cl_Lib.lib_Flags |= LIBF_DELEXP;
   }

   DB(("return = 0x%lx\n",seg));

   return (LONG) seg;
}
/*FE*/
/*FS*/LibCall LONG LibReserved(REGA6 struct ClassBase *cb GREG(a6))
{
   return 0;
}
/*FE*/

