/*---------------------------------------------------------------------
 * $Date: 1994-08-09 17:35:22 $             $Revision: 1.1 $
 *---------------------------------------------------------------------
 *
 *
 *             Copyright (c) 1992, Visual Edge Software Ltd.
 *
 * ALL  RIGHTS  RESERVED.  Permission  to  use,  copy,  modify,  and
 * distribute  this  software  and its documentation for any purpose
 * and  without  fee  is  hereby  granted,  provided  that the above
 * copyright  notice  appear  in  all  copies  and  that  both  that
 * copyright  notice and this permission notice appear in supporting
 * documentation,  and that  the name of Visual Edge Software not be
 * used  in advertising  or publicity  pertaining to distribution of
 * the software without specific, written prior permission. The year
 * included in the notice is the year of the creation of the work.
 *-------------------------------------------------------------------*/
/*------------------------------------------------------------------------
 * Method registration and dispatch for swidget interfaces.
 * This version supports the default macros and generated code,
 * using a simple (and fast) table lookup approach.
 *------------------------------------------------------------------------*/

#ifdef XT_CODE
#include "UxXt.h"
#else				/* XT_CODE */
#include "UxLib.h"
#include "method.h"
#endif				/* XT_CODE */


#ifdef DESIGN_TIME
#include "veos.h"
#include "swidget.h"
#endif

/* Global environment variable supplied for the user's convenience */
Environment UxEnv = {NO_EXCEPTION};

static char **Names;		/* All method names as registered */
static int NameLen = 0;		/* Gross length of names array	 */
static int NumNames = 0;	/* Num Elements in names array	 */

static void ***MethodTable;	/* All method pointers 		 */
static int *BaseTable;		/* Base class id's by class id. */
static int NumClasses = 0;	/* Active width of method table	 */
static int *MethodCounts;	/* Height by row in method table */

#ifdef _NO_PROTO
static int UndefinedMethod();
#else				/* _NO_PROTO */
static int UndefinedMethod(void);
#endif				/* _NO_PROTO */

#ifdef XT_CODE
static XContext xcontext_mid = 0;
#endif				/* XT_CODE */

/*------------------------------------------------------------------------
 * NAME: UxMethodLookup
 * INPUT:	swidget sw;	-- a swidget, usually an interface swidget
 *		int	mid;	-- a method id from UxMethodRegister
 * RETURNS:	Pointer to method function for this method.
 * DESCRIPTION:
 *	This is the heart of virtual method lookup.
 *	The design-time routing functions and runtime macros
 *	use this function to fnd the implementation of a method.
 *	The return value is the actual function pointer
 *	for the implementation of the method associated with `sw's
 *	interface class.
 *
 * LAST REV:	Feb 93	fix3910		Add support for subclassing.
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
void *
UxMethodLookup(sw, mid, mname)
swidget sw;
int mid;
char *mname;
#else				/* _NO_PROTO */
void *
UxMethodLookup(swidget sw, int mid, char *mname)
#endif				/* _NO_PROTO */
{
    int cid = UxGetClassCode(sw);

    if (mid < 0) {
	mid = UxMessageIndex(mname);
	if (mid < 0) {
	    return 0;
	}
    }
    /*-----------------------------------------------------------
     * The method may be registered on this class or a base class.
     *-----------------------------------------------------------*/

    while (cid > -1 && cid < NumClasses) {
	if (mid < MethodCounts[cid] && MethodTable[cid][mid]) {
	    return MethodTable[cid][mid];
	}
	cid = BaseTable[cid];
    }
#ifndef DESIGN_TIME
    return (void *) UndefinedMethod;
#else
    return 0;
#endif
}

/*------------------------------------------------------------------------
 * NAME: UxNewClassId
 * RETURNS:	a unique integer each time called.
 * DESCRIPTION:
 *	This is to get a unique id for each interface class registered.
 *	The result is used for calls to UxMethodRegister, and
 *	is associated with each instance of the interface class:
 *	it is returned by UxGetClassId(sw) for each interface swidget sw
 *	that is an instance of the interface it represents.
 *
 *	We have no subclassing.  When it comes, parent class id's
 *	will be supplied in this call, and used to initialize the
 *	classes' method table with its parent's versions.
 *
 * LAST REV:	Feb 93	fix3910		Add support for subclassing.
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
int
UxNewClassId()
#else				/* _NO_PROTO */
int
UxNewClassId(void)
#endif				/* _NO_PROTO */
{
    static int GrossClasses = 0;/* Gross width of method table  */

    int cid = NumClasses++;

    if (GrossClasses < NumClasses) {
	if (GrossClasses) {
	    MethodTable = (void ***)
		UxRealloc(MethodTable,
			  (GrossClasses += 10) * sizeof(void **));
	    MethodCounts = (int *)
		UxRealloc(MethodCounts,
			  (GrossClasses * sizeof(int)));
	    BaseTable = (int *)
		UxRealloc(BaseTable,
			  GrossClasses * sizeof(int));
	} else {
	    MethodTable = (void ***) UxCalloc((GrossClasses += 10),
					      sizeof(void **));
	    MethodCounts = (int *) UxCalloc(GrossClasses,
					    sizeof(int));
	    BaseTable = (int *) UxCalloc(GrossClasses,
					 sizeof(int));
	}
    }
    MethodTable[cid] = (void **) UxCalloc(NumNames, sizeof(void *));
    MethodCounts[cid] = NumNames;
    BaseTable[cid] = -1;

    return cid;
}

/*------------------------------------------------------------------------
 * NAME: UxNewSubclassId (super)
 * 	<register a new class as a subclass of an existing class>
 * INPUT:	super class id
 * RETURNS:	new class id of a subclass of the superclass.
 *
 * LAST REV:	Feb 93	fix3910		Add support for subclassing.
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
int
UxNewSubclassId(super)
int super;
#else				/* _NO_PROTO */
int
UxNewSubclassId(int super)
#endif				/* _NO_PROTO */
{
    int cid = UxNewClassId();
    BaseTable[cid] = super;
    return cid;
}

/*------------------------------------------------------------------------
 * NAME:	UxMessageIndex
 * INPUT:	char* name; -- name of a method being registered.
 * RETURNS:	A unique id number for this name.
 * DESCRIPTION:
 *	Assigns a unique integer to a message name.
 *	Must return the same id for all calls with the same name.
 *
 *	This is used while rgistering methods at startup time.
 *	The result is used when method calls are made
 *	(see UxMethodLookup).  If a very large number of method names
 *	are being used, initialization could be speeded up by making
 *	this function faster (by sorting or hashing the names).
 *
 * LAST REV:	June 92	fix3574		Created.
 *------------------------------------------------------------------------*/
#ifdef _NO_PROTO
int
UxMessageIndex(name)
char *name;
#else				/* _NO_PROTO */
int
UxMessageIndex(char *name)
#endif				/* _NO_PROTO */
{
    int mid;

    for (mid = 0; mid < NumNames; mid++) {
	if (UxStrEqual(Names[mid], name)) {
	    return mid;
	}
    }
    if (mid >= NameLen) {
	if (NameLen == 0) {
	    Names = (char **) UxMalloc((NameLen = 64) * sizeof(char *));
	} else {
	    Names = (char **) UxRealloc(Names,
					(NameLen *= 2) * sizeof(char *));
	}
    }
    Names[NumNames] = (char *) UxMalloc(strlen(name) + 1);
    strcpy(Names[NumNames], name);
    return NumNames++;
}

/*------------------------------------------------------------------------
 * NAME: MethodTableSpace
 * INPUT:
 *		int cid;	-- class id from UxNewClassId ()
 *		int mid;	-- method id  from UxMessageIndex ()
 * DESCRIPTION:
 *
 *  Enlarges the method table for the given cid,
 *  so that it is at least big enough to register a method
 *  for the given message index.  We also add a little more space
 *  (two more cells), but not a lot: there's one of these
 *  arrays for each class, so we don't want them growing too large.
 *
 * LAST REV:	Feb 93	fix3910		Add support for subclassing.
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
static void
MethodTableSpace(cid, mid)
int cid;
int mid;
#else				/* _NO_PROTO */
static void
MethodTableSpace(int cid, int mid)
#endif				/* _NO_PROTO */
{
    int old_cnt = MethodCounts[cid];
    int i;

    if (old_cnt < mid + 1) {
	MethodTable[cid] = (void **) UxRealloc(MethodTable[cid],
					       (mid + 2) * sizeof(void *));
	for (i = old_cnt; i < mid + 2; i++) {
	    MethodTable[cid][i] = 0;
	}
	MethodCounts[cid] = mid + 2;
    }
}

/*------------------------------------------------------------------------
 * NAME: UxMethodRegister
 * INPUT: 	int 	cid;		-- a class code from UxNewClassId()
 *		char 	*name;		-- a method name
 *		void 	(*function) ();	-- implementation of the method
 *					   for the identified class.
 *
 * RETURNS:	An identifier code for the given method name
 *		to be used in subsequent calls to UxMethodLookup.
 *		All calls to this function with the same method name
 *		return the same index.
 *
 * DESCRIPTION:
 *	Called during interface class initialization for each method
 *	implemented by the class.
 *	It builds the method lookup table for the class.
 *
 * LAST REV:	June 92	fix3574		Created.
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
int
UxMethodRegister(cid, name, function)
int cid;
char *name;
void (*function) ();
#else				/* _NO_PROTO */
int
UxMethodRegister(int cid, char *name, void (*function) ())
#endif				/* _NO_PROTO */
{
    int mid = UxMessageIndex(name);

    MethodTableSpace(cid, mid);
    MethodTable[cid][mid] = (void *) function;

    return mid;
}

/*------------------------------------------------------------------------
 * NAME:  UxGetClassCode
 *
 * INPUT:	some swidget
 * RETURNS:		its ifClassCode, or that of its nearest container
 *			that has a class code.
 * DESCRIPTION:
 *
 *	This makes calling methods a little more flexible.
 *	Class codes are assigned to top-level swidgets in interfaces,
 *	when their delarations are initialized.
 *
 * LAST REV:
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
int
UxGetClassCode(sw)
swidget sw;
#else				/* _NO_PROTO */
int
UxGetClassCode(swidget sw)
#endif				/* _NO_PROTO */
{
    while (sw && UxGetIfClassCode(sw) < 0) {
	sw = UxGetParent(sw);
    }

    if (sw)
	return UxGetIfClassCode(sw);

    return -1;
}

/******************************************************************************
NAME:		UxPutClassCode( wgt, id )

INPUT:		Widget	wgt		- Widget
		int	id;		- Id od method

RETURN:		int			UX_ERROR / UX_NO_ERROR

DESCRIPTION:	Uses the X Context manager to store the given id
		in a memory location that is indexed by the given widget id.

EXT REFERENCES:	UxTopLevel, xcontext_mid
EXT EFFECTS:	xcontext_mid

CREATION:	Visual Edge Software		January 9 1993
-----------------------------------------------------------------------------*/
#ifdef XT_CODE
#ifdef _NO_PROTO
int
UxPutClassCode(wgt, id)
Widget wgt;
int id;
#else
int
UxPutClassCode(Widget wgt, int id)
#endif				/* _NO_PROTO */
{
    int status;

    if (xcontext_mid == 0)
	xcontext_mid = XUniqueContext();

    if (wgt == NULL)
	return (UX_ERROR);

    status = XSaveContext(XtDisplay(UxTopLevel),
			  (Window) wgt,
			  xcontext_mid,
			  (XtPointer) id);
    if (status != 0)
	return (UX_ERROR);

    XtAddCallback(wgt, XmNdestroyCallback,
		  UxDeleteContextCB, (XtPointer) xcontext_mid);

    return (UX_NO_ERROR);
}
#endif				/* XT_CODE */

/******************************************************************************
NAME:		UxGetIfClassCode( wgt )

INPUT:		Widget	wgt		- widget

RETURN:		caddr_t			- the context pointer

DESCRIPTION:	Uses the X Context manager to find the method id
		stored in a memory location indexed by the given widget id.

EXT REFERENCES:	UxTopLevel, xcontext_mid

CREATION:	Visual Edge Software		January 9 1993
-----------------------------------------------------------------------------*/
#ifdef XT_CODE
#ifdef _NO_PROTO
int
UxGetIfClassCode(wgt)
Widget wgt;
#else
int
UxGetIfClassCode(Widget wgt)
#endif				/* _NO_PROTO */
{
    int status;
    XtPointer id;

    if (wgt == NULL)
	return -1;

    status = XFindContext(XtDisplay(UxTopLevel),
			  (Window) wgt,
			  xcontext_mid,
			  (XtPointer) & id);

    if (status != 0)
	return -1;

    return ((int) id);
}
#endif				/* XT_CODE */

/*------------------------------------------------------------------------
 * UxChildSite (this)
 * INPUT:	swidget inst - user-defined component
 * Gets the designatedChildSite by composing a call to the childSite
 * method and calling that method. If the childSite is an intance,
 * we recur by calling the childSite's childSite method and so on...
 * LAST REV:	March 1993	3988	- don't recur when childSite==this
 *------------------------------------------------------------------------*/
#ifdef _NO_PROTO
swidget
UxChildSite(inst)
swidget inst;
#else				/* _NO_PROTO */
swidget
UxChildSite(swidget inst)
#endif				/* _NO_PROTO */
{
    /*-----------------------------------------------------
     * Index of "childSite" in low-level method dispatcher.
     *-----------------------------------------------------*/
    static int ChildSiteMID = -1;

    swidget childSite = NULL;
#ifdef _NO_PROTO
    void (*childSiteMethod) ();
#else
    void (*childSiteMethod) (swidget, Environment *);
#endif

    if (ChildSiteMID == -1) {
	ChildSiteMID = UxMessageIndex("childSite");
    }
    if (!inst) {
	return childSite;
    }
    childSiteMethod = (void (*) ())
	UxMethodLookup(inst, ChildSiteMID, "childSite");
    if (childSiteMethod) {
	childSite = ((swidget(*) ()) childSiteMethod) (inst, &UxEnv);
	if (childSite && childSite != inst) {
	    swidget nested = UxChildSite(childSite);
	    if (nested) {
		childSite = nested;
	    }
	}
    }
    if (!childSite) {
	childSite = inst;
    }
    return childSite;
}

/*------------------------------------------------------------------------
 * NAME: UxReclassifyCID (cid, super)
 * 	<redefine a subclass as being derived from a new base.>
 * INPUT:	sub class id
 *		super class id
 *
 * LAST REV:	3988 Mar 93	Created.
 *------------------------------------------------------------------------*/
#ifdef DESIGN_TIME

#ifdef _NO_PROTO
void
UxReclassifyCID(cid, super)
int cid;
int super;
#else				/* _NO_PROTO */
void
UxReclassifyCID(int cid, int super)
#endif				/* _NO_PROTO */
{
    if (cid == super) {
	UxInternalError(__FILE__, __LINE__, "Cid == Super Cid");
    }
    BaseTable[cid] = super;
}
#endif				/* DESIGN_TIME */
/*------------------------------------------------------------------------
 * NAME: UxInheritedMethodLookup
 * INPUT:	swidget sw;	-- a swidget, usually an interface swidget
 *		int	mid;	-- a method id from UxMethodRegister
 * RETURNS:	Pointer to method function for this method.
 * DESCRIPTION:
 *	Like UxMethodLookup, but starts searching at the first
 *	base class of the given sw, not at its own class.
 *
 * LAST REV:	3988 March 93 Created.
 *------------------------------------------------------------------------*/
#ifdef DESIGN_TIME

#ifdef _NO_PROTO
void *
UxInheritedMethodLookup(sw, mid, mname)
swidget sw;
int mid;
char *mname;
#else				/* _NO_PROTO */
void *
UxInheritedMethodLookup(swidget sw, int mid, char *mname)
#endif				/* _NO_PROTO */
{
    int cid = UxGetClassCode(sw);

    if (cid < 0 || cid > NumClasses) {
	return 0;
    }
    if (mid < 0) {
	mid = UxMessageIndex(mname);
	if (mid < 0) {
	    return 0;
	}
    }
    cid = BaseTable[cid];
    while (cid > -1 && cid < NumClasses) {
	if (mid < MethodCounts[cid] && MethodTable[cid][mid]) {
	    return MethodTable[cid][mid];
	}
	cid = BaseTable[cid];
    }
    return 0;
}
#endif				/* DESIGN_TIME */

#ifndef DESIGN_TIME
/*------------------------------------------------------------------------
 * NAME: UndefinedMethod
 * INPUT:
 * RETURNS:	always 0.
 * DESCRIPTION: Function used as a return value of UxMethodLookup
 *		in case the method lookup fails.
 *
 * LAST REV:	April 8 1993
 *------------------------------------------------------------------------*/

#ifdef _NO_PROTO
static int
UndefinedMethod()
#else				/* _NO_PROTO */
static int
UndefinedMethod(void)
#endif				/* _NO_PROTO */
{
    return 0;
}
#endif				/* DESIGN_TIME */
