objmgr.c

Goto definition


/*  objmgr.c
* ===========================================================================
*
*                            PUBLIC DOMAIN NOTICE                          
*               National Center for Biotechnology Information
*                                                                          
*  This software/database is a "United States Government Work" under the   
*  terms of the United States Copyright Act.  It was written as part of    
*  the author's official duties as a United States Government employee and 
*  thus cannot be copyrighted.  This software/database is freely available 
*  to the public for use. The National Library of Medicine and the U.S.    
*  Government have not placed any restriction on its use or reproduction.  
*                                                                          
*  Although all reasonable efforts have been taken to ensure the accuracy  
*  and reliability of the software and data, the NLM and the U.S.          
*  Government do not and cannot warrant the performance or results that    
*  may be obtained by using this software or data. The NLM and the U.S.    
*  Government disclaim all warranties, express or implied, including       
*  warranties of performance, merchantability or fitness for any particular
*  purpose.                                                                
*                                                                          
*  Please cite the author in any work or product based on this material.   
*
* ===========================================================================
*
* File Name:  objmgr.c
*
* Author:  James Ostell
*   
* Version Creation Date: 9/94
*
* $Revision: 6.81 $
*
* File Description:  Manager for Bioseqs and BioseqSets
*
* Modifications:  
* --------------------------------------------------------------------------
* $Log: objmgr.c,v $
* Revision 6.81  2007/05/08 20:03:22  kans
* quiet theoretically uninitialized variables detected by CodeWarrior
*
* Revision 6.80  2007/02/06 19:46:45  bollin
* Maximum number of objects is Uint4, not Int2
*
* Revision 6.79  2006/11/02 22:38:58  kans
* ObjMgrReap collects candidates, sorts by touch time, goes through list to remove N squared behavior
*
* Revision 6.78  2006/08/25 16:12:33  bollin
* added ObjMgrReportProc back in
*
* Revision 6.77  2006/08/25 15:34:21  bollin
* rollback of objmgr to earlier version, followed by selective changes to fix
* Object Manager problems.
*
* Revision 6.62  2005/07/18 14:49:53  kans
* fixed minor xcode compiler warnings
*
* Revision 6.61  2005/04/13 21:22:17  kans
* ObjMgrRecycleEntityID and ObjMgrRemoveEntityIDFromRecycle cast to Uint4 for calculations, just like ObjMgrNextAvailEntityID already did
*
* Revision 6.60  2005/04/08 21:23:08  kans
* added ObjMgrStatusString function for debugging
*
* Revision 6.59  2005/02/01 19:51:58  kans
* print actual entityID if ObjMgrRecycleEntityID has bad idx or jdx
*
* Revision 6.58  2004/10/28 16:00:42  kans
* ObjMgrFreeCacheFunc uses type == OBJ_MAX to not free temp loaded objects that are still locked, called from ObjMgrAddFunc when autoclean is triggered
*
* Revision 6.57  2004/10/15 19:08:36  bollin
* when removing an object, make sure to also deselect it
*
* Revision 6.56  2004/09/07 14:08:27  kans
* post errors on failure of ObjMgrNextAvailEntityID and ObjMgrRecycleEntityID
*
* Revision 6.55  2004/06/09 01:56:43  kans
* initialize assigned id array from all functions that use it
*
* Revision 6.54  2004/06/08 20:48:09  kans
* changed entityID recycling from small array of integers to bit array of all possible values
*
* Revision 6.53  2004/06/08 18:19:01  kans
* ObjMgrFreeCacheFunc frees all TL_CACHED, and frees TL_LOADED if type == 0
*
* Revision 6.52  2004/04/21 19:40:51  kans
* ObjMgrReap calculate tempcnt based on temp loaded records, but excluded locked ones - more work still to do in other functions to completely avoid unnecessary thrashing
*
* Revision 6.51  2004/04/01 13:43:05  lavr
* Spell "occurred", "occurrence", and "occurring"
*
* Revision 6.50  2003/12/22 15:29:28  kans
* ObjMgrSendMsg calls SeqMgrClearFeatureIndexes on OM_MSG_DEL as well as OM_MSG_UPDATE
*
* Revision 6.49  2002/07/30 14:41:45  kans
* removed omdp->rearranged
*
* Revision 6.48  2002/07/29 21:30:17  kans
* added rearranged flag to omdp
*
* Revision 6.47  2002/07/01 14:29:02  kans
* changed totobj, currobj to Uint4
*
* Revision 6.46  2002/06/07 21:24:57  kans
* update entityID/omdp index in BioseqReloadFunc, ObjMgrConnectFunc, ObjMgrDetachFunc
*
* Revision 6.45  2002/06/01 05:52:00  vakatov
* Fix to R6.44 -- add LIBCALL and NLM_EXTERN modifiers to match the func proto
*
* Revision 6.44  2002/05/31 21:53:26  yaschenk
* changing lookup by EntityID to array[][]
*
* Revision 6.43  2001/12/13 13:59:14  kans
* ObjMgrSendMsg clears feature indexes if OM_MSG_UPDATE
*
* Revision 6.42  2001/11/30 12:19:47  kans
* ObjMgrDelete bails if omdp->bulkIndexFree
*
* Revision 6.41  2001/11/19 15:27:52  kans
* minor fix to ObjMgrDeleteAllInRecord
*
* Revision 6.40  2001/11/19 15:26:19  kans
* added ObjMgrDeleteAllInRecord, still need to bail in ObjMgrDelete if bulkIndexFree, then call from BioseqFree and BioseqSetFree
*
* Revision 6.39  2001/11/15 18:15:48  kans
* set bsp->omdp at creation, SeqMgrDeleteIndexesInRecord sets omdp->bulkIndexFree
*
* Revision 6.38  2001/10/02 12:22:57  kans
* ObjMgrRecordOmdpByEntityID for quick access to top omdp by entity
*
* Revision 6.37  2001/05/31 22:58:25  kans
* added ObjMgrReapOne, DEFAULT_MAXOBJ, autoclean reaps and frees one entity at a time, as needed
*
* Revision 6.36  2001/05/31 22:33:02  kans
* added autoclean and maxobj to ObjMgr structure,  optionally calls ObjMgrReap and ObjMgrFreeCache to completely clear out least recently accessed objects if currobj >= maxobj
*
* Revision 6.35  2001/05/24 21:54:34  kans
* check for incrementing totobj or currobj above UINT2_MAX, reducing currobj below 0
*
* Revision 6.34  2001/02/16 21:34:49  ostell
* changed GetSecs() to ObjMgrTouchCnt() to reduce system calls
*
* Revision 6.33  2000/11/28 22:58:08  kans
* removed omdp->lockcnt == 0 debugging breakpoint statement
*
* Revision 6.32  2000/11/28 21:43:54  kans
* added ObjMgrReportFunc for debugging
*
* Revision 6.31  2000/10/30 21:26:08  shavirin
* Changes and fixes for some MT-safety related problems.
*
* Revision 6.30  2000/03/27 23:10:23  kans
* initial readlock/lookup/unlock removed from ObjMgrDelete by request of EY
*
* Revision 6.29  2000/03/23 17:45:44  madden
* Use ObjMgrWriteLock instead of ObjMgrReadLock in ObjMgrFreeCache
*
* Revision 6.28  2000/03/20 23:38:40  aleksey
* Finally submitted the changes which have been made by serge bazhin
* and been kept in my local directory.
*
* These changes allow to establish user callback functions
* in 'Asn2ffJobPtr' structure which are called within
* 'SeqEntryToFlatAjp' function call.
* The new members are:
* user_data       - pointer to a user context for passing data
* ajp_count_index - user defined function
* ajp_print_data  - user defined function
* ajp_print_index - user defined function
*
* Revision 6.27  1999/10/20 17:17:37  lewisg
* delete rest of userdata when using OM_OPT_FREE_IF_NO_VIEW in ObjMgrFreeUserDataFunc
*
* Revision 6.26  1999/10/18 20:58:47  lewisg
* add rowID to ObjMgrSendMsgFunc
*
* Revision 6.25  1999/09/27 20:03:14  kans
* added rowID to OMMsgStruct, new ObjMgrSendRowMsg function
*
* Revision 6.24  1999/08/13 19:43:35  kans
* ObjMgrDelete and ObjMgrGetEntityIDForPointer now multithread-safe (EY)
*
* Revision 6.23  1999/08/11 15:17:53  kans
* added ObjMgrFreeByEntityID
*
* Revision 6.22  1999/06/11 16:42:39  kans
* remove from recycle just shrinks stack if removing last element
*
* Revision 6.21  1999/06/10 21:10:28  kans
* added ObjMgrRemoveEntityIDFromRecycle, called from RestoreSeqEntryObjMgrData
*
* Revision 6.20  1999/06/01 21:06:48  ywang
* get rid of (sip1->strand == sip2->strand) in ObjMgrRegionMatch
*
* Revision 6.19  1999/05/17 02:11:23  chappey
* remove the temporary fix
*
* Revision 6.18  1999/05/17 00:41:54  chappey
* temporary fix in ObjMgrRegionComp when SeqLocStop==APPEND_RESIDUE
*
* Revision 6.17  1999/02/19 19:24:34  chappey
* Possible deselection of a region included in a selected region
*
* Revision 6.16  1999/01/22 16:19:51  chappey
* AlsoSelect does not Deselect anymore when selected region of an entityID is already selected. Only one selection remains
*
* Revision 6.15  1999/01/21 19:45:32  chappey
* bug fixed in ObjMgrSelectFunc2, CheckRedondantSelect, ObjMgrRegionComp
*
* Revision 6.14  1998/12/02 01:56:24  kans
* when recycling entityID, check to see if it is already in list, do not add second copy - will see why this is happening later
*
* Revision 6.13  1998/11/30 20:59:01  egorov
* Add ObjMgrNextAvailableEntityID() to avoid overflowing
*
* Revision 6.12  1998/10/07 22:25:43  kans
* reapextra, like freeextra, takes omdp, not omdp->extradata
*
* Revision 6.11  1998/10/07 21:20:14  kans
* ObjMgrAlsoSelect changes (CC)
*
* Revision 6.10  1998/09/28 19:54:10  kans
* made ObjMgrDump debugging function public, no longer conditionally compiled
*
* Revision 6.9  1998/08/18 14:57:02  kans
* protect against nulls in ObjMgrRegionMatch
*
* Revision 6.8  1998/07/01 19:11:45  kans
* added fromProcID, toProcID, OM_MSG_PROCESS, ObjMgrSendProcMsg, time of indexing, ObjMgrGetProcID, moved protFeat and cdsOrRnaFeat to seqmgr structure
*
* Revision 6.7  1998/06/26 23:01:24  kans
* ObjMgrDelete frees extra data
*
* Revision 6.6  1998/06/02 16:53:27  kans
* fixed bug that prevented userdata from being freed, and decrement highest entity if attaching highest to something else
*
* Revision 6.5  1997/12/11 14:37:28  ostell
* corrected a deadlock with DeSelectRegion
*
* Revision 6.4  1997/12/05 16:42:42  ostell
* added objmgrunlock() around "Free if no views"
*
* Revision 6.3  1997/11/22 16:49:57  ostell
* fixed deadlock in ObjMgrReap
*
* Revision 6.2  1997/11/19 22:14:35  ostell
* added support for multithreaded programs
*
* Revision 6.1  1997/09/11 15:55:45  ostell
* Added support for SetColor messages
*
* Revision 6.0  1997/08/25 18:06:43  madden
* Revision changed to 6.0
*
* Revision 5.9  1997/08/01 14:12:19  ostell
* put check for time_t being signed int back into ObjMgrReap for Solaris
*
* Revision 5.8  1997/07/31 16:06:07  kans
* ObjMgrReap lowest time uses UINT4_MAX because of overflow of the number of seconds since 1900
*
* Revision 5.7  1997/07/25 21:31:20  ostell
* free if no view now checks that omudp->messagefunc != NULL instead of checking
* all of them.
*
* Revision 5.6  1997/06/19 18:38:27  vakatov
* [WIN32,MSVC++]  Adopted for the "NCBIOBJ.LIB" DLL'ization
*
* Revision 5.5  1997/06/02 17:04:09  kans
* added ObjMgrProcLoadEx
*
* Revision 5.4  1997/04/29 16:11:30  kans
* send deselect message AFTER removing item from list of selected items
*
 * Revision 5.3  1997/01/22  05:29:46  ostell
 * made objmgrget static
 * made seqmgrget static
 *
 * Revision 5.2  1996/07/30  21:18:15  chappey
 * don't return FALSE in ObjMgrSetTempLoad when index == 0
 *
 * Revision 5.1  1996/06/26  20:07:48  chappey
 * ObjMgrRegionMatch now returs TRUE when the regions are identical
 *
 * Revision 5.0  1996/05/28  13:23:23  ostell
 * Set to revision 5.0
 *
 * Revision 4.14  1996/05/22  21:46:59  kans
 * don't return on OBJ_MSG_RET_DONE in ObjMgrSendStructMsgFunc
 *
 * Revision 4.13  1996/03/05  19:15:01  kans
 * fixed bug in ObjMgrSetOptions and ObjMgrClearOptions
 *
 * Revision 4.12  1996/02/28  04:53:06  ostell
 * added ObjMgrHold suport
 *
 * Revision 4.10  1996/01/22  13:28:11  kans
 * cast first parameter of MemSet to (Pointer) for SunOS compiler
 *
 * Revision 4.9  1996/01/05  14:39:12  ostell
 * fix for control chars in comment
 *
 * Revision 4.7  1995/12/22  20:12:01  ostell
 * added lock in ObjMgrTempLoad to protect new record from ObjMgrReap
 *
 * Revision 4.6  1995/12/22  14:45:45  ostell
 * added do_not_reload_from_cache to OmProcControl so that DataGet
 * functions from gather can control this behvior
 * added protection from recursion in ObjMgrDelete
 *
 * Revision 4.5  1995/12/15  02:47:01  ostell
 * fix to ObjMgrReap so highest time_t is INT4_MAX not UINT4_MAX to compensate
 * for buggy include files from vendors
 *
 * Revision 4.4  1995/10/03  15:50:37  ostell
 * added support for selection by region.. now fully implemented
 *
 * Revision 4.3  1995/10/02  14:36:55  ostell
 * fixed ObjMgrFreeUserData to get options from omp or omdp
 *
 * Revision 4.2  1995/09/30  03:38:31  ostell
 * Changed ObjMgrMessage functions to pass a structure
 * Added support for selecting regions
 * Added ability to remove entity when no more views on it
 *
 * Revision 4.1  1995/09/25  18:06:41  ostell
 * added ObjMgrGenericAsnTextRead
 *
 * Revision 4.0  1995/07/26  13:49:01  ostell
 * force revision to 4.0
 *
 * Revision 1.25  1995/07/20  18:30:32  kans
 * changed SeqIdPrint to SeqIdWrite
 *
 * Revision 1.24  1995/07/08  15:21:01  ostell
 * Added ObjMgrSetOptions() and related functions
 * Added support for delete of entity when no more user data on it
 * Added support for DirtyFlag
 *
 * Revision 1.23  1995/05/15  21:46:05  ostell
 * added Log line
 *
*
*
* ==========================================================================
*/

/** for ErrPostEx() ****/

static char *this_module = "ncbiapi";
#define THIS_MODULE this_module
static char *this_file = __FILE__;
#define THIS_FILE this_file

/**********************/

#include <objmgr.h>		   /* the interface */
#include <objsset.h>       /* temporary for caching functions */
#include <ncbithr.h>       /* for thread safe functions */
#include <sequtil.h>
#include <explore.h>       /* for SeqMgrClearFeatureIndexes */

/***
#define DEBUG_OBJMGR
***/

#define OBJMGR_MAX UINT4_MAX


/*****************************************************************************
*
*   Data Object local functions
*
*****************************************************************************/
static Uint4 NEAR ObjMgrTouchCnt PROTO((void));
static Boolean NEAR ObjMgrExtend PROTO((ObjMgrPtr omp));
static ObjMgrDataPtr NEAR ObjMgrFindByEntityID PROTO((ObjMgrPtr omp, Uint2 entityID, ObjMgrDataPtr PNTR prev));
static Boolean NEAR ObjMgrFreeClipBoardFunc PROTO((ObjMgrPtr omp));
static Boolean NEAR ObjMgrFreeCacheFunc PROTO((ObjMgrPtr omp, Uint2 type, Uint2Ptr rettype, VoidPtr PNTR retval));
static Boolean NEAR ObjMgrAddFunc PROTO((ObjMgrPtr omp, Uint2 type, Pointer data));
static Boolean NEAR ObjMgrFreeUserDataFunc PROTO((ObjMgrPtr omp, Uint2 entityID,
								Uint2 procid, Uint2 proctype, Uint2 userkey));
static Int4 NEAR ObjMgrLockFunc (ObjMgrPtr omp, Uint2 type, Pointer data, Boolean lockit);
/*****************************************************************************
*
*   Procedure Management local Functions
*
*****************************************************************************/
static Boolean NEAR ObjMgrProcExtend PROTO((ObjMgrPtr omp));

/*****************************************************************************
*
*   Type Management Local functions
*
*****************************************************************************/
static Boolean NEAR ObjMgrTypeExtend PROTO((ObjMgrPtr omp));

/*****************************************************************************
*
*   Messaging/Selection local functions
*
*****************************************************************************/
static Boolean NEAR ObjMgrSendMsgFunc PROTO((ObjMgrPtr omp, ObjMgrDataPtr omdp,
				Int2 msg, Uint2 entityID, Uint4 itemID,	Uint2 itemtype, Uint2 rowID,
				Uint2 fromProcID, Uint2 toProcID, Pointer procmsgdata));
static Boolean NEAR ObjMgrSendStructMsgFunc PROTO((ObjMgrPtr omp, ObjMgrDataPtr omdp,
				OMMsgStructPtr ommsp));
static SelStructPtr NEAR ObjMgrAddSelStruct PROTO((ObjMgrPtr omp,
	 Uint2 entityID, Uint4 itemID, Uint2 itemtype, Uint1 regiontype, Pointer region));
static Boolean NEAR ObjMgrSelectFunc PROTO((ObjMgrPtr omp, Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region));
static Boolean NEAR ObjMgrDeSelectFunc PROTO((ObjMgrPtr omp, Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region));
/* static Boolean NEAR ObjMgrDeSelectStrucFunc PROTO((ObjMgrPtr omp, SelStructPtr ssp)); */
static Boolean NEAR ObjMgrDeSelectAllFunc PROTO((ObjMgrPtr omp));
static Boolean NEAR ObjMgrRegionMatch PROTO((Uint1 regiontype1, Pointer region1,
						Uint1 regiontype2, Pointer region2));
/* static Pointer NEAR ObjMgrRegionCopy PROTO((ObjMgrPtr omp, Uint1 regiontype, Pointer region)); */
static Pointer NEAR ObjMgrRegionFree PROTO((ObjMgrPtr omp, Uint1 regiontype, Pointer region));
static Pointer NEAR ObjMgrMemCopyFunc PROTO((ObjMgrPtr omp, Uint2 type, Pointer ptr, Boolean unlock));
static Pointer NEAR ObjMgrFreeFunc PROTO((ObjMgrPtr omp, Uint2 type, Pointer ptr, Boolean unlock));

/*****************************************************************************
*
*   ObjMgr Functions
*
*****************************************************************************/
static TNlmMutex omctr_mutex = NULL;
static Uint4 NEAR ObjMgrTouchCnt (void)
{
	static Uint4 ObjMgrTouchCtr = 1; /* start at 1 to avoid rollover signal */
	Int4 ret, imax, i;
	ObjMgrPtr omp;
	ObjMgrDataPtr PNTR omdpp;
	Uint4 tmpctr;

	if (! ObjMgrTouchCtr)  /* rolled over */
	{
		ret = NlmMutexLockEx(&omctr_mutex); /* protect this section */
		if (ret)  /* error */
		{
			ErrPostEx(SEV_FATAL,0,0,"ObjMgrTouchCnt failed [%ld]", (long)ret);
			return ObjMgrTouchCtr;
		}
		
		/**** reset the touch values *****/
		/** all calls to this function should already have ObjMgrWriteLock **/

		if (! ObjMgrTouchCtr) /* check that this was not a stalled thread */
		{
			tmpctr = 0;
			omp = ObjMgrGet();
			imax = omp->currobj;
			omdpp = omp->datalist;
			for (i = 0; i < imax; i++)
			{
				if (omdpp[i]->parentptr == NULL)
					omdpp[i]->touch = ++tmpctr;
			}
			ObjMgrTouchCtr = tmpctr;
		}
		NlmMutexUnlock(omctr_mutex);
	}

	return ++ObjMgrTouchCtr;

}

NLM_EXTERN ObjMgrDataPtr LIBCALL ObjMgrGetData (Uint2 entityID)
{
	ObjMgrDataPtr prev, retval=NULL;
	ObjMgrPtr omp;

	omp = ObjMgrReadLock();
	retval = ObjMgrFindByEntityID (omp, entityID, &prev);
	ObjMgrUnlock();
	return retval;
}

NLM_EXTERN ObjMgrDataPtr LIBCALL ObjMgrGetDataStruct (ObjMgrPtr omp, Uint2 entityID)
{
	ObjMgrDataPtr prev;

	return ObjMgrFindByEntityID (omp, entityID, &prev);
}

/* cache to avoid linear search of all objects in ObjMgrFindByEntityID */

#define TOP_ID_STACK_SIZE  100

static Uint2 topEntityIDs [TOP_ID_STACK_SIZE];
static ObjMgrDataPtr topOMDPs [TOP_ID_STACK_SIZE];
static Int2  topIDStackPt = 0;

extern void ObjMgrRecordOmdpByEntityID (Uint2 entityID, ObjMgrDataPtr omdp);
extern void ObjMgrRecordOmdpByEntityID (Uint2 entityID, ObjMgrDataPtr omdp)

{
	Int2  i;

	if (entityID < 1) return;

	/* check for preexisting entry, update */

	for (i = 0; i < topIDStackPt; i++) {
		if (entityID == topEntityIDs [i]) {
			topOMDPs [i] = omdp; /* record in stack */
			if (omdp == NULL) { /* remove if null */
				if (topIDStackPt > i + 1) {
					topIDStackPt--;
					topEntityIDs [i] = topEntityIDs [topIDStackPt];
					topOMDPs [i] = topOMDPs [topIDStackPt];
				} else {
					topIDStackPt--;
				}
			}
			return;
		}
	}

	/* add if not found */

	if (omdp == NULL) return;
	if (topIDStackPt < TOP_ID_STACK_SIZE) {
		topEntityIDs [topIDStackPt] = entityID;
		topOMDPs [topIDStackPt] = omdp;
		topIDStackPt++;
	}
	ObjMgrAddIndexOnEntityID(NULL,entityID,omdp);
}

static ObjMgrDataPtr NEAR ObjMgrFindByEntityID (ObjMgrPtr omp, Uint2 entityID, ObjMgrDataPtr PNTR prev)
{
	ObjMgrDataPtr omdp, prevptr=NULL;
	ObjMgrDataPtr PNTR omdpp;
	Int4 i, imax;

	/* check cache first to avoid linear search through all objects */
	if((omdp=ObjMgrLookupIndexOnEntityID(omp,entityID)) != NULL) return omdp;

	for (i = 0; i < topIDStackPt; i++) {
		if (entityID == topEntityIDs [i]) {
			omdp = topOMDPs [i];
			if (omdp != NULL && omdp->parentptr == NULL && omdp->EntityID == entityID) {
				if (prev != NULL)
					*prev = prevptr;
				return omdp;
			}
		}
	}

	imax = omp->currobj;
	omdpp = omp->datalist;
	for (i = 0; i < imax; i++)
	{
		omdp = omdpp[i];    /* emptys always at end */
		if (omdp->parentptr == NULL)
		{
			if (omdp->EntityID == entityID)
			{
				if (prev != NULL)
					*prev = prevptr;
				return omdp;
			}
		}
	}
	return NULL;
}

NLM_EXTERN ObjMgrDataPtr LIBCALL ObjMgrFindByData (ObjMgrPtr omp, Pointer ptr)
{
	ObjMgrDataPtr omdp;
	ObjMgrDataPtr PNTR omdpp;
	Int4 i, imax;
	
	if (ptr == NULL) return NULL;

	imax = omp->currobj;
	omdpp = omp->datalist;

	i = ObjMgrLookup(omp, ptr);   /* find by binary search on dataptr? */
	if (i >= 0)
		return omdpp[i];          /* found it */

	for (i = 0; i < imax; i++)
	{
		omdp = omdpp[i];    /* emptys always at end */
		if ((Pointer)(omdp->choice) == ptr)
		{
			return omdp;
		}
	}
	return NULL;
}

static Uint4    assignedIDsArray [2050];
static Int2     assignedIDStackPt = 0;
static Boolean  assignedIDsInited = FALSE;

static Uint4    assignedIDsBitIdx [32];

static void ObjMgrInitAssignedIDArray (void)

{
  Uint4  bit;
  Int2   jdx;

  if (! assignedIDsInited) {
    MemSet ((Pointer) &assignedIDsArray, 0, sizeof assignedIDsArray);
    MemSet ((Pointer) &assignedIDsBitIdx, 0, sizeof (assignedIDsBitIdx));
  
    /* initialize bit index array */

    bit = 1;
    for (jdx = 0; jdx < 32; jdx++) {
      assignedIDsBitIdx [jdx] = bit;
      bit = bit << 1;
    }
  
    /* entityID 0 is not available for use */

    assignedIDsArray [0] = assignedIDsBitIdx [0];

    assignedIDStackPt = 0;
    assignedIDsInited = TRUE;
  }
}

static Uint2 ObjMgrNextAvailEntityID (ObjMgrPtr omp)

{
  Uint2  entityID;
  Int2   idx, jdx;
  Uint4  val;

  if (! assignedIDsInited) {
    ObjMgrInitAssignedIDArray ();
  }

  /* find first 32 bit word with an available entityID */

  idx = assignedIDStackPt;
  while (idx < 2048 && assignedIDsArray [idx] == 0xFFFFFFFF) {
    idx++;
  }
  if (idx >= 2048) {
    ErrPostEx (SEV_ERROR, 0, 0, "ObjMgrNextAvailEntityID failed with idx %d", (int) idx);
    return 0;
  }

  /* reset starting point, everything below should be in use */

  assignedIDStackPt = idx;

  /* find first empty bit in array element */

  val = assignedIDsArray [idx];
  jdx = 0;
  while (jdx < 32 && (val & assignedIDsBitIdx [jdx]) != 0) {
    jdx++;
  }
  if (jdx >= 32) {
    ErrPostEx (SEV_ERROR, 0, 0, "ObjMgrNextAvailEntityID failed with jdx %d", (int) jdx);
    return 0;
  }

  /* set bit to mark new entityID as in use */

  assignedIDsArray [idx] |= assignedIDsBitIdx [jdx];

  /* calculate entityID */

  entityID = (Uint2) (((Int4) idx) * 32L + (Int4) jdx);

  if (omp != NULL && omp->HighestEntityID < entityID) {
    omp->HighestEntityID = entityID;
  }

  return entityID;
}

static void ObjMgrRecycleEntityID (Uint2 entityID, ObjMgrPtr omp)

{
  Int2   idx, jdx;

  if (! assignedIDsInited) {
    ObjMgrInitAssignedIDArray ();
  }

  if (entityID < 1) return;

  idx = (Int2) ((Uint4) entityID / 32L);
  jdx = (Int2) ((Uint4) entityID % 32L);

  if (idx >= 2048 || idx < 0) {
    ErrPostEx (SEV_ERROR, 0, 0, "ObjMgrRecycleEntityID %d failed with idx %d", (int) entityID, (int) idx);
    return;
  }
  if (jdx >= 32 || jdx < 0) {
    ErrPostEx (SEV_ERROR, 0, 0, "ObjMgrRecycleEntityID %d failed with jdx %d", (int) entityID, (int) jdx);
    return;
  }

  /* clear bit to mark old entityID as available */

  assignedIDsArray [idx] ^= assignedIDsBitIdx [jdx];

  /* reset starting point, everything below should be in use */

  if (idx < assignedIDStackPt) {
    assignedIDStackPt = idx;
  }
}

extern void ObjMgrRemoveEntityIDFromRecycle (Uint2 entityID, ObjMgrPtr omp);
extern void ObjMgrRemoveEntityIDFromRecycle (Uint2 entityID, ObjMgrPtr omp)

{
  Int2   idx, jdx;

  if (! assignedIDsInited) {
    ObjMgrInitAssignedIDArray ();
  }

  if (entityID < 1) return;

  idx = (Int2) ((Uint4) entityID / 32L);
  jdx = (Int2) ((Uint4) entityID % 32L);

  if (idx >= 2048 || idx < 0) return;
  if (jdx >= 32 || jdx < 0) return;

  /* set bit to restore old entityID status to in use */

  assignedIDsArray [idx] |= assignedIDsBitIdx [jdx];
}

NLM_EXTERN Uint2 LIBCALL ObjMgrAddEntityID (ObjMgrPtr omp, ObjMgrDataPtr omdp)
{
	if (omdp == NULL) return 0;

	if (omdp->EntityID)
	   return omdp->EntityID;

	/* omdp->EntityID = ++(omp->HighestEntityID); */
	omdp->EntityID = ObjMgrNextAvailEntityID (omp);
	ObjMgrRecordOmdpByEntityID (omdp->EntityID, omdp);

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrAddEntityID-A");
#endif

	ObjMgrSendMsgFunc(omp, omdp, OM_MSG_CREATE, omdp->EntityID, 0, 0, 0, 0, 0, NULL);

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrAddEntityID-B");
#endif

	return omdp->EntityID;
}
static Boolean NEAR ObjMgrSendMsgFunc (ObjMgrPtr omp, ObjMgrDataPtr omdp,
				Int2 msg, Uint2 entityID, Uint4 itemID,	Uint2 itemtype, Uint2 rowID,
				Uint2 fromProcID, Uint2 toProcID, Pointer procmsgdata)
{
	OMMsgStruct omms;

	MemSet((Pointer)(&omms), 0, sizeof(OMMsgStruct));

	omms.message = msg;
	omms.entityID = entityID;
	omms.itemID = itemID;
	omms.itemtype = itemtype;
	omms.fromProcID = fromProcID;
	omms.toProcID = toProcID;
	omms.procmsgdata = procmsgdata;
    omms.rowID = rowID;
	
	return ObjMgrSendStructMsgFunc (omp, omdp, &omms);
}

/*********************************************************************
*
*   ObjMgrSendStructMsgFunc()
*   This function actually sends the messages to registered proceedures.
*   It has to unlock the objmgr so the proceedures can read or write the
*   objmgr. This is risky, but I am not sure how to do this safer. It does
*   keep an array of messages to send before it unlocks anything, in case
*   the message list changes from a callback. This imposes a fixed limit to 
*   the number of messages that can be sent at once to MAXMSG. This may become
*   a problem.
*
*********************************************************************/
static Boolean NEAR ObjMgrSendStructMsgFunc (ObjMgrPtr omp, ObjMgrDataPtr omdp,
				OMMsgStructPtr ommsp)
{
#define MAXMSG 250  /* max number of messages to save */
	OMUserDataPtr prev=NULL, curr, next, PNTR root, msgs[MAXMSG], omudp;
	Int2 retval;
	Int2 ctr, nummsg, i;
	Boolean is_write_locked, too_many, done;

	is_write_locked = omp->is_write_locked;   /* remember state */
	nummsg = 0;      /* no messages known yet */
	  
	                  /*****************************************
					  *  First find all messages to send while locked
					  *****************************************/
	curr = omp->userdata;
	root = &(omp->userdata);
	ctr = 2;
	too_many = FALSE;
	while ((ctr) && (! too_many))
	{
		prev = NULL;
		while ((curr != NULL) && (! too_many))
		{
			next = curr->next;
			if (curr->messagefunc != NULL)
			{
				if (ommsp->toProcID == 0 || ommsp->toProcID == curr->procid) {
					if (nummsg < MAXMSG)
					{
						msgs[nummsg] = curr;
						nummsg++;
					}
					else
						too_many = TRUE;
				}
			}
			curr = next;
		}

		if (omdp == NULL)
			ctr -= 2;
		else if (ctr == 2)
		{
			ctr--;
			curr = omdp->userdata;
			root = &(omdp->userdata);
		}
		else
			ctr--;
	}

	if (too_many)
	{
		ErrPostEx(SEV_ERROR,0,0,"ObjMgrSendMessage: more than %d messages",
			(int)nummsg);
	}



                      /*****************************************
					  *  now send the messages confirming that each
					  *    still exists
					  *****************************************/

	for (i = 0; i < nummsg; i++)
	{
		omudp = msgs[i];
		done = FALSE;
	curr = omp->userdata;
	root = &(omp->userdata);
	ctr = 2;
	while ((ctr) && (! done))
	{
		prev = NULL;
		while ((curr != NULL) && (! done))
		{
			next = curr->next;
			if (curr == omudp)
			{
			  done = TRUE;   /* found this one */
			  if (curr->messagefunc != NULL)
			  {
				ommsp->omuserdata = (Pointer)curr;

				ObjMgrUnlock();   /* have to allow message to react */
				retval = (* (curr->messagefunc))(ommsp);
				if (is_write_locked)
					ObjMgrWriteLock();
				else
					ObjMgrReadLock();

				if (retval == OM_MSG_RET_ERROR)
					ErrShow();
				else if (retval == OM_MSG_RET_DEL)
				{
					if (prev == NULL)
						*root = next;
					else
						prev->next = next;
					if (curr->freefunc != NULL)
						(* (curr->freefunc))(curr->userdata.ptrvalue);
					MemFree(curr);
				}
			  }
			}
			curr = next;
		}

		if (omdp == NULL)
			ctr -= 2;
		else if (ctr == 2)
		{
			ctr--;
			curr = omdp->userdata;
			root = &(omdp->userdata);
		}
		else
			ctr--;
	  }
	}
	return TRUE;

}
/*****************************************************************************
*
*   Return the current ObjMgr
*   	ObjMgrGet is obsolete
*       ObjMgrReadLock, ReadUnlock, WriteLock, WriteUnlock are thread safe
*
*****************************************************************************/
static TNlmMutex omp_mutex = NULL;
static ObjMgrPtr global_omp = NULL;
static TNlmRWlock omp_RWlock = NULL;

/*****************************************************************************
*
*   Return the current ObjMgr
*   	Initialize if not done already
*       This function will become obsolete
*
*****************************************************************************/
NLM_EXTERN ObjMgrPtr LIBCALL ObjMgrGet (void)
{
	Int4 ret;
	ObjMgrPtr omp;

	if (global_omp != NULL)
		return global_omp;

	ret = NlmMutexLockEx(&omp_mutex);  /* protect this section */
	if (ret)  /* error */
	{
		ErrPostEx(SEV_FATAL,0,0,"ObjMgrGet failed [%ld]", (long)ret);
		return NULL;
	}

	if (global_omp == NULL)  /* check again after mutex */
	{
	                             /*** have to initialize it **/
		omp = (ObjMgrPtr) MemNew (sizeof(ObjMgr));
		omp->maxtemp = DEFAULT_MAXTEMP;
		omp->maxobj = DEFAULT_MAXOBJ;
		omp_RWlock = NlmRWinit();  /* initialize RW lock */
		global_omp = omp;       /* do this last for mutex safety */
	}

	NlmMutexUnlock(omp_mutex);

	return global_omp;
}

/*****************************************************************************
*
*   ObjMgrReadLock()
*   	Initialize if not done already
*       A thread can have only one read or write lock at a time
*       Many threads can have read locks
*       Only one thread can have a write lock
*       No other threads may have read locks if a write lock is granted
*       If another thread holds a write lock, this call blocks until write
*          is unlocked.
*
*****************************************************************************/
NLM_EXTERN ObjMgrPtr LIBCALL ObjMgrReadLock (void)
{
	ObjMgrPtr omp;
	Int4 ret;

	omp = ObjMgrGet();  /* ensure initialization */

        ret = NlmRWrdlock(omp_RWlock); 
	/* ret = NlmRWwrlock(omp_RWlock); */

	if (ret != 0)
	{
		ErrPostEx(SEV_ERROR,0,0,"ObjMgrReadLock: RWrdlock error [%ld]",
			(long)ret);
		return NULL;
	}

	omp->is_write_locked = TRUE;

	return omp;
}

/*****************************************************************************
*
*   ObjMgrWriteLock
*   	Initialize if not done already
*       A thread can have only one read or write lock at a time
*       Many threads can have read locks
*       Only one thread can have a write lock
*       No other threads may have read locks if a write lock is granted
*       If another thread holds a read or write lock, this call blocks until write
*          is unlocked.
*
*****************************************************************************/
NLM_EXTERN ObjMgrPtr LIBCALL ObjMgrWriteLock (void)
{
	ObjMgrPtr omp;
	Int4 ret;

	omp = ObjMgrGet();  /* ensure initialization */

	ret = NlmRWwrlock(omp_RWlock);
	if (ret != 0)
	{
		ErrPostEx(SEV_ERROR,0,0,"ObjMgrWriteLock: RWwrlock error [%ld]",
			(long)ret);
		return NULL;
	}
	omp->is_write_locked = TRUE;
	return omp;
}


/*****************************************************************************
*
*  ObjMgrUnlock()
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrUnlock (void)
{
	ObjMgrPtr omp;
	Int4 ret;

	omp = ObjMgrGet();  /* ensure initialization */

	ret = NlmRWunlock(omp_RWlock);
	if (ret != 0)
	{
		ErrPostEx(SEV_ERROR,0,0,"ObjMgrUnlock: RWunlock error [%ld]",
			(long)ret);
		return FALSE;
	}
	omp->is_write_locked = FALSE;  /* can't be write locked */
	return TRUE;
}




/*****************************************************************************
*
*   ObjMgrSetOptions(Uint2 option, Uint2 entityID)
*     Sets options on the ObjMgr. All Options are 1 bit flags set with
*       defines. Several Options may be ORed together.
*       returns current options.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrSetOptions (Uint2 option, Uint2 entityID)
{
	ObjMgrPtr omp;
	Uint2Ptr optionptr;
	ObjMgrDataPtr omdp, prev;

	omp = ObjMgrGet();
	if (entityID == 0)
		optionptr = &(omp->options);
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, &prev);
		if (omdp == NULL)
			return 0;
		optionptr = &(omdp->options);
	} 
	*optionptr |= (option & 0xFFFF);
	return *optionptr;
}

/*****************************************************************************
*
*   ObjMgrClearOptions(Uint2 option, Uint2 entityID)
*     Clears options on the ObjMgr. All Options are 1 bit flags set with
*       defines. Several Options may be ORed together.
*       returns current options.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrClearOptions (Uint2 option, Uint2 entityID)
{
	ObjMgrPtr omp;
 	Uint2Ptr optionptr;
	ObjMgrDataPtr omdp, prev;


	omp = ObjMgrGet();
	if (entityID == 0)
		optionptr = &(omp->options);
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, &prev);
		if (omdp == NULL)
			return 0;
		optionptr = &(omdp->options);
	} 
	*optionptr &= ~(option & 0xFFFF);
	return *optionptr;
}

/*****************************************************************************
*
*   ObjMgrGetOptions(entityID)
*     Returns options on the ObjMgr. All Options are 1 bit flags set with
*       defines. Several Options may be ORed together.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrGetOptions (Uint2 entityID)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp, prev;
	Uint2 retval = 0;

	omp = ObjMgrReadLock();
	if (entityID == 0)
		 retval = omp->options;
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, &prev);
		if (omdp != NULL)
			retval = omdp->options;
	}
	ObjMgrUnlock();
	return retval; 
}

/*****************************************************************************
*
*   ObjMgrTestOptions(Uint2 option)
*     Tests options on the ObjMgr. All Options are 1 bit flags set with
*       defines. Several Options may be ORed together.
*       returns TRUE if ANY flags in "option" are set in the ObjMgr.
*       Normally one would supply only 1 option to test. If many were to
*       be tested, use ObjMgrGetOptions()
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrTestOptions (Uint2 option, Uint2 entityID)
{
	ObjMgrPtr omp;
	Uint2Ptr optionptr;
	ObjMgrDataPtr omdp, prev;
	Boolean retval = FALSE;

	omp = ObjMgrReadLock();
	if (entityID == 0)
		optionptr = &(omp->options);
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, &prev);
		if (omdp == NULL)
			goto erret;
		optionptr = &(omdp->options);
	}
	
	if (*optionptr & (option & 0xFFFF))
		retval = TRUE;
erret:

	ObjMgrUnlock();

	return retval;
}

/*****************************************************************************
*
*   ObjMgrSetHold()
*     Increments hold count in ObjMgr
*       returns current hold count
*     if hold != 0, then no tempload records are cached out of memory
*     This is useful if you are doing a complex task involving repeated
*      access to many records and you want them to stay in memory while
*      the task is performed, even though they are loaded temporarily by
*      BioseqLock..
*     Be sure to call ObjMgrClearHold() when you are done so they can be
*      cached out.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrSetHold (void)
{
	ObjMgrPtr omp;

	omp = ObjMgrWriteLock();

	if (omp->hold < OBJMGR_MAX)
		omp->hold++;
	else
		ErrPostEx(SEV_ERROR,0,0, "ObjMgrSetHold: hold > OBJMGR_MAX");
	ObjMgrUnlock();
	return omp->hold;
}

/*****************************************************************************
*
*   ObjMgrClearHold()
*     Decrements hold count in ObjMgr
*       returns current hold count
*       will never decrement below 0
*     if hold == 0, then tempload records will be cached out of memory
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrClearHold (void)
{
	ObjMgrPtr omp;

	omp = ObjMgrWriteLock();

	if (omp->hold)
	{
		omp->hold--;
		if (! (omp->hold))  /* down to 0 */
			ObjMgrReap(omp);
	}
	else
		ErrPostEx(SEV_ERROR,0,0, "ObjMgrClearHold: hold = 0");
	ObjMgrUnlock();
	return omp->hold;
}

/*****************************************************************************
*
*   ObjMgrCheckHold()
*       returns current hold count
*     if hold == 0, then tempload records will be cached out of memory
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrCheckHold (void)
{
	ObjMgrPtr omp;

	omp = ObjMgrReadLock();
	ObjMgrUnlock();
	return omp->hold;
}



/*****************************************************************************
*
*   Data Management Functions
*
*****************************************************************************/


/*****************************************************************************
*
*   ObjMgrExtend(omp)
*
*****************************************************************************/
static Boolean NEAR ObjMgrExtend (ObjMgrPtr omp)
{
	Boolean result = FALSE;
	OMDataPtr omdp, prev=NULL;
	ObjMgrDataPtr PNTR tmp;
	Int4 i, j;

	for (omdp = omp->ncbidata; omdp != NULL; omdp = omdp->next)
		prev = omdp;

	omdp = (OMDataPtr)MemNew(sizeof(OMData));
	if (omdp == NULL) return result;
	tmp = (ObjMgrDataPtr PNTR)MemNew((size_t)(sizeof(ObjMgrDataPtr) * (omp->totobj + NUM_OMD)));
	if (tmp == NULL)
	{
		MemFree(omdp);
		return result;
	}

	if (prev != NULL)
	{
		prev->next = omdp;
		MemMove(tmp, omp->datalist, (size_t)(sizeof(ObjMgrDataPtr) * omp->totobj));
		MemFree(omp->datalist);
	}
	else
		omp->ncbidata = omdp;

	j = omp->totobj;

	for (i = 0; i < NUM_OMD; i++, j++)
		tmp[j] = &(omdp->data[i]);

	if (omp->totobj < OBJMGR_MAX - NUM_OMD)
		omp->totobj += NUM_OMD;
	else
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrExtend: incrementing totobj above OBJMGR_MAX");
	omp->datalist = tmp;

	result = TRUE;
	return result;
}
/*****************************************************************************
*
*   ObjMgrAdd(type, data)
*   	adds a pointer (data) of type (type) to the sequence manager
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrAdd (Uint2 type, Pointer data)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	retval = ObjMgrAddFunc(omp, type, data);
	ObjMgrUnlock();
	return retval;
}


/*****************************************************************************
*
*   ObjMgrAddFunc(omp, type, data)
*   	adds a pointer (data) of type (type) to the sequence manager
*
*****************************************************************************/
static Boolean NEAR ObjMgrAddFunc (ObjMgrPtr omp, Uint2 type, Pointer data)
{
	ObjMgrDataPtr omdp;
	ObjMgrDataPtr PNTR omdpp;
	Int4 i, imin, imax;
	Boolean retval = FALSE;
	unsigned long tmp, datai;
	BioseqPtr bsp;
#ifdef DEBUG_OBJMGR
	FILE * fp;

	fp = FileOpen("ObjMgr.log", "a");
#endif

	/* if autoclean is set and above maxobj, remove least recently accessed objects */
	if (omp->autoclean && omp->currobj >= omp->maxobj) {
		ObjMgrReapOne (omp);
		ObjMgrFreeCache (OBJ_MAX); /* only frees unlocked objects */
	}

	if (omp->currobj >= omp->totobj)
	{
		if (! ObjMgrExtend(omp))
			goto erret;
	}

	i = omp->currobj;
	omdpp = omp->datalist;
	omdp = omdpp[i];    /* emptys always at end */
	omdp->options = omp->options;   /* set default options */

	imin = 0;                   /* find where it goes */
	imax = omp->currobj-1;

	datai = (unsigned long)data;

	if ((i) && (datai < (unsigned long)(omdpp[imax]->dataptr)))
	{
		i = (imax + imin) / 2;
		while (imax > imin)
		{
			tmp = (unsigned long)(omdpp[i]->dataptr);
#ifdef DEBUG_OBJMGR
			fprintf(fp, "Sort: i=%d tmp=%ld data=%ld imax=%d imin=%d\n",
				(int)i, (long)tmp, (long)data, (int)imax, (int)imin);
#endif
			if (tmp > datai)
				imax = i - 1;
			else if (tmp < datai)
				imin = i + 1;
			else
				break;
			i = (imax + imin)/2;
		}

#ifdef DEBUG_OBJMGR
			fprintf(fp, "End: i=%d tmp=%ld data=%ld imax=%d imin=%d\n",
				(int)i, (long)tmp, (long)data, (int)imax, (int)imin);
#endif
		if (datai > (unsigned long)(omdpp[i]->dataptr)) /* check for off by 1 */
		{
			i++;
#ifdef DEBUG_OBJMGR
			fprintf(fp, "Adjust: i=%d\n", (int)i);
#endif
		}


		imax = omp->currobj - 1;	 /* open the array */
		while (imax >= i)
		{
			omdpp[imax+1] = omdpp[imax];
			imax--;
		}
	}

	omdpp[i] = omdp;    /* put in the pointer in order */
	if (omp->currobj < OBJMGR_MAX)
		omp->currobj++;     /* got one more */
	else
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrAddFunc: incrementing currobj above OBJMGR_MAX");

	omdp->dataptr = data;  /* fill in the values */
	omdp->datatype = type;
	omdp->touch = ObjMgrTouchCnt();   /* stamp with time */

	if (type == OBJ_BIOSEQ) {
		bsp = (BioseqPtr) data;
		if (bsp != NULL) {
			/* used for feature indexing, rapid delete from SeqID index */
			bsp->omdp = (Pointer) omdp;
		}
	}

#ifdef DEBUG_OBJMGR
	FileClose(fp);
	ObjMgrDump(NULL, "ObjMgrAdd");
#endif
	retval = TRUE;
erret: 
	return retval;
}

/*****************************************************************************
*
*   ObjMgrRegister (datatype, data)
*   	datatype is the datatype of data to register
*   	if data is already registered in ObjMgr, returns entityID
*   	if not, is added to the ObjMgr, returns entityID
*
*   	if (datatype is a choice type, uses data as a ValNodePtr)
*   
*   	on failure returns 0
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrRegister (Uint2 datatype, Pointer data)
{
	Uint2 dtype, retval = 0;
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	ObjMgrTypePtr omtp;
	Pointer ptr;


	if (data == NULL) return retval;

	dtype = datatype;  /* sets the default state */
	ptr = data;

	omp = ObjMgrWriteLock();

	omdp = ObjMgrFindByData(omp, data);  /* already have it? */
	if (omdp != NULL)
	{
		if (! omdp->EntityID)
		{
			if (omdp->parentptr != NULL)
				ErrPostEx(SEV_ERROR,0,0, "ObjMgrRegister: parent != NULL");

			ObjMgrAddEntityID(omp, omdp);
		}

		retval = omdp->EntityID;
		goto erret;
	}
								/* have to add it to ObjMgr */
	omtp = ObjMgrTypeFind(omp, datatype, NULL, NULL);
	if (omtp == NULL)
	{
		ErrPostEx(SEV_ERROR,0,0, "ObjMgrRegister: invalid data type [%d]", (int)datatype);
		goto erret;
	}
      
      /*** this was to register things like SeqEntry.. currently we register data.ptrvalue directly **
      
	if (omtp->fromchoicefunc == NULL)
	{
		dtype = datatype;
		ptr = data;
	}
	else
	{
		vnp = (ValNodePtr)data;
		dtype = (*(omtp->fromchoicefunc))(vnp);
		ptr = vnp->data.ptrvalue;
	}

      ***********************************************************************************************/
      
	ObjMgrAddFunc(omp, dtype, ptr);
	omdp = ObjMgrFindByData(omp, ptr);
	if (omdp == NULL) goto erret;

	ObjMgrAddEntityID(omp, omdp);
	retval = omdp->EntityID;
erret:
	ObjMgrUnlock();

	return retval;
}


/*****************************************************************************
*
*   ObjMgrAddUserData(entityID, procid, proctype, userkey)
*   	creates a new OMUserData struct attached to entityID
*   	if entityID = 0, attaches to the desktop (all objects)
*   	Caller must fill in returned structure
*   	returns NULL on failure
*
*****************************************************************************/
NLM_EXTERN OMUserDataPtr LIBCALL ObjMgrAddUserData (Uint2 entityID, Uint2 procid, Uint2 proctype, Uint2 userkey)
{
	OMUserDataPtr omudp=NULL, tmp;
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;

	omp = ObjMgrWriteLock();

	omudp = MemNew(sizeof(OMUserData));
	omudp->procid = procid;
	omudp->proctype = proctype;
	omudp->userkey = userkey;

	if (entityID == 0)
	{
		if (omp->userdata == NULL)
			omp->userdata = omudp;
		else
		{
			for (tmp = omp->userdata; tmp->next != NULL; tmp = tmp->next)
				continue;
			tmp->next = omudp;
		}
	}
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
		if (omdp == NULL)
			omudp = MemFree(omudp);
		else
		{
			if (omdp->userdata == NULL)
				omdp->userdata = omudp;
			else
			{
				for (tmp = omdp->userdata; tmp->next != NULL; tmp = tmp->next)
					continue;
				tmp->next = omudp;

			}
		}
	}

	ObjMgrUnlock();
	return omudp;
}

/*****************************************************************************
*
*   ObjMgrGetUserData(entityID, procid, proctype, userkey)
*   	Finds an OMUserData struct attached to entityID
*   	if entityID = 0, gets from the desktop
*   	returns NULL on failure
*
*****************************************************************************/
NLM_EXTERN OMUserDataPtr LIBCALL ObjMgrGetUserData (Uint2 entityID, Uint2 procid, Uint2 proctype, Uint2 userkey)
{
	OMUserDataPtr omudp=NULL;
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;

	omp = ObjMgrReadLock();

	if (entityID == 0)
	{
		omudp = omp->userdata;
	}
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
		if (omdp != NULL)
			omudp = omdp->userdata;
	}

	while (omudp != NULL)
	{
		if ((omudp->procid == procid) || (! procid))
		{
			if ((omudp->proctype == proctype) || (! proctype))
			{
				if ((omudp->userkey == userkey) || (! userkey))
					break;
			}
		}
		omudp = omudp->next;
	}

	ObjMgrUnlock();
	return omudp;
}
/*****************************************************************************
*
*   ObjMgrFreeUserData(entityID, procid, proctype, userkey)
*   	frees OMUserData attached to entityID by procid
*       if procid ==0, frees all OMUserData of proctype
*   	if proctype ==0, matches any proctype
*       if userkey == matches any userkey
*   	returns TRUE if any freed
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrFreeUserData (Uint2 entityID, Uint2 procid, Uint2 proctype, Uint2 userkey)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	retval = ObjMgrFreeUserDataFunc(omp, entityID, procid, proctype, userkey);
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrFreeUserDataFunc(omp, entityID, procid, proctype, userkey)
*   	frees OMUserData attached to entityID by procid
*       if procid ==0, frees all OMUserData of proctype
*   	if proctype ==0, matches any proctype
*       if userkey == matches any userkey
*   	returns TRUE if any freed
*
*****************************************************************************/
static Boolean NEAR ObjMgrFreeUserDataFunc (ObjMgrPtr omp, Uint2 entityID,
								Uint2 procid, Uint2 proctype, Uint2 userkey)
{
	OMUserDataPtr omudp=NULL, prev, next;
	ObjMgrDataPtr omdp;
	Boolean got_one = FALSE, view_left;
	ObjMgrTypePtr omtp = NULL;
	Uint2 type, options;
	Pointer ptr;
	Boolean is_write_locked;

	options = omp->options;

	if (entityID == 0)
	{
		omudp = omp->userdata;
	}
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
		if (omdp != NULL)
		{
			omudp = omdp->userdata;
			options = omdp->options;
		}
	}

	prev = NULL;
	view_left = FALSE;  /* assume nothing left to receive messages */
	while (omudp != NULL)
	{
		next = omudp->next;
		if ((omudp->procid == procid) || (! procid))
		{
			if ((omudp->proctype == proctype) || (! proctype))
			{
				if ((omudp->userkey == userkey) || (! userkey))
				{
					got_one = TRUE;
					if (prev == NULL)
					{
						if (entityID == 0)
							omp->userdata = next;
						else
							omdp->userdata = next;
					}
					else
						prev->next = next;
					if (omudp->freefunc != NULL)
						(* (omudp->freefunc))(omudp->userdata.ptrvalue);
					omudp = MemFree(omudp);
				}
			}
		}
		if (omudp != NULL)
		{
			prev = omudp;
			if (omudp->messagefunc != NULL) /* gets messages */
				view_left = TRUE;
		}
		omudp = next;
	}

	if (options & OM_OPT_FREE_IF_NO_VIEW)  /* free data if no more views */
	{
		if ((got_one) && (! view_left) && (omdp != NULL))   /* no more views */
		{
			if (! omdp->being_freed)  /* being freed by ObjMgrDelete */
			{
				if (omdp->choice != NULL)
				{
					type = omdp->choicetype;
					ptr = omdp->choice;
				}
				else
				{
					type = omdp->datatype;
					ptr = omdp->dataptr;
				}

				omtp = ObjMgrTypeFind(omp, type, NULL, NULL);
				if (omtp != NULL)
				{
					omdp->being_freed = TRUE; /* flag not to call this func */
                    /* get rid of extra user data */
                    ObjMgrFreeUserDataFunc(omp, entityID, 0, 0, 0);
					is_write_locked = omp->is_write_locked;
					ObjMgrUnlock();
					(*(omtp->freefunc))(ptr);
					if (is_write_locked)
						ObjMgrWriteLock();
					else
						ObjMgrReadLock();
				}
			}
		}
	}
	return got_one;
}



/*****************************************************************************
*
*   ObjMgrLookup(omp, data)
*   	Binary lookup of data in omp->datalist
*   	returns index (>=0) if found
*       returns -1 if not found
*
*****************************************************************************/
NLM_EXTERN Int4 LIBCALL ObjMgrLookup(ObjMgrPtr omp, Pointer data)
{
	Int4 imin, imax, i;
	ObjMgrDataPtr PNTR omdpp;
	unsigned long tmp, datai;

	imin = 0;
	imax = omp->currobj - 1;
	omdpp = omp->datalist;

	datai = (unsigned long)data;

	while (imax >= imin)
	{
		i = (imax + imin)/2;
		tmp = (unsigned long)(omdpp[i]->dataptr);
		if (tmp > datai)
			imax = i - 1;
		else if (tmp < datai)
			imin = i + 1;
		else
			return i;
	}

	return (Int4)(-1);
}

/*****************************************************************************
*
*   ObjMgrDelete(type, data)
*   	deletes a pointer (data) of type (type) to the sequence manager
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrDelete (Uint2 type, Pointer data)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	ObjMgrDataPtr PNTR omdpp, PNTR to, PNTR from;
	Int4 i, j;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();  /* really remove the entity */
	i = ObjMgrLookup(omp, data);  /* make sure it is still current */
	if (i < 0)  /* not found */
	{
	   /***	may not be registered with objmgr ***
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrDelete: pointer [%ld] type [%d] not found",
			(long)data, (int)type);
		***/
		goto erret;
	}

	omdpp = omp->datalist;
	omdp = omdpp[i];    /* emptys always at end */

	if (omdp == NULL) goto erret;

	if (omdp->EntityID != 0)
	{
		ObjMgrSendMsgFunc(omp, omdp, OM_MSG_DEL, omdp->EntityID, 0, 0, 0, 0, 0, NULL);
		ObjMgrDeSelectFunc(omp, omdp->EntityID,0,0,0,NULL);
		if (! omdp->being_freed)  /* ObjMgrFreeUserData can call delete */
		{
			omdp->being_freed = TRUE;  /* break cycle */
			ObjMgrFreeUserDataFunc(omp, omdp->EntityID, 0, 0, 0);
		}
	}

	if (omdp->clipboard)    /* update the clipboard */
		omp->clipboard = NULL;

	if (omdp->lockcnt)
		ErrPostEx(SEV_ERROR, 0,0,"ObjMgrDelete: deleting locked element");
	else if (omdp->tempload == TL_LOADED)
	{
		if (omp->tempcnt)
			omp->tempcnt--;
		else
			ErrPostEx(SEV_ERROR, 0,0, "ObjMgrDelete: reducing tempcnt below 0");
	}

	/* if (omdp->EntityID != 0 && omdp->EntityID == omp->HighestEntityID)
		omp->HighestEntityID--; */
	if (omdp->EntityID != 0)
		ObjMgrRecycleEntityID (omdp->EntityID, omp);

	ObjMgrDeleteIndexOnEntityID(omp,omdp->EntityID);

	if (omdp->extradata != NULL && omdp->freeextra != NULL) {
		omdp->freeextra ((Pointer) omdp);
	}

	if (omdp->bulkIndexFree) {
		ObjMgrUnlock(); /* if bulk free, delete at end of BioseqFree or BioseqSetFree */
		return TRUE;
	}

	MemSet((Pointer)omdp, 0, sizeof(ObjMgrData));
	if (omp->currobj)
		omp->currobj--;
	else
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrDelete: reducing currobj below 0");
	j = omp->currobj - i;
	if (j)
	{
		to = omdpp + i;
		from = to + 1;
		MemMove(to, from, (size_t)(sizeof(ObjMgrDataPtr) * j));
	}
	omdpp[omp->currobj] = omdp;  /* put in pointer to empty data space */

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrDelete");
#endif

	retval = TRUE;
erret:
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrDeleteAllInRecord()
*   	deletes all omdp entries in a record
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrDeleteAllInRecord (
  void
)

{
  Int4               i, j, k, num;
  ObjMgrDataPtr       omdp;
  ObjMgrDataPtr PNTR  omdpp;
  ObjMgrPtr           omp;
  ObjMgrDataPtr PNTR  tmp;

  omp = ObjMgrWriteLock ();

  if (omp != NULL) {
    omdpp = omp->datalist;
    if (omdpp != NULL) {

      num = omp->currobj;
      tmp = (ObjMgrDataPtr PNTR) MemNew (sizeof (ObjMgrDataPtr) * (size_t) (num + 1));
      if (tmp != NULL) {

        for (i = 0, j = 0, k = 0; i < num; i++) {
          omdp = omdpp [i];
          if (omdp != NULL && omdp->bulkIndexFree) {
            MemSet ((Pointer) omdp, 0, sizeof (ObjMgrData));
            tmp [k] = omdp;
            k++;
          } else {
            omdpp [j] = omdpp [i];
            j++;
          }
        }
        omp->currobj = j;
        MemMove (omdpp + j, tmp, sizeof (ObjMgrDataPtr) * (size_t) k);
      }

      MemFree (tmp);
    }
  }

  ObjMgrUnlock ();

  return TRUE;
}

/*****************************************************************************
*
*   ObjMgrGetClipBoard()
*     returns ObjMgrDataPtr to current clipboard object or NULL if none
*
*****************************************************************************/
NLM_EXTERN ObjMgrDataPtr LIBCALL ObjMgrGetClipBoard (void)
{
	ObjMgrPtr omp;
	
	omp = ObjMgrReadLock();
	ObjMgrUnlock();
	return omp->clipboard;
}

/*****************************************************************************
*
*   ObjMgrAddToClipBoard(entityID, ptr)
*   	if entityID > 0, then uses it.
*   	else, looks up entityID using ptr
*       adds entityID if needed
*       sends OM_MSG_TO_CLIPBOARD
*
*   	Anything in the clipboard is deleted
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrAddToClipBoard (Uint2 entityID, Pointer ptr)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	if (! entityID)
	{
		omdp = ObjMgrFindByData(omp, ptr);
		if (omdp != NULL)
		{
			if (omdp->parentptr != NULL)
			{
				ErrPostEx(SEV_ERROR,0,0,"AddToClipBoard: ParentPtr != NULL");
				goto erret;
			}
			/* if (omdp->EntityID == 0)
				omdp->EntityID = ++(omp->HighestEntityID); */
			if (omdp->EntityID == 0) {
				omdp->EntityID = ObjMgrNextAvailEntityID (omp);
				ObjMgrRecordOmdpByEntityID (omdp->EntityID, omdp);
			}
		}
	}
	else
	{
		omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	}

	if (omdp == NULL)
	{
		ErrPostEx(SEV_ERROR,0,0,"AddToClipBoard: data not found");
		goto erret;
	}

	ObjMgrFreeClipBoardFunc(omp);

	omdp->clipboard = TRUE;
	omp->clipboard = omdp;

	ObjMgrSendMsgFunc(omp, omdp, OM_MSG_TO_CLIPBOARD, omdp->EntityID, 0, 0, 0, 0, 0, NULL);

	retval = TRUE;
erret:
	ObjMgrUnlock();

	return retval;
}

/*****************************************************************************
*
*   ObjMgrFreeClipBoard()
*     clears any data from the clipboard
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrFreeClipBoard (void)
{
	ObjMgrPtr omp;
	Boolean retval;
	
	omp = ObjMgrWriteLock();
	retval = ObjMgrFreeClipBoardFunc(omp);
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrFreeClipBoardFunc()
*     clears any data from the clipboard
*
*****************************************************************************/
static Boolean NEAR ObjMgrFreeClipBoardFunc (ObjMgrPtr omp)
{
	ObjMgrTypePtr omtp;
	ObjMgrDataPtr omdp;
	Uint2 type;
	Pointer ptr;
	
	omdp = omp->clipboard;
	if (omdp == NULL) return TRUE;

	if (omdp->choice != NULL)
	{
		type = omdp->choicetype;
		ptr = omdp->choice;
	}
	else
	{
		type = omdp->datatype;
		ptr = omdp->dataptr;
	}

	omtp = ObjMgrTypeFind(omp, type, NULL, NULL);
	if (omtp == NULL)
	{
		ErrPostEx(SEV_ERROR,0,0,"ObjMgrFreeClipBoard: cant find type [%d]", (int)type);
		return FALSE;
	}
	else
		(*(omtp->freefunc))(ptr);
	ObjMgrDelete(omdp->datatype, omdp->dataptr);
	omp->clipboard = NULL;
	return TRUE;
}
/*****************************************************************************
*
*   ObjMgrConnect (type, data, parenttype, parentdata)
*   	Adds parent info to element
*       Updates EntityID
*   		if both are 0, assigns it by incrementing HighestEntityID
*   		if one is 0, assigns it the other
*           if neither is 0 and not the same ID
*                assigns parent to child (and cascades to its children)
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrConnect (Uint2 type, Pointer data, Uint2 parenttype, Pointer parentdata)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;
	
	omp = ObjMgrWriteLock();
	retval = ObjMgrConnectFunc(omp, type, data, parenttype, parentdata);
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrConnectFunc (type, data, parenttype, parentdata)
*   	Adds parent info to element
*       Updates EntityID
*   		if both are 0, assigns it by incrementing HighestEntityID
*   		if one is 0, assigns it the other
*           if neither is 0 and not the same ID
*                assigns parent to child (and cascades to its children)
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrConnectFunc (ObjMgrPtr omp, Uint2 type, Pointer data, Uint2 parenttype, Pointer parentdata)
{
	Int4 i;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;
	
	i = ObjMgrLookup(omp, data);
	if (i < 0)  /* not found */
	{
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrConnect: pointer [%ld] type [%d] not found",
			(long)data, (int)type);
		goto erret;
	}

	omdp = omp->datalist[i];

	if ((omdp->EntityID != 0) && (parentdata != NULL))
	{                         /* registered type, inform any attached procs */

		ObjMgrSendMsgFunc(omp, omdp, OM_MSG_CONNECT, omdp->EntityID, 0, 0, 0, 0, 0, NULL);
		ObjMgrDeSelectFunc(omp, omdp->EntityID,0,0,0,NULL);
		ObjMgrFreeUserDataFunc(omp, omdp->EntityID, 0, 0, 0);

		/* if (omp->HighestEntityID == omdp->EntityID) {
			(omp->HighestEntityID)--;
		} */
		ObjMgrRecycleEntityID (omdp->EntityID, omp);
		ObjMgrDeleteIndexOnEntityID (omp, omdp->EntityID);
		omdp->EntityID = 0;   /* reset.. now has a parent */
	}
	omdp->parenttype = parenttype;
	omdp->parentptr = parentdata;

	if (parentdata != NULL)
	{
		i = ObjMgrLookup(omp, parentdata);
		if (i < 0)  /* not found */
		{
			ErrPostEx(SEV_ERROR, 0,0, "ObjMgrConnect: parent pointer [%ld] type [%d] not found",
				(long)parentdata, (int)parenttype);
			goto erret;
		}

		
	}

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrConnect");
#endif
	retval = TRUE;
erret:
	return retval;
}
/*****************************************************************************
*
*   ObjMgrDetach (type, data)
*   	Removes parent info from element
*       Adds to objmgr if necessary
*       Does NOT register entity
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrDetach (Uint2 type, Pointer data)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;
	
	omp = ObjMgrWriteLock();
	retval = ObjMgrDetachFunc(omp, type, data);
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrDetach (type, data)
*   	Removes parent info from element
*       Adds to objmgr if necessary
*       Does NOT register entity
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrDetachFunc (ObjMgrPtr omp, Uint2 type, Pointer data)
{
	Int4 i;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;
	
	i = ObjMgrLookup(omp, data);
	if (i < 0)  /* not found */
	{
		retval = ObjMgrAddFunc(omp, type, data);
		goto erret;
	}

	omdp = omp->datalist[i];
	if (omdp->parentptr == NULL)  /* not connected anyway */
		return TRUE;

	omdp->parenttype = 0;
	omdp->parentptr = NULL;
	ObjMgrDeleteIndexOnEntityID (omp, omdp->EntityID);
	omdp->EntityID = 0;   /* reset.. now has a parent */

	retval = TRUE;
erret:
	return retval;
}

/*****************************************************************************
*
*   ObjMgrSetDirtyFlag (entityID, the_flag)
*     record that an entity has been changed but not yet saved.
*       the_flag sets or unsets this state
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSetDirtyFlag (Uint2 entityID, Boolean the_flag)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	if (entityID <= 0) return FALSE;

	omp = ObjMgrWriteLock();
	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp != NULL)
	{
		omdp->dirty = the_flag;
		retval = TRUE;
	}
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrGetDirtyFlag (entityID)
*     returns the state of the dirty flag for entityID
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrGetDirtyFlag (Uint2 entityID)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	if (entityID <= 0) return retval;

	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp != NULL)
		retval = omdp->dirty;
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrSetChoice(type, choice, data)
*   	Adds the ValNodePtr pointing directly to this Bioseq or BioseqSet
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSetChoice (Uint2 type, ValNodePtr choice, Pointer data)
{
	ObjMgrPtr omp;
	Int4 i;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;
	
	omp = ObjMgrWriteLock();

	i = ObjMgrLookup(omp, data);
	if (i < 0)  /* not found */
	{
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrChoice: pointer [%ld] type [%d] not found",
			(long)data, (int)type);
		goto erret;
	}

	omdp = omp->datalist[i];
	omdp->choicetype = type;
	omdp->choice = choice;
	retval = TRUE;
erret:
	ObjMgrUnlock();

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrChoice");
#endif

	return retval;
}

/*****************************************************************************
*
*   ObjMgrFindTop(omp, omdp)
*   	finds the highest parent of omdp
*       returns a ObjMgrDataPtr to the top
*
*****************************************************************************/
NLM_EXTERN ObjMgrDataPtr LIBCALL ObjMgrFindTop (ObjMgrPtr omp, ObjMgrDataPtr omdp)
{
	Int4 i;

	if ((omp == NULL) || (omdp == NULL)) return NULL;

	while (omdp->parentptr != NULL)
	{
		i = ObjMgrLookup(omp, omdp->parentptr);
		if (i < 0)  /* not found */
		{
			ErrPostEx(SEV_ERROR, 0,0, "ObjMgrFindTop: parentptr [%ld] not found",
				(long)(omdp->parentptr));
			return NULL;
		}
		omdp = omp->datalist[i];
	}

	return omdp;
}

/*****************************************************************************
*
*   ObjMgrWholeEntity(omdp, itemID, itemtype)
*		returns TRUE if itemID, itemtype identify a complete entity omdp
*       returns FALSE if these are an internal part of the entity
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrWholeEntity (ObjMgrDataPtr omdp, Uint4 itemID, Uint2 itemtype)
{
	if (omdp == NULL)
		return FALSE;

	if ((itemID == 0) && (itemtype == 0))
		return TRUE;

	if ((itemID == 1) && (itemtype == omdp->datatype))
		return TRUE;

	return FALSE;

}

/*****************************************************************************
*
*   ObjMgrFreeCache(type)
*   	Frees all cached objects of type and subtypes of type
*   		based on ObjMgrMatch()
*   	if type == 0, frees all cached objects
*   	returns TRUE if no errors occurred
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrFreeCache (Uint2 type)
{
	ObjMgrPtr omp;
	Boolean more_to_go = TRUE;
	Uint2 rettype = 0;
	VoidPtr retval = NULL;

	while (more_to_go)
        {
           omp = ObjMgrWriteLock();
/*
                omp = ObjMgrReadLock ();
*/
                more_to_go = ObjMgrFreeCacheFunc(omp, type, &rettype, &retval);
		if (more_to_go)
		{
			switch (rettype)
			{
			case OBJ_SEQENTRY:
				SeqEntryFree((SeqEntryPtr)retval);
				break;
			case OBJ_BIOSEQ:
				BioseqFree((BioseqPtr)retval);
				break;
			case OBJ_BIOSEQSET:
				BioseqSetFree((BioseqSetPtr)retval);
				break;
			default:
				break;
			}
		}
		ObjMgrUnlock();
	}

        /*        if(type == 0)
                  omp->HighestEntityID = 0; */
        
	return TRUE;
}

/*****************************************************************************
*
*   ObjMgrFreeCacheFunc(type)
*   	Finds next cached objects of type and subtypes of type
*   		based on ObjMgrMatch()
*   	if type == 0, frees all cached objects
*   	if type == OBJ_MAX, frees unlocked cached objects
*   	returns TRUE if no more found
*
*****************************************************************************/
static Boolean NEAR ObjMgrFreeCacheFunc (ObjMgrPtr omp, Uint2 type, Uint2Ptr rettype, VoidPtr PNTR retval)
{
  Int4 i, num;
	ObjMgrDataPtr PNTR omdpp, omdp;

	if (omp->hold)   /* things are being held */
		return FALSE;   /* nothing to do, but send a warning */

	omdpp = omp->datalist;
	num = omp->currobj;
    for (i = 0; i < num; i++)
    {
		omdp = omdpp[i];
		if ((omdp->parentptr == NULL) &&     /* top level */
			(omdp->tempload == TL_CACHED ||   /* cached or */
			(type == 0 && omdp->tempload == TL_LOADED) ||   /* temp loaded but not cached out */
			(type == OBJ_MAX && omdp->tempload == TL_LOADED && omdp->lockcnt == 0)))   /* unlocked but not cached out */
		{
			if (type == 0 || type == OBJ_MAX ||
				(ObjMgrMatch(type, omdp->datatype)) ||
				(ObjMgrMatch(type, omdp->choicetype)))
			{
				/** here is where the freefunc should be called **/

				if (omdp->choice != NULL)
				{
					switch (omdp->choicetype)
					{
						case OBJ_SEQENTRY:
							*rettype = omdp->choicetype;
							*retval = omdp->choice;
							return TRUE;
						default:
							ErrPostEx(SEV_ERROR,0,0, "ObjMgrFreeCache: unsupported choicetype[%d]",
								(int)(omdp->choicetype));
							break;
					}
				}
				else
				{
					switch(omdp->datatype)
					{
						case OBJ_BIOSEQ:
						case OBJ_BIOSEQSET:
							*rettype = omdp->datatype;
							*retval = omdp->dataptr;
							return TRUE;
						default:
							ErrPostEx(SEV_ERROR,0,0,"ObjMgrFreeCache: usupported datatype [%d]",
								(int)(omdp->datatype));
							break;
					}
				}
			}
		}
    }
    return FALSE;

}


/*****************************************************************************
*
*   ObjMgrMatch(type1, type2)
*   	returns 0 if no match
*   	1 if identical
*   	2 if 2 is a subset of 1   (e.g. 1=OBJ_SEQENTRY, 2=BIOSEQ)
*       current type1 that can have subtypes are:
*   		OBJ_SEQENTRY
*   		OBJ_PUB
*   		OBJ_SEQANNOT
*   		OBJ_SEQCODE_SET
*   		OBJ_GENETIC_CODE_SET
*
*****************************************************************************/
NLM_EXTERN Int2 LIBCALL ObjMgrMatch (Uint2 type1, Uint2 type2)
{
	if (type1 == type2)
		return 1;

	switch (type1)
	{
		case OBJ_SEQENTRY:
			switch (type2)
			{
				case OBJ_BIOSEQ:
				case OBJ_BIOSEQSET:
					return 2;
			}
			break;
		case OBJ_SEQANNOT:
			switch (type2)
			{
				case OBJ_SEQFEAT:
				case OBJ_SEQALIGN:
				case OBJ_SEQGRAPH:
					return 2;
			}
			break;
		case OBJ_SEQCODE_SET:
			switch (type2)
			{
				case OBJ_SEQCODE:
					return 2;
			}
			break;
		case OBJ_GENETIC_CODE_SET:
			switch (type2)
			{
				case OBJ_GENETIC_CODE:
					return 2;
			}
			break;
		default:
			break;
	}

	return 0;
}


/*****************************************************************************
*
*   ObjMgrGetChoiceForData(data)
*   	returns ValNodePtr for a BioseqPtr or BioseqSetPtr
*       choice must have been put in ObjMgr using ObjMgrChoice
*       the Bioseq/BioseqSets it is a part of must also be in ObjMgr
*       returns NULL on failure.
*
*****************************************************************************/
NLM_EXTERN ValNodePtr LIBCALL ObjMgrGetChoiceForData (Pointer data)
{
	ObjMgrPtr omp;
	Int4 i;
	ValNodePtr choice = NULL;

	if (data == NULL) return choice;
	omp = ObjMgrReadLock();
	i = ObjMgrLookup(omp, data);
	if (i < 0)
	{
		ErrPostEx(SEV_ERROR, 0,0, "ChoiceGetChoiceForData: data not in ObjMgr");
	}
	else
	{
		choice = omp->datalist[i]->choice;
	}
	ObjMgrUnlock();

	return choice;
}

/*****************************************************************************
*
*   ObjMgrGetEntityIDForChoice(choice)
*   	returns the EntityID for a ValNodePtr
*       choice must have been put in ObjMgr using ObjMgrChoice
*       the Bioseq/BioseqSets it is a part of must also be in ObjMgr
*       This function will move up to the top of the Choice tree it may be
*          in. If top level EntityID is 0, one is assigned at this point.
*       If an element is moved under a different hierarchy, its EntityID will
*          change.
*       returns 0 on failure.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrGetEntityIDForChoice (ValNodePtr choice)
{
	Pointer data;

	if (choice == NULL) return 0;
	data = choice->data.ptrvalue;
	if (data == NULL) return 0;

	return ObjMgrGetEntityIDForPointer (data);
}

/*****************************************************************************
*
*   ObjMgrGetEntityIDForPointer(data)
*   	returns the EntityID for any pointer, Choice or Data
*       This function will move up to the top of the tree it may be
*          in. If top level EntityID is 0, one is assigned at this point.
*       If an element is moved under a different hierarchy, its EntityID will
*          change.
*
*       Either ObjMgrGetEntityIDForPointer() or ObjMgrGetEntityIDForChoice()
*   		MUST be called to have an OM_MSG_CREATE message sent to any
*           registered proceedures
*   
*       returns 0 on failure.
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrGetEntityIDForPointer (Pointer ptr)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Uint2 retval = 0;

	if (ptr == NULL)
		return retval;

	omp = ObjMgrWriteLock(); /* (EY) */
	omdp = ObjMgrFindByData(omp, ptr);

	if (omdp == NULL) goto erret;

	omdp = ObjMgrFindTop(omp, omdp);
	if (omdp == NULL) goto erret;

	if (omdp->EntityID == 0)     /* need to assign it */
		ObjMgrAddEntityID(omp, omdp);

	retval = omdp->EntityID;
erret:
	ObjMgrUnlock();

	return retval;
}

/*****************************************************************************
*
*   ObjMgrGetChoiceForEntityID (id)
*
*****************************************************************************/
NLM_EXTERN ValNodePtr LIBCALL ObjMgrGetChoiceForEntityID (Uint2 id)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	ValNodePtr retval = NULL;

	if (id <= 0) return retval;

	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByEntityID(omp, id, NULL);
	if (omdp != NULL)
		retval = omdp->choice;
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrLock(type, data, lockit)
*   	if lockit=TRUE, locks the element
*       else unlocks it
*       returns the current lock count or -1 on failure
*
*****************************************************************************/
NLM_EXTERN Int4 LIBCALL ObjMgrLock (Uint2 type, Pointer data, Boolean lockit)
{
	ObjMgrPtr omp;
	Int4 retval = -1;
	
	omp = ObjMgrWriteLock();
	if (omp != NULL)
		retval = ObjMgrLockFunc(omp, type, data, lockit);
	ObjMgrUnlock();
	return retval;
}

static Int4 NEAR ObjMgrLockFunc (ObjMgrPtr omp, Uint2 type, Pointer data, Boolean lockit)
{
	Int4 i, lockcnt = -1;
	ObjMgrDataPtr omdp;

#ifdef DEBUG_OBJMGR
	Char buf[80];
#endif

	i = ObjMgrLookup(omp, data);
	if (i < 0)  /* not found */
	{
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrLock: pointer [%ld] type [%d] not found",
			(long)data, (int)type);
		return lockcnt;
	}

	omdp = ObjMgrFindTop(omp, omp->datalist[i]);

	if (lockit) {
		omdp->lockcnt++;

		/******** desktop can do this *******
		if (omdp->tempload == TL_CACHED)
		{
			ErrPostEx(SEV_ERROR, 0,0,"ObjMgrLock: locking a cached entity");
		}
		*************************************/
	}
	else
	{
		if (omdp->lockcnt) {
			omdp->lockcnt--;
		}
		else
		{
			ErrPostEx(SEV_ERROR, 0,0,"ObjMgrLock: unlocking 0 lockcnt");
			return lockcnt;
		}
	}

	lockcnt = omdp->lockcnt;

	if (! lockit)     /* check for automatic delete */
	{
		if ((omdp->tempload != TL_NOT_TEMP) && (! omdp->lockcnt))
		{
			omdp->touch = ObjMgrTouchCnt();   /* stamp with time */
			/*
			omp->tempcnt++;
			*/
			ObjMgrReap(omp);
		}
	}


#ifdef DEBUG_OBJMGR
	if (lockit)
		sprintf(buf, "ObjMgrLock   Lock [%d]", (int)i);
	else
		sprintf(buf, "ObjMgrLock   Unlock [%d]", (int)i);
	ObjMgrDump(NULL, buf);
#endif

	return lockcnt;
}

/*****************************************************************************
*
*   Boolean ObjMgrSetTempLoad (ObjMgrPtr omp, Pointer ptr)
*   Sets a Loaded element to "temporary"
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSetTempLoad (ObjMgrPtr omp, Pointer ptr)
{
	Int4 index;
	ObjMgrDataPtr omdp;

	index = ObjMgrLookup(omp, ptr);
	if (index < 0) return FALSE;

	omdp = ObjMgrFindTop(omp, omp->datalist[index]);
	if (omdp == NULL) return FALSE;

	if (omdp->tempload == TL_NOT_TEMP)
	{
		omdp->tempload = TL_LOADED;
		omp->tempcnt++;
	}
	omdp->touch = ObjMgrTouchCnt();

	omdp->lockcnt++;    /* protect against reaping this one */
	ObjMgrReap (omp);   /* check to see if we need to reap */
	omdp->lockcnt--;    /* reset to previous state */

	return TRUE;
}

/*****************************************************************************
*
*   ObjMgrReap(omp)
*   	Checks to see if memory needs to be cleared, and does it
*
*****************************************************************************/
static int LIBCALLBACK SortDitchArrayByTouchTime (
  VoidPtr vp1,
  VoidPtr vp2
)

{
  ObjMgrDataPtr  omdpp1, omdpp2;

  if (vp1 == NULL || vp2 == NULL) return 0;
  omdpp1 = *((ObjMgrDataPtr PNTR) vp1);
  omdpp2 = *((ObjMgrDataPtr PNTR) vp2);
  if (omdpp1 == NULL || omdpp2 == NULL) return 0;

  if (omdpp1->touch > omdpp2->touch) return 1;
  if (omdpp1->touch < omdpp2->touch) return -1;

  return 0;
}

NLM_EXTERN Boolean LIBCALL ObjMgrReap (ObjMgrPtr omp)
{
	Uint4 lowest;
	Int4 num, j;
	Uint2 tempcnt, i, k;
	ObjMgrDataPtr tmp, ditch, PNTR omdpp, PNTR ditcharray;
	Boolean is_write_locked, did_one = FALSE;

	if (omp->hold)      /* keep all tempload records around while hold is on */
		return FALSE;

	if (omp->reaping)   /* protect against recursion caused by ObjMgrSendMsg */
		return FALSE;

    tempcnt = 0;
	num = omp->currobj;
	omdpp = omp->datalist;
	for (j = 0; j < num; j++, omdpp++)
	{
		tmp = *omdpp;
		if ((tmp->tempload == TL_LOADED) && (! tmp->lockcnt))
		{
			tempcnt++;
		}
	}

    if (tempcnt <= omp->maxtemp) return TRUE;

    /* faster version */

    ditcharray = (ObjMgrDataPtr PNTR) MemNew (sizeof (ObjMgrDataPtr) * (tempcnt + 1));
    if (ditcharray != NULL) {

        tempcnt = 0;
		num = omp->currobj;
		omdpp = omp->datalist;
		for (j = 0; j < num; j++, omdpp++) {
			tmp = *omdpp;
			if ((tmp->tempload == TL_LOADED) && (! tmp->lockcnt)) {
			    ditcharray [(int) tempcnt] = tmp;
			    tempcnt++;
			}
		}

        HeapSort (ditcharray, (size_t) tempcnt, sizeof (ObjMgrDataPtr), SortDitchArrayByTouchTime);

        for (i = 0, k = tempcnt; i < tempcnt && k > omp->maxtemp; i++, k--) {
            ditch = ditcharray [(int) i];
            if (ditch == NULL) continue;

		    omp->reaping = TRUE;
		    ditch->tempload = TL_CACHED;
		    ObjMgrSendMsgFunc(omp, ditch, OM_MSG_CACHED, ditch->EntityID, 0, 0, 0, 0, 0, NULL);
		    omp->tempcnt--;
		    is_write_locked = omp->is_write_locked;

		    /* null out feature pointers in seqmgr feature indices via reap function */

		    if (ditch->extradata != NULL && ditch->reapextra != NULL) {
		    	ditch->reapextra ((Pointer) ditch);
		    }

		    if (ditch->choice != NULL) {
		    	switch (ditch->choicetype) {
			    	case OBJ_SEQENTRY:
				    	did_one = TRUE;
				    	ObjMgrUnlock();
				    	SeqEntryFreeComponents(ditch->choice);
					    break;
				    default:
				    	ErrPostEx(SEV_ERROR,0,0,"ObjMgrReap: ditching unknown type");
				    	break;
			    }
		    } else {
		    	switch (ditch->datatype) {
			    	case OBJ_BIOSEQ:
				    	did_one = TRUE;
					    ObjMgrUnlock();
					    BioseqFreeComponents((BioseqPtr)(ditch->dataptr));
					    break;
				    case OBJ_BIOSEQSET:
				    	did_one = TRUE;
				      	ObjMgrUnlock();
			    		BioseqSetFreeComponents((BioseqSetPtr)(ditch->dataptr), FALSE);
				    	break;
				    default:
				    	ErrPostEx(SEV_ERROR,0,0,"ObjMgrReap: ditching unknown type");
				    	break;
			    }
		    }

		    if (did_one) {
		    	if (is_write_locked) {
			    	omp = ObjMgrWriteLock();
		    	} else {
			    	omp = ObjMgrReadLock();
			    }
		    }
		
		    omp->reaping = FALSE;
        }

        MemFree (ditcharray);
        return TRUE;
    }

    /* otherwise fall through to old less efficient code */

	while (/* omp-> */ tempcnt > omp->maxtemp)   /* time to reap */
	{
		lowest = UINT4_MAX;

        tempcnt = 0;
		num = omp->currobj;
		omdpp = omp->datalist;
		ditch = NULL;
		for (j = 0; j < num; j++, omdpp++)
		{
			tmp = *omdpp;
			if ((tmp->tempload == TL_LOADED) && (! tmp->lockcnt))
			{
			    tempcnt++;
				if (lowest > tmp->touch)
				{
					lowest = tmp->touch;
					ditch = tmp;
				}
			}
		}
		if (ditch == NULL)    /* nothing to free */
			return FALSE;

		omp->reaping = TRUE;
		ditch->tempload = TL_CACHED;
		ObjMgrSendMsgFunc(omp, ditch, OM_MSG_CACHED, ditch->EntityID, 0, 0, 0, 0, 0, NULL);
		omp->tempcnt--;
		tempcnt--;
		is_write_locked = omp->is_write_locked;

		/* null out feature pointers in seqmgr feature indices via reap function */

		if (ditch->extradata != NULL && ditch->reapextra != NULL) {
			ditch->reapextra ((Pointer) ditch);
		}

		if (ditch->choice != NULL)
		{
			switch (ditch->choicetype)
			{
				case OBJ_SEQENTRY:
					did_one = TRUE;
					ObjMgrUnlock();
					SeqEntryFreeComponents(ditch->choice);
					break;
			}
		}
		else
		{
			switch (ditch->datatype)
			{
				case OBJ_BIOSEQ:
					did_one = TRUE;
					ObjMgrUnlock();
					BioseqFreeComponents((BioseqPtr)(ditch->dataptr));
					break;
				case OBJ_BIOSEQSET:
					did_one = TRUE;
					ObjMgrUnlock();
					BioseqSetFreeComponents((BioseqSetPtr)(ditch->dataptr), FALSE);
					break;
				default:
					ErrPostEx(SEV_ERROR,0,0,"ObjMgrUnlock: ditching unknown type");
					break;
			}
		}

		if (did_one)
		{
			if (is_write_locked)
				omp = ObjMgrWriteLock();
			else
				omp = ObjMgrReadLock();
		}
		
		omp->reaping = FALSE;

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrReap");
#endif

	}

	return TRUE;
}

/*****************************************************************************
*
*   ObjMgrReapOne(omp)
*   	Reaps the single least recently accessed entity
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrReapOne (ObjMgrPtr omp)
{
	Uint4 lowest;
	Int4 num, j;
	ObjMgrDataPtr tmp, ditch, PNTR omdpp;
	Boolean is_write_locked, did_one = FALSE;

	if (omp->hold)      /* keep all tempload records around while hold is on */
		return FALSE;

	if (omp->reaping)   /* protect against recursion caused by ObjMgrSendMsg */
		return FALSE;

		lowest = UINT4_MAX;
		
		num = omp->currobj;
		omdpp = omp->datalist;
		ditch = NULL;
		for (j = 0; j < num; j++, omdpp++)
		{
			tmp = *omdpp;
			if ((tmp->tempload == TL_LOADED) && (! tmp->lockcnt))
			{
				if (lowest > tmp->touch)
				{
					lowest = tmp->touch;
					ditch = tmp;
				}
			}
		}
		if (ditch == NULL)    /* nothing to free */
			return FALSE;

		omp->reaping = TRUE;
		ditch->tempload = TL_CACHED;
		ObjMgrSendMsgFunc(omp, ditch, OM_MSG_CACHED, ditch->EntityID, 0, 0, 0, 0, 0, NULL);
		omp->tempcnt--;
		is_write_locked = omp->is_write_locked;

		/* null out feature pointers in seqmgr feature indices via reap function */

		if (ditch->extradata != NULL && ditch->reapextra != NULL) {
			ditch->reapextra ((Pointer) ditch);
		}

		if (ditch->choice != NULL)
		{
			switch (ditch->choicetype)
			{
				case OBJ_SEQENTRY:
					did_one = TRUE;
					ObjMgrUnlock();
					SeqEntryFreeComponents(ditch->choice);
					break;
			}
		}
		else
		{
			switch (ditch->datatype)
			{
				case OBJ_BIOSEQ:
					did_one = TRUE;
					ObjMgrUnlock();
					BioseqFreeComponents((BioseqPtr)(ditch->dataptr));
					break;
				case OBJ_BIOSEQSET:
					did_one = TRUE;
					ObjMgrUnlock();
					BioseqSetFreeComponents((BioseqSetPtr)(ditch->dataptr), FALSE);
					break;
				default:
					ErrPostEx(SEV_ERROR,0,0,"ObjMgrUnlock: ditching unknown type");
					break;
			}
		}

		if (did_one)
		{
			if (is_write_locked)
				omp = ObjMgrWriteLock();
			else
				omp = ObjMgrReadLock();
		}
		
		omp->reaping = FALSE;

#ifdef DEBUG_OBJMGR
	ObjMgrDump(NULL, "ObjMgrReapOne");
#endif

	return TRUE;
}

/*****************************************************************************
*
*   Boolean ObjMgrIsTemp(data)
*   	returns TRUE if data is a temporarily loaded item
*       data must be BioseqPtr or BioseqSetPtr
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrIsTemp (Pointer data)
{
	ObjMgrPtr omp;
	Int4 i;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;
	
	omp = ObjMgrReadLock();

	i = ObjMgrLookup(omp, data);
	if (i < 0)  /* not found */
	{
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrIsTemp: pointer [%ld] not found",
			(long)data);
		goto erret;
	}

	omdp = ObjMgrFindTop(omp, omp->datalist[i]);
	if (omdp == NULL) goto erret;

	if (omdp->tempload != TL_NOT_TEMP)
		retval = TRUE;
erret:
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   Boolean ObjMgrIsParent(parent, child)
*   	returns TRUE if child is a child of parent
*       if parent = NULL, returns TRUE if child has no parent
*       child must never be NULL
*       returns TRUE if they are the equal
*       data must be BioseqPtr or BioseqSetPtr
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrIsChild (Pointer parent, Pointer child)
{
	ObjMgrPtr omp;
	Int4 i;
	ObjMgrDataPtr omdp, PNTR omdpp;
	Boolean retval = FALSE;

	if (child == NULL) return FALSE;
	if (parent == child) return TRUE;

	omp = ObjMgrReadLock();
	omdpp = omp->datalist;

	i = ObjMgrLookup(omp, child);
	if (i < 0)  /* not found */
	{
		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrIsChild: pointer [%ld] not found",
			(long)child);
		goto erret;
	}

	omdp = omdpp[i];
	if (parent == NULL)
	{
		if (omdp->parentptr == NULL)
			retval = TRUE;
		goto erret;
	}

	while (omdp->parentptr != NULL)
	{
		if (omdp->parentptr == parent)
		{
			retval = TRUE;
			goto erret;
		}
		i = ObjMgrLookup(omp, omdp->parentptr);
		if (i < 0)
			goto erret;
		omdp = omdpp[i];
	}
erret:
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrDump(fp)
*
*****************************************************************************/

NLM_EXTERN void LIBCALL ObjMgrDump (FILE * fp, CharPtr title)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Uint4 i;
	Char buf[80];
	BioseqPtr bsp;
	Boolean close_it = FALSE;
	
	if (fp == NULL)
	{
		fp = FileOpen("ObjMgr.log", "a");
		close_it = TRUE;
	}

	omp = ObjMgrGet();
	fprintf(fp, "\n%s currobj=%d tempcnt=%d\n", title, (int)(omp->currobj),
		(int)(omp->tempcnt));
	for (i = 0; i < omp->currobj; i++)
	{
		omdp = omp->datalist[i];
		fprintf(fp, "[%d] [%d %d %ld] [%d %ld] %ld (%d) %uld\n", (int)i,
		    (int)omdp->EntityID, (int)(omdp->datatype),
			(long)(omdp->dataptr), (int)(omdp->parenttype),
			(long)(omdp->parentptr), (long)(omdp->choice), (int)(omdp->lockcnt),
			(unsigned long)(omdp->touch));
		if ((omdp->datatype == OBJ_BIOSEQ) && (omdp->dataptr != NULL))
		{
			bsp = (BioseqPtr)(omdp->dataptr);
			SeqIdWrite(bsp->id, buf, PRINTID_FASTA_LONG, sizeof (buf));
			fprintf(fp, "[%s %ld]\n", buf, (long)(bsp));
		}
	}
	
	if (close_it)
		FileClose(fp);
	return;
}

/*****************************************************************************
*
*   Procedure Management Functions
*
*****************************************************************************/


/*****************************************************************************
*
*   ObjMgrProcExtend(omp)
*
*****************************************************************************/
static Boolean NEAR ObjMgrProcExtend (ObjMgrPtr omp)
{
	Boolean result = FALSE;
	OMProcPtr omdp, prev=NULL;
	ObjMgrProcPtr PNTR tmp;
	Int4 i, j;

	for (omdp = omp->ncbiproc; omdp != NULL; omdp = omdp->next)
		prev = omdp;

	omdp = (OMProcPtr)MemNew(sizeof(OMProc));
	if (omdp == NULL) return result;
	tmp = (ObjMgrProcPtr PNTR)MemNew((size_t)(sizeof(ObjMgrProcPtr) * (omp->totproc + NUM_OMD)));
	if (tmp == NULL)
	{
		MemFree(omdp);
		return result;
	}

	if (prev != NULL)
	{
		prev->next = omdp;
		MemMove(tmp, omp->proclist, (size_t)(sizeof(ObjMgrProcPtr) *omp->totproc));
		MemFree(omp->proclist);
	}
	else
		omp->ncbiproc = omdp;

	j = omp->totproc;

	for (i = 0; i < NUM_OMD; i++, j++)
		tmp[j] = &(omdp->data[i]);

	omp->totproc += NUM_OMD;
	omp->proclist = tmp;

	result = TRUE;
	return result;
}

/*****************************************************************************
*
*   ObjMgrProcAdd(data, priority)
*   	adds an ObjMgrProc at given priority
*		priority must be > 0
*       highest priority function is called first.
*       if priority == 0 (default)
*   		gets the next highest priority over previous procs of same type
*       if priority == PROC_PRIORITY_HIGHEST
*           is always the highest priority (first one wins)
*   	if priority == PROC_PRIORITY_LOWEST
*           is always the lowest priority
*
*****************************************************************************/
NLM_EXTERN Uint2  LIBCALL ObjMgrProcAdd (ObjMgrProcPtr data, Int2 priority)
{
	ObjMgrPtr omp;
	ObjMgrProcPtr omdp;
	ObjMgrProcPtr PNTR omdpp;
	Int4 i;
	Uint2 procID;
	Uint2 retval = 0;

	omp = ObjMgrWriteLock();

	if (priority == 0)   /* set to next highest */
	{
		omdp = ObjMgrProcFindNext(omp, data->proctype, data->inputtype,
								data->outputtype, NULL);
		if (omdp != NULL)
			priority = omdp->priority + 10;
	}


	if (omp->currproc >= omp->totproc)
	{
		if (! ObjMgrProcExtend(omp))
			goto erret;
	}

	i = omp->currproc;
	omdpp = omp->proclist;
	omdp = omdpp[i];    /* emptys always at end */

	omp->currproc++;     /* got one more */
	procID = ++(omp->HighestProcID);

	MemMove(omdp, data, sizeof(ObjMgrProc));
	omdp->priority = priority;
	omdp->procid = procID;  /* fill in the values */
	omdp->proclabel = StringSave(data->proclabel);
	omdp->procname = StringSave(data->procname);
	omdp->submenu = StringSave(data->submenu);
	retval = procID;
erret:
	ObjMgrUnlock();

	return retval;
}

/*****************************************************************************
*
*   ObjMgrProcLoadEx(proctype, procname, proclabel, inputtype, subinputtype,
*              outputtype, suboutputtype,
*   				data, func, priority, submenu)
*   	adds a new proceedure with these parameters
*   	returns the procid
*   	if a procedure of the same name and type are already loaded
*   		returns the procid of the loaded proc.. does not reload
*
*****************************************************************************/
NLM_EXTERN Uint2 ObjMgrProcLoadEx (Uint2 proctype, CharPtr procname, CharPtr proclabel,
							Uint2 inputtype, Uint2 subinputtype,
							Uint2 outputtype, Uint2 suboutputtype, Pointer userdata,
							ObjMgrGenFunc func, Int2 priority, CharPtr submenu)
{
	ObjMgrPtr omp;
	ObjMgrProcPtr ompp;
	ObjMgrProc ompd;

	omp = ObjMgrWriteLock();
	ompp = ObjMgrProcFind(omp, 0, procname, proctype);
	ObjMgrUnlock();
	if (ompp != NULL)  /* already enabled */
	{
		return ompp->procid;
	}

	ompp = &ompd;
	MemSet((Pointer)ompp, 0, sizeof(ObjMgrProc));
	
	ompp->proctype = proctype;
	ompp->procname = procname;
	ompp->proclabel = proclabel;
	ompp->inputtype = inputtype;
	ompp->subinputtype = subinputtype;
	ompp->outputtype = outputtype;
	ompp->suboutputtype = suboutputtype;
	ompp->procdata = userdata;
	ompp->func = func;
	ompp->submenu = submenu;

	return ObjMgrProcAdd(ompp, priority); /* order determines priority */
	
}

/*****************************************************************************
*
*   ObjMgrProcLoad(proctype, procname, proclabel, inputtype, subinputtype,
*              outputtype, suboutputtype,
*   				data, func, priority)
*   	adds a new proceedure with these parameters
*   	returns the procid
*   	if a procedure of the same name and type are already loaded
*   		returns the procid of the loaded proc.. does not reload
*
*****************************************************************************/
NLM_EXTERN Uint2 ObjMgrProcLoad (Uint2 proctype, CharPtr procname, CharPtr proclabel,
							Uint2 inputtype, Uint2 subinputtype,
							Uint2 outputtype, Uint2 suboutputtype, Pointer userdata,
							ObjMgrGenFunc func, Int2 priority)

{
	return ObjMgrProcLoadEx (proctype, procname, proclabel, inputtype, subinputtype,
							outputtype, suboutputtype, userdata, func, priority, NULL);
}

/*****************************************************************************
*
*   ObjMgrProcLookup(omp, procid)
*   	these are currently just stored in order
*   	returns index (>=0) if found
*       returns -1 if not found
*
*****************************************************************************/
NLM_EXTERN Int4 LIBCALL ObjMgrProcLookup(ObjMgrPtr omp, Uint2 procid)
{
	if (omp == NULL)
		return (Int4)(-1);

	if (procid)
		return (Int4)(procid - 1);
	else
		return (Int4)(-1);
}

/*****************************************************************************
*
*   ObjMgrProcFind(omp, procid, procname, proctype)
*   	if procid != NULL looks for it
*   	else matches on procname and proctype
*   		proctype = 0, matches all proctypes
*
*****************************************************************************/
NLM_EXTERN ObjMgrProcPtr LIBCALL ObjMgrProcFind(ObjMgrPtr omp, Uint2 procid,
										CharPtr procname, Uint2 proctype)
{
	ObjMgrProcPtr ompp=NULL, PNTR omppp, tmp;
	Int4 i, imax;

	omppp = omp->proclist;
	imax = omp->currproc;

	if (procid)   /* procid lookup is different */
	{
		i = ObjMgrProcLookup(omp, procid);
		if (i >= 0)
			ompp = omppp[i];
		return ompp;
	}

	for (i = 0; i < imax; i++)
	{
		tmp = omppp[i];
		if ((! proctype) || (proctype == tmp->proctype))
		{
			if (! StringCmp(procname, tmp->procname))
				return tmp;
		}
	}
	return ompp;
}

/*****************************************************************************
*
*   ObjMgrGetProcID(omp, procname, proctype)
*   	returns procid given procname and proctype
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrGetProcID (ObjMgrPtr omp, CharPtr procname, Uint2 proctype)

{
	ObjMgrProcPtr  ompp;

	ompp = ObjMgrProcFind (omp, 0, procname, proctype);
	if (ompp == NULL) return 0;
	return ompp->procid;
}

/*****************************************************************************
*
*   ObjMgrProcFindNext(omp, proctype, inputtype, outputtype, last)
*   	looks for proctype of highest priority that
*   		matches inputtype and outputtype
*   	0 on proctype or inputtype or outputtype matches any
*   	if last != NULL, then gets next after last
*       if omp == NULL, will ObjMgrReadLock() and Unlock() within the function
*
*****************************************************************************/
NLM_EXTERN ObjMgrProcPtr LIBCALL ObjMgrProcFindNext (ObjMgrPtr tomp, Uint2 proctype,
						Uint2 inputtype, Uint2 outputtype, ObjMgrProcPtr last)
{
	ObjMgrPtr omp;
	ObjMgrProcPtr best = NULL, tmp, retval = NULL;
	Int4 i, bestpriority=-32766, imax, maxpriority=32767;
	ObjMgrProcPtr PNTR omppp;
	Boolean unlock = FALSE;

	if (tomp == NULL)
	{
		omp = ObjMgrReadLock();
		unlock = TRUE;
	}
	else
		omp = tomp;

	omppp = omp->proclist;
	imax = omp->currproc;

	if (last != NULL)
	{
		maxpriority = last->priority;
		i = ObjMgrProcLookup(omp, last->procid);
		i++;
		while (i < imax)  /* find next of equal priority */
		{
			tmp = omppp[i];
			if ((! proctype) || (tmp->proctype == proctype))
			{
				if ((! inputtype) || (tmp->inputtype == inputtype))
				{
					if ((! outputtype) || (tmp->outputtype == outputtype))
					{
						if (tmp->priority == maxpriority)
						{
							retval = tmp;
							goto erret;
						}
					}
				}
			}
			i++;
		}
	}

	for (i = 0; i < imax; i++)  /* find the highest priority less than any previous */
	{
		tmp = omppp[i];
		if ((! proctype) || (tmp->proctype == proctype))
		{
			if ((! inputtype) || (tmp->inputtype == inputtype))
			{
				if ((! outputtype) || (tmp->outputtype == outputtype))
				{
					if ((tmp->priority > bestpriority) &&
						(tmp->priority < maxpriority))
					{
						best = tmp;
						bestpriority = tmp->priority;
					}
				}
			}
		}

	}

	retval = best;
erret:
	if (unlock)
		ObjMgrUnlock();
	return retval;
}

NLM_EXTERN Int2 LIBCALL ObjMgrProcOpen (ObjMgrPtr omp, Uint2 outputtype)
{
	ObjMgrProcPtr currp=NULL;
	Int2 retval = OM_MSG_RET_ERROR;
	Boolean did_one = FALSE;
	OMProcControl ompc;

	MemSet((Pointer)(&ompc), 0, sizeof(OMProcControl));
	ompc.output_itemtype = outputtype;

	while ((currp = ObjMgrProcFindNext(omp, OMPROC_OPEN, 0, outputtype, currp)) != NULL)
	{
		ompc.proc = currp;
		retval = (*(currp->func)) (&ompc);
		did_one = TRUE;
		if (retval == OM_MSG_RET_DONE)
			break;
	}

	if (! did_one)
	{
		ErrPostEx(SEV_ERROR,0,0, "No OPEN function found");
		retval = OM_MSG_RET_ERROR;
	}
	return retval;
}

NLM_EXTERN Uint2 LIBCALL OMGetNextUserKey (void)

{
  ObjMgrPtr  omp;
  Uint2 retval = 0;

  omp = ObjMgrWriteLock ();
  if (omp == NULL) return retval;
  (omp->HighestUserKey)++;
  if (omp->HighestUserKey == 0) {
    (omp->HighestUserKey)++;
  }
  retval = omp->HighestUserKey;
  ObjMgrUnlock();
  return retval;
}

/*****************************************************************************
*
*   Data Type Functions
*
*****************************************************************************/

static Int2 LIBCALLBACK ObjMgrDefaultLabelFunc ( Pointer data, CharPtr buffer, Int2 buflen, Uint1 content)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	ObjMgrTypePtr omtp;
	CharPtr label=NULL;
	static CharPtr defaultlabel="NoLabel";
	Int2 retval = 0;

	label = defaultlabel;
	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByData(omp, data);
	if (omdp != NULL)
	{
		omtp = ObjMgrTypeFind(omp, omdp->datatype, NULL, NULL);
		if (omtp != NULL)
		{
			if (omtp->label != NULL)
				label = omtp->label;
			else if (omtp->name != NULL)
				label = omtp->name;
			else
				label = omtp->asnname;
		}
	}

	retval = LabelCopy(buffer, label, buflen);
	ObjMgrUnlock();
	return retval;
}

static Uint2 LIBCALLBACK ObjMgrDefaultSubTypeFunc (Pointer ptr)
{
	return 0;
}

/*****************************************************************************
*
*   ObjMgrTypeExtend(omp)
*
*****************************************************************************/
static Boolean NEAR ObjMgrTypeExtend (ObjMgrPtr omp)
{
	Boolean result = FALSE;
	OMTypePtr omdp, prev=NULL;
	ObjMgrTypePtr PNTR tmp;
	Int4 i, j;

	for (omdp = omp->ncbitype; omdp != NULL; omdp = omdp->next)
		prev = omdp;

	omdp = (OMTypePtr)MemNew(sizeof(OMType));
	if (omdp == NULL) return result;
	tmp = (ObjMgrTypePtr PNTR)MemNew((size_t)(sizeof(ObjMgrTypePtr) * (omp->tottype + NUM_OMD)));
	if (tmp == NULL)
	{
		MemFree(omdp);
		return result;
	}

	if (prev != NULL)
	{
		prev->next = omdp;
		MemMove(tmp, omp->typelist, (size_t)(sizeof(ObjMgrTypePtr) * omp->tottype));
		MemFree(omp->typelist);
	}
	else
		omp->ncbitype = omdp;

	j = omp->tottype;

	for (i = 0; i < NUM_OMD; i++, j++)
		tmp[j] = &(omdp->data[i]);

	omp->tottype += NUM_OMD;
	omp->typelist = tmp;

	result = TRUE;
	return result;
}

/*****************************************************************************
*
*   ObjMgrTypeAddFunc(omp, type, data)
*   	adds a pointer (data) of type (type) to the sequence manager
*       Currently this MUST be predefined and < OBJ_MAX
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrTypeAdd (ObjMgrTypePtr data)
{
	ObjMgrPtr omp;
	ObjMgrTypePtr omdp;
	ObjMgrTypePtr PNTR omdpp;
	Int4 i, imin, imax;
	Uint2 tmp, type,retval = 0;

	if (data == NULL) return retval;

	omp = ObjMgrWriteLock();

	type = data->datatype;
	if ((! type) || (type >= OBJ_MAX))
	{
		/***
		type = ++(omp->HighestObjMgrType);
	    ***/

		ErrPostEx(SEV_ERROR, 0,0, "ObjMgrTypeAdd: Can't register new object types yet");
		goto erret;
	}

	if (omp->currtype >= omp->tottype)
	{
		if (! ObjMgrTypeExtend(omp))
			goto erret;
	}

	i = omp->currtype;
	omdpp = omp->typelist;
	omdp = omdpp[i];    /* emptys always at end */

	imin = 0;                   /* find where it goes */
	imax = omp->currtype-1;

	if ((i) && (type < omdpp[imax]->datatype))
	{
		i = (imax + imin) / 2;
		while (imax > imin)
		{
			tmp = omdpp[i]->datatype;
			if (tmp > type)
				imax = i - 1;
			else if (tmp < type)
				imin = i + 1;
			else
				break;
			i = (imax + imin)/2;
		}

		if (type > omdpp[i]->datatype)     /* check for off by 1 */
			i++;

		imax = omp->currtype - 1;	 /* open the array */
		while (imax >= i)
		{
			omdpp[imax+1] = omdpp[imax];
			imax--;
		}
	}

	omdpp[i] = omdp;    /* put in the pointer in order */
	omp->currtype++;     /* got one more */

	MemMove(omdp, data, sizeof(ObjMgrType));
	omdp->datatype = type;  /* fill in the values */
	omdp->asnname = StringSave(data->asnname);
	omdp->label = StringSave(data->label);
	omdp->name = StringSave(data->name);
	if (omdp->labelfunc == NULL)
		omdp->labelfunc = ObjMgrDefaultLabelFunc;
	if (omdp->subtypefunc == NULL)
		omdp->subtypefunc = ObjMgrDefaultSubTypeFunc;
	retval = type;
erret:
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrTypeLoad(args)
*
*****************************************************************************/
NLM_EXTERN Uint2 LIBCALL ObjMgrTypeLoad ( Uint2 type, CharPtr asnname,
		CharPtr label, CharPtr name, AsnTypePtr atp, OMNewFunc newfunc,
		AsnReadFunc asnread, AsnWriteFunc asnwrite, OMFreeFunc freefunc,
		OMLabelFunc labelfunc, OMSubTypeFunc subtypefunc)
{
	ObjMgrType omt;
	Uint2 newtype;

	MemSet((Pointer)(&omt), 0, sizeof(ObjMgrType));
	omt.datatype = type;
	omt.asnname = asnname;
	omt.label = label;
	omt.name = name;
	omt.atp = atp;
	omt.newfunc = newfunc;
	omt.asnread = asnread;
	omt.asnwrite = asnwrite;
	omt.freefunc = freefunc;
	omt.labelfunc = labelfunc;
	omt.subtypefunc = subtypefunc;

	newtype = ObjMgrTypeAdd(&omt);

	return newtype;
}

/*****************************************************************************
*
*   ObjMgrTypeLookup(omp, data)
*   	Binary lookup of data in omp->typelist
*   	returns index (>=0) if found
*       returns -1 if not found
*
*****************************************************************************/
NLM_EXTERN Int4 LIBCALL ObjMgrTypeLookup(ObjMgrPtr omp, Uint2 data)
{
	Int4 imin, imax, i;
	ObjMgrTypePtr PNTR omdpp;
	Uint2 tmp;

	imin = 0;
	imax = omp->currtype - 1;
	omdpp = omp->typelist;

	while (imax >= imin)
	{
		i = (imax + imin)/2;
		tmp = omdpp[i]->datatype;
		if (tmp > data)
			imax = i - 1;
		else if (tmp < data)
			imin = i + 1;
		else
			return i;
	}

	return (Int4)(-1);
}

/*****************************************************************************
*
*   ObjMgrTypeFind(omp, type, asnname, name)
*   	returns the objmgrptr by looking for
*   		type: if type != 0
*   		asnname: if asnname != NULL
*   		name: if name != NULL
*       in that order of preference.
*       if omp == NULL, then does temporary read lock in this function
*   	returns NULL if can't match on highest priority key
*
*****************************************************************************/
NLM_EXTERN ObjMgrTypePtr LIBCALL ObjMgrTypeFind (ObjMgrPtr tomp, Uint2 type, CharPtr asnname, CharPtr name)
{
	ObjMgrTypePtr omtp = NULL;
	ObjMgrTypePtr PNTR omdpp;
	Int4 i, imax, result;
	ObjMgrPtr omp;
	Boolean unlock = FALSE;

	if (tomp == NULL)
	{
		omp = ObjMgrReadLock();
		unlock = TRUE;
	}
	else
		omp = tomp;

	omdpp = omp->typelist;
	imax = omp->currtype;
	if (type)
	{
		i = ObjMgrTypeLookup(omp, type);
		if (i >= 0)
			omtp = omdpp[i];
	}
	else
	{
		for (i = 0; i < imax; i++)
		{
			if (asnname != NULL)
				result = StringCmp(omdpp[i]->asnname, asnname);
			else
				result = StringCmp(omdpp[i]->name, name);
			if (! result)
			{
				omtp = omdpp[i];
				break;
			}
		}
	}

	if (unlock)
		ObjMgrUnlock();

	return omtp;
}

/*****************************************************************************
*
*   ObjMgrTypeSetLabelFunc(type, labelfunc)
*   	replaces the labelfunc for type with a new one
*   	can also set it for the first time
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrTypeSetLabelFunc(Uint2 type, OMLabelFunc labelfunc)
{
	ObjMgrPtr omp;
	ObjMgrTypePtr omtp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	omtp = ObjMgrTypeFind(omp, type, NULL, NULL);
	if (omtp != NULL)
	{
		omtp->labelfunc = labelfunc;
		retval = TRUE;
	}
	ObjMgrUnlock();
	return retval;
}

/**************************************************************************
*
* ObjMgrTypeFindNext(omp, omtp)
*    returns next ObjMgrType after omtp
*    Exhaustively traverses registered types if omtp starts as NULL
*
***************************************************************************/		
NLM_EXTERN ObjMgrTypePtr LIBCALL ObjMgrTypeFindNext (ObjMgrPtr omp, ObjMgrTypePtr last)
{
	ObjMgrTypePtr PNTR omdpp;
	Int4 i, imax;
	Boolean got_it = FALSE;

	omdpp = omp->typelist;
	imax = omp->currtype;
	if (last == NULL)   /* take the first one */
		got_it = TRUE;
	for (i = 0; i < imax; i++)
	{
		if (got_it)
			return omdpp[i];
			
		if (omdpp[i] == last)
			got_it = TRUE;
	}
	
	return NULL;
}

/*****************************************************************************
*
*   Selection Functions for data objects
*
*****************************************************************************/
static Boolean NEAR ObjMgrSendSelMsg (ObjMgrPtr omp, Uint2 entityID,
         Uint4 itemID, Uint2 itemtype, Uint1 regiontype, Pointer region)
{
        ObjMgrDataPtr omdp;
        OMMsgStruct ommds;

        omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
        if (omdp == NULL)
           return FALSE;

        MemSet((Pointer)(&ommds), 0, sizeof(OMMsgStruct));
        ommds.message = OM_MSG_SELECT;
        ommds.entityID = entityID;
        ommds.itemtype = itemtype;
        ommds.itemID = itemID;
        ommds.regiontype = regiontype;
        ommds.region = region;

        ObjMgrSendStructMsgFunc(omp, omdp, &ommds);
	return TRUE;
}

static Boolean NEAR ObjMgrSendDeSelMsg (ObjMgrPtr omp, Uint2 entityID,
         Uint4 itemID, Uint2 itemtype, Uint1 regiontype, Pointer region)
{
        ObjMgrDataPtr omdp;
        OMMsgStruct ommds;

        omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
        if (omdp == NULL)
           return FALSE;

        MemSet((Pointer)(&ommds), 0, sizeof(OMMsgStruct));
        ommds.message = OM_MSG_DESELECT;
        ommds.entityID = entityID;
        ommds.itemtype = itemtype;
        ommds.itemID = itemID;
        ommds.regiontype = regiontype;
        ommds.region = region;

	ObjMgrSendStructMsgFunc(omp, omdp, &ommds);
	return TRUE;
}

static SelStructPtr NEAR ObjMgrAddSelStruct (ObjMgrPtr omp, Uint2 entityID,
		 Uint4 itemID, Uint2 itemtype, Uint1 regiontype, Pointer region)
{
	SelStructPtr ssp, tmp;

	tmp = omp->sel;

	if (tmp != NULL)
	{
		while (tmp->next != NULL)
			tmp = tmp->next;
	}

	ssp = MemNew(sizeof(SelStruct));
	if (tmp != NULL)
	{
		tmp->next = ssp;
		ssp->prev = tmp;
	}
	else
		omp->sel = ssp;

	ssp->entityID = entityID;
	ssp->itemID = itemID;
	ssp->itemtype = itemtype;
	ssp->regiontype = regiontype;
	ssp->region = region;

	return ssp;
}


static Boolean NEAR ObjMgrDeSelectStructFunc (ObjMgrPtr omp, SelStructPtr ssp)
{
	SelStructPtr next, prev;

	if (ssp == NULL) 
		return FALSE;

	next = ssp->next;
	prev = ssp->prev;
	if (prev != NULL)
		prev->next = next;
	else
		omp->sel = next;
	if (next != NULL)
		next->prev = prev;

        ObjMgrSendDeSelMsg (omp, ssp->entityID, ssp->itemID, ssp->itemtype, ssp->regiontype, ssp->region);

	ObjMgrRegionFree(omp, ssp->regiontype, ssp->region); /* free any region */

	MemFree(ssp);

	return TRUE;
}

static Boolean NEAR ObjMgrDeSelectAllFunc (ObjMgrPtr omp)
{
	SelStructPtr tmp, prev;

	tmp = omp->sel;
	if (tmp == NULL)
		return TRUE;

	prev = NULL;

	while (tmp->next != NULL)
	{
		prev = tmp;
		tmp = tmp->next;
	}

	while (tmp != NULL)
	{
		ObjMgrDeSelectStructFunc(omp, tmp);
		tmp = prev;
		if (tmp != NULL)
			prev = tmp->prev;
	}

	return TRUE;
}

static Boolean NEAR ObjMgrRegionMatch (Uint1 regiontype1, Pointer region1,
					Uint1 regiontype2, Pointer region2)
{
	SeqLocPtr slp;
	SeqIntPtr sip1, sip2;

	if (regiontype1 != regiontype2) return FALSE;
	if (! regiontype1) return FALSE;
	if ((region1 == NULL) || (region2 == NULL))	 return FALSE;

	switch (regiontype1)
	{
		case OM_REGION_SEQLOC:    /* SeqLocs of type SeqInt */
			slp = (SeqLocPtr)region1;
			sip1 = (SeqIntPtr)(slp->data.ptrvalue);
			slp = (SeqLocPtr)(region2);
			sip2 = (SeqIntPtr)(slp->data.ptrvalue);
			if (sip1 == NULL || sip2 == NULL) return FALSE;
			if ( ((sip1->from == sip2->from) &&
				(sip1->to == sip2->to)))
                       /* get rid of (sip1->strand == sip2->strand) */
					return TRUE;
			break;
		default:
			break;
	}
	return FALSE;	
}

/*
static Pointer NEAR ObjMgrRegionCopy (ObjMgrPtr omp, Uint1 regiontype, Pointer region)
{
	Pointer newregion = NULL;

	if (region != NULL)
	{
		switch (regiontype)
		{
			case OM_REGION_SEQLOC:
				newregion = ObjMgrMemCopyFunc(omp, OBJ_SEQLOC, region, FALSE);
				break;
			default:
				break;
		}
	}
	return newregion;
}
*/

static Pointer NEAR ObjMgrRegionFree (ObjMgrPtr omp, Uint1 regiontype, Pointer region)
{
	if (region != NULL)
	{
		switch (regiontype)
		{
			case OM_REGION_SEQLOC:
				ObjMgrFreeFunc(omp, OBJ_SEQLOC, region, FALSE);
				break;
			default:
				break;
		}
	}

	return NULL;
}

/*****************************************************************************
*
*   ObjMgrSelect(entityID, itemID, itemtype)
*      if entityID == 0, just deselects everything
*      if entityID,itemID, itemtype already selected, deselects everything else
*      if something else selected, deselects it first, then selects requested
*        item
*      returns TRUE if item is now currently selected, FALSE if not
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSelect (Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	retval = ObjMgrSelectFunc(omp, entityID, itemID, itemtype,
		        regiontype, region);
	ObjMgrUnlock();
	return retval;
}


/*****************************************************************************
*
*   ObjMgrSelect(entityID, itemID, itemtype)
*      if entityID == 0, just deselects everything
*      if entityID,itemID, itemtype already selected, deselects everything else
*      if something else selected, deselects it first, then selects requested
*        item
*      returns TRUE if item is now currently selected, FALSE if not
*
*****************************************************************************/
static Boolean NEAR ObjMgrSelectFunc (ObjMgrPtr omp, Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region)
{
	SelStructPtr ssp, next;
	Boolean was_selected = FALSE, is_selected, remove_this, retval = TRUE;


	if (entityID == 0)    /* desktop */
	{
		ObjMgrDeSelectAllFunc(omp);
		goto erret;
	}
	else
	{
		ssp = omp->sel;
		while (ssp != NULL)
		{
			remove_this = TRUE;
			next = ssp->next;
			if (ssp->entityID == entityID)
			{
				if ((ssp->itemID == itemID) || (! itemID))
				{
					if ((ssp->itemtype == itemtype) || (! itemtype))
					{
						if ((ssp->regiontype == regiontype) || (! regiontype))
						{
							is_selected = TRUE;
							if (regiontype)
								is_selected = ObjMgrRegionMatch(ssp->regiontype,
									ssp->region, regiontype, region);
							if (is_selected)
							{
								remove_this = FALSE;
								was_selected = TRUE;
							}
						}
					}
				}
			}
			if (remove_this)
				ObjMgrDeSelectStructFunc(omp, ssp);

			ssp = next;
		}
	}

	if (was_selected)  /* no more action needed */
	{
		retval = TRUE;
	}
	else
	{
		ssp = ObjMgrAddSelStruct(omp, entityID, itemID, itemtype, regiontype, region);

		if (ssp != NULL)
			retval = ObjMgrSendSelMsg(omp, entityID, itemID, itemtype, regiontype, region);;
	}

erret:
	return retval;

}

static SeqLocPtr SeqLocChangeIntervalle (SeqLocPtr slp, Int4 start, Int4 stop)
{
        SeqIntPtr sip;

        sip = (SeqIntPtr)(slp->data.ptrvalue);
        if (start > -1)
                sip->from = start;
        if (stop > -1)
                sip->to = stop;
        return slp;
}

static Int2 NEAR ObjMgrRegionComp (Pointer region1,Pointer region2, Boolean direction2_plus)
{
        SeqLocPtr slp1, slp2;
        Int2      res;

        if ((region1 == NULL) || (region2 == NULL))      
           return FALSE;

        slp1 = (SeqLocPtr)region1;
        slp2 = (SeqLocPtr)region2;
        res = SeqLocCompare (slp1,slp2);
        if (res == SLC_NO_MATCH)
        {
           if (SeqLocStop(slp1) == SeqLocStart(slp2)-1)
              return 2;
           if (SeqLocStart(slp1) == SeqLocStop(slp2)+1)
              return 3;
           return 0;
        }
        if (res == SLC_A_EQ_B)
           return 1;
        if (res == SLC_B_IN_A)
        {
	   if (SeqLocStart(slp1)==SeqLocStart(slp2))
              return 5;
           if (SeqLocStop(slp1)==SeqLocStop(slp2))
              return 6;
           if (direction2_plus)
              return 5;
           else
              return 6;
        }
        if (res == SLC_A_IN_B)
           return 4;
        if (res == SLC_A_OVERLAP_B)
        {
           if (SeqLocStart(slp1) < SeqLocStart(slp2))
              return 2;
           if (SeqLocStart(slp1) > SeqLocStart(slp2))
              return 3;
        }
        return 0;
}


static Boolean NEAR ObjMgrSelectFunc2 
(ObjMgrPtr omp, Uint2 entityID, Uint4 itemID, Uint2 itemtype, Uint1 regiontype, Pointer region)
{
        SelStructPtr ssp, next;
        Boolean      retval = FALSE;
	SeqLocPtr    tmp = NULL,
                     tmp2 = NULL;
        Int2         res=0;
	Boolean direction_plus = TRUE;

        if (entityID == 0)    /* desktop */
        {
                ObjMgrDeSelectAllFunc(omp);
                goto erret;
        }
        else if (region)
        {
/***** Colombe Patrick ****/
/** send Seq_strand_minus seqloc of selection when the cursor moves to the left **/
/** the Seq_strand_minus is now change to Seq_strand_plus     for ever **/

           if (SeqLocStrand(region)==Seq_strand_minus)
	   {
SeqIntPtr sit;
SeqLocPtr slp;
      	      slp = (SeqLocPtr)region;
	      sit = (SeqIntPtr)(slp->data.ptrvalue);
	      sit->strand = Seq_strand_plus;
              direction_plus=FALSE;
	   }
/***** Colombe Patrick ****/
                ssp = omp->sel;
                while (ssp != NULL && res == 0)
                {
                        next = ssp->next;
                        if (ssp->entityID == entityID)
                        {
                                if ((ssp->itemID == itemID) || (! itemID))
                                {
                                        if ((ssp->itemtype == itemtype) || (! itemtype))
                                        {
                                                if (ssp->regiontype && regiontype)
                                                {
                                                        res = ObjMgrRegionComp (ssp->region, region, direction_plus);
							if (res>0)
								break;
                                                }
                                        }
                                }
                        }
                        ssp = next;
                }
        }
        if (res==2)  /* extend ssp->region to right */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp->region, -1, SeqLocStop(region));
                retval=ObjMgrSendSelMsg (omp, entityID, itemID, itemtype, ssp->regiontype, ssp->region);
        }
        else if (res==3)   /* extend ssp->region to left */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp->region, SeqLocStart(region), -1);
                retval=ObjMgrSendSelMsg (omp, entityID, itemID, itemtype, ssp->regiontype, ssp->region);
        }
        else if (res==4)   /* extend ssp->regiontype both sides */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp->region, SeqLocStart(region), SeqLocStop(region));
                retval=ObjMgrSendSelMsg (omp, entityID, itemID, itemtype, ssp->regiontype, ssp->region);
        }
        else if (res==5)  /* shrink ssp->region from right */
        {
                tmp = SeqLocIntNew (SeqLocStart((SeqLocPtr)ssp->region), SeqLocStop((SeqLocPtr)region), 0, SeqLocId(region));
                tmp2 = SeqLocIntNew (SeqLocStop((SeqLocPtr)region)+1, SeqLocStop((SeqLocPtr)ssp->region), 0, SeqLocId(region));
                SeqLocChangeIntervalle ((SeqLocPtr)ssp->region, -1, (Int4)(SeqLocStop(region)));
		ValNodeFree (tmp);
                retval=ObjMgrSendDeSelMsg (omp, entityID, itemID, itemtype, OM_REGION_SEQLOC, tmp2);

		ValNodeFree (tmp2);
        }
        else if (res==6) /* shrink ssp->region from left */
        {
                tmp = SeqLocIntNew (SeqLocStart((SeqLocPtr)region), SeqLocStop((SeqLocPtr)ssp->region), 0, SeqLocId(region));
                tmp2 = SeqLocIntNew (SeqLocStart((SeqLocPtr)ssp->region), SeqLocStart((SeqLocPtr)region)-1, 0, SeqLocId(region));
                SeqLocChangeIntervalle ((SeqLocPtr)ssp->region, (Int4)(SeqLocStart(region)), -1);
		ValNodeFree (tmp);
                retval=ObjMgrSendDeSelMsg (omp, entityID, itemID, itemtype, OM_REGION_SEQLOC, tmp2);
		ValNodeFree (tmp2);
        }
        else if (res==0)
        {
                ssp = ObjMgrAddSelStruct(omp, entityID, itemID, itemtype, regiontype, region);
                if (ssp != NULL)
                        retval=ObjMgrSendSelMsg (omp, entityID, itemID, itemtype, regiontype, region);
        }

erret:
        return (Boolean)(res>0);

}

static Boolean NEAR CheckRedondantSelect (ObjMgrPtr omp)
{
        SelStructPtr ssp1 = NULL, 
        		ssp2 = NULL,
        		next1,
        		next2 = NULL, pre2 = NULL;
        SeqLocPtr    tmp;
        Boolean      retval = FALSE;
        Int2         res=0;

        if (omp!=NULL)   
        {
                ssp1 = omp->sel;
                while (ssp1 != NULL && res==0)
                {
                   next1 = ssp1->next;
                   ssp2=next1;
                   pre2=ssp1;
                   while (ssp2!=NULL && res==0)
                   {
                   	next2 = ssp2->next;
                        if (ssp1->entityID == ssp2->entityID)
                        {
                                if (ssp1->itemID == ssp2->itemID)
                                {
                                        if (ssp1->itemtype == ssp2->itemtype)
                                        {
                                                if (ssp1->regiontype && ssp2->regiontype)
                                                {
                                                        res = ObjMgrRegionComp (ssp1->region, ssp2->region, TRUE);
							if (res>0)
								break;
                                                }
                                        }
                                }
                        }
                        ssp2 = next2;
                   }
                   if (res==0)
			ssp1 = next1;
                }
        }
	if (res==1)
	{
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);
                ObjMgrSendSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
                retval=TRUE;
	}
        else if (res==2)  /* extend ssp->regiontype to right */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp1->region, -1, SeqLocStop(ssp2->region));
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);                
                ObjMgrSendSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
                retval=TRUE;
        }
        else if (res==3)   /* extend ssp->regiontype to left */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp1->region, SeqLocStart(ssp2->region), -1);
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);                
                ObjMgrSendSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
                retval=TRUE;
        }
        else if (res==4)   /* extend ssp->regiontype both sides */
        {
                SeqLocChangeIntervalle ((SeqLocPtr)ssp1->region, SeqLocStart(ssp2->region), SeqLocStop(ssp2->region));
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);                
                ObjMgrSendSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
                retval=TRUE;
        }
        else if (res==5)  /* shrink ssp->region from right */
        {
		tmp = SeqLocIntNew (SeqLocStart((SeqLocPtr)ssp1->region), SeqLocStop(ssp2->region), 0, SeqLocId(ssp2->region));
                SeqLocChangeIntervalle ((SeqLocPtr)ssp1->region, -1, SeqLocStop(ssp2->region));
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);                
                ObjMgrSendDeSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
		ValNodeFree (tmp);
                retval=TRUE;
        }
        else if (res==6) /* shrink ssp->region from left */
        {
                tmp = SeqLocIntNew (SeqLocStart(ssp2->region), SeqLocStop((SeqLocPtr)ssp1->region), 0, SeqLocId(ssp2->region));
                SeqLocChangeIntervalle ((SeqLocPtr)ssp1->region, SeqLocStart(ssp2->region), -1);
       		pre2->next=next2;
       		ssp2=MemFree(ssp2);                
                ObjMgrSendDeSelMsg (omp, ssp1->entityID, ssp1->itemID, ssp1->itemtype, 0, 0);
		ValNodeFree (tmp);
                retval=TRUE;
        }
        return retval;
}



/*****************************************************************************
*
*   ObjMgrSetColor(entityID, itemID, itemtype, regiontype, region, rgb);
*      Sets color of object in displays
*      if regiontype != 0, and restricts to region
*      rgb is a pointer to a Uint1[3] array containing an RGB value.
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSetColor (Uint2 entityID, Uint4 itemID,
                                        Uint2 itemtype, Uint1 regiontype,
                                        Pointer region, Uint1Ptr rgb)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	OMMsgStruct ommds;
	Boolean retval = FALSE;

	omp = ObjMgrReadLock();

	if (rgb == NULL) goto erret;

	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp == NULL) goto erret;

	MemSet((Pointer)(&ommds), 0, sizeof(OMMsgStruct));
	ommds.message = OM_MSG_SETCOLOR;
	ommds.entityID = entityID;
	ommds.itemtype = itemtype;
	ommds.itemID = itemID;
	ommds.regiontype = regiontype;
	ommds.region = region;
        ommds.rgb[0] = rgb[0];
        ommds.rgb[1] = rgb[1];
        ommds.rgb[2] = rgb[2];

                                /* send the message */
	ObjMgrSendStructMsgFunc(omp, omdp, &ommds);
                                /* free the region */
	retval = TRUE;
erret:
	ObjMgrUnlock();

	return retval;
}
/*****************************************************************************
*
*   ObjMgrDeSelect(entityID, itemID, itemtype)
*   	if this item was selected, then deselects and returns TRUE
*   	else returns FALSE
*
*****************************************************************************/

NLM_EXTERN Boolean LIBCALL ObjMgrDeSelect (Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region)
{
	ObjMgrPtr omp;
	Boolean retval=FALSE;

	omp = ObjMgrWriteLock();
	retval = ObjMgrDeSelectFunc (omp, entityID, itemID, itemtype,
				regiontype, region);
	ObjMgrUnlock();
	return retval;
}


/*****************************************************************************
*
*   ObjMgrDeSelect(entityID, itemID, itemtype)
*   	if this item was selected, then deselects and returns TRUE
*   	else returns FALSE
*
*****************************************************************************/

static Boolean NEAR ObjMgrDeSelectFunc (ObjMgrPtr omp, Uint2 entityID, Uint4 itemID, Uint2 itemtype,
									Uint1 regiontype, Pointer region)
{
	SelStructPtr tmp, next;
	Boolean retval=FALSE, tret, do_it;
	SeqLocPtr slp;

	if (entityID == 0)
	{
		retval = ObjMgrDeSelectAllFunc(omp);
		goto erret;
	}

	tmp = omp->sel;
	while (tmp != NULL)
	{
		next = tmp->next;
		do_it = FALSE;
		if (tmp->entityID == entityID)
		{
			if ((! itemtype) || (itemtype == tmp->itemtype))
			{
				if ((! itemID) || (itemID == tmp->itemID))
				{
					if ((tmp->regiontype == regiontype) || (! regiontype))
					{
						do_it = TRUE;
						if (regiontype)
							do_it = ObjMgrRegionMatch(tmp->regiontype,
								tmp->region, regiontype, region);
						if (do_it)
						{
							tret = ObjMgrDeSelectStructFunc(omp, tmp);
							if (tret)
								retval = TRUE;
						}
                                                else if (SeqLocCompare(tmp->region, region)==SLC_B_IN_A)
						{
							if (SeqLocStart(tmp->region)==SeqLocStart(region))
							{
                						SeqLocChangeIntervalle ((SeqLocPtr)tmp->region, (Int4)(SeqLocStop(region)+1), -1);
                						retval=ObjMgrSendDeSelMsg (omp, entityID, itemID, itemtype, OM_REGION_SEQLOC, region);
							}
							else if (SeqLocStop(tmp->region)==SeqLocStop(region))
							{
								SeqLocChangeIntervalle ((SeqLocPtr)tmp->region, -1, (Int4)(SeqLocStart(region)-1));
								retval=ObjMgrSendDeSelMsg (omp, entityID, itemID, itemtype, OM_REGION_SEQLOC, region);
							}
							else {
								slp=SeqLocIntNew(SeqLocStop(region)+1, SeqLocStop((SeqLocPtr)tmp->region), 0, SeqLocId(tmp->region));
				 				SeqLocChangeIntervalle ((SeqLocPtr)tmp->region, -1, (Int4)(SeqLocStart(region)-1));
                						ObjMgrAddSelStruct(omp, entityID, itemID, itemtype, regiontype, slp);
                                                                retval=ObjMgrSendDeSelMsg (omp, entityID, itemID, itemtype, OM_REGION_SEQLOC, region);

							}
						}
					}
				}
			}
		}
		tmp = next;
	}

erret:
	return retval;
}

NLM_EXTERN Boolean LIBCALL ObjMgrAlsoSelect (Uint2 entityID, Uint4 itemID,
					 Uint2 itemtype, Uint1 regiontype, Pointer region)
{
	ObjMgrPtr omp;
	Boolean retval;
				/* if already selected, just deselect */
/**
	if (ObjMgrDeSelect(entityID, itemID, itemtype, regiontype, region))
		return FALSE;
**/
	omp = ObjMgrWriteLock();

	retval = ObjMgrSelectFunc2 (omp, entityID, itemID, itemtype, regiontype, region);
	if (retval) {
		while (retval)
			retval=CheckRedondantSelect (omp);
	}
	ObjMgrUnlock();

        return retval;

}

NLM_EXTERN Boolean LIBCALL ObjMgrDeSelectAll (void)
{
	ObjMgrPtr omp;
	Boolean retval = FALSE;

	omp = ObjMgrWriteLock();
	retval = ObjMgrDeSelectAllFunc(omp);
	ObjMgrUnlock();
	return retval;
}

NLM_EXTERN SelStructPtr LIBCALL ObjMgrGetSelected (void)
{
	ObjMgrPtr omp;
	SelStructPtr sel;

	omp = ObjMgrReadLock();
	sel = omp->sel;
	ObjMgrUnlock();
	return sel;
}

/*****************************************************************************
*
*   ObjMgrSendMsg(msg, entityID, itemID, itemtype)
*       Directly invokes the objmgr messaging system
*       should be used cautiously
*
*****************************************************************************/
NLM_EXTERN Boolean LIBCALL ObjMgrSendMsg(Uint2 msg, Uint2 entityID, Uint4 itemID, Uint2 itemtype)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	if (msg == OM_MSG_UPDATE) {
		SeqMgrClearFeatureIndexes (entityID, NULL);
	} else if (msg == OM_MSG_DEL) {
		SeqMgrClearFeatureIndexes (entityID, NULL);
	  ObjMgrDeSelect (entityID, itemID, itemtype, 0, NULL);
	}
	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp != NULL)
	{
		ObjMgrSendMsgFunc(omp, omdp, msg, entityID, itemID, itemtype, 0, 0, 0, NULL);
		retval = TRUE;
	}
	ObjMgrUnlock();
	return retval;
}

NLM_EXTERN Boolean LIBCALL ObjMgrSendProcMsg(Uint2 msg, Uint2 entityID, Uint4 itemID, Uint2 itemtype,
                                             Uint2 fromProcID, Uint2 toProcID, Pointer procmsgdata)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp != NULL)
	{
		ObjMgrSendMsgFunc(omp, omdp, msg, entityID, itemID, itemtype, 0, fromProcID, toProcID, procmsgdata);
		retval = TRUE;
	}
	ObjMgrUnlock();
	return retval;
}

NLM_EXTERN Boolean LIBCALL ObjMgrSendRowMsg(Uint2 msg, Uint2 entityID, Uint4 itemID, Uint2 itemtype, Uint2 rowID)
{
	ObjMgrPtr omp;
	ObjMgrDataPtr omdp;
	Boolean retval = FALSE;

	omp = ObjMgrReadLock();
	omdp = ObjMgrFindByEntityID(omp, entityID, NULL);
	if (omdp != NULL)
	{
		ObjMgrSendMsgFunc(omp, omdp, msg, entityID, itemID, itemtype, rowID, 0, 0, NULL);
		retval = TRUE;
	}
	ObjMgrUnlock();
	return retval;
}

/*****************************************************************************
*
*   ObjMgrGenericAsnTextFileRead(filename, datatypeptr, entityIDptr)
*      reads an asn1 text file for any datatype registered with the ObjMgr.
*      filename may contain a path
*      scans the start of the file to figure out the type.
*
*      if it fails, it returns NULL
*      if it succeeds, it returns a Pointer to the loaded data object
*          if datatypeptr is not NULL, fills it in with the proper OBJ_...
*          if entityIDptr is not NULL,
*                 it registers the data with the object manager
*                 it fills in entityIDptr with the entityID of the loaded object
*
*****************************************************************************/
NLM_EXTERN Pointer LIBCALL ObjMgrGenericAsnTextFileRead (CharPtr filename,
                                   Uint2Ptr datatypeptr, Uint2Ptr entityIDptr)
{
	Char line[255];
	FILE * fp;
	AsnIoPtr aip;
	Pointer ptr = NULL;
	Int2 ct, i;
	ObjMgrPtr omp;
	ObjMgrTypePtr omtp = NULL;

	if (filename == NULL)
		return ptr;

	if (datatypeptr != NULL) *datatypeptr = 0;
	if (entityIDptr != NULL) *entityIDptr = 0;

	fp = FileOpen(filename, "r");
	ct = FileRead(line, 1, 255, fp);
	for (i = 0; i < ct; i++)
	{
		if (line[i] == ':')
		{
			if ((line[i+1] == ':') && (line[i+2] == '='))
			{
				line[i] = '\0';
				i--;
				while ((i >= 0) && (IS_WHITESP(line[i])))
				{
					line[i] = '\0';
					i--;
				}
				while ((i >= 0) && (! IS_WHITESP(line[i])))
					i--;
				omp = ObjMgrReadLock();
				omtp = ObjMgrTypeFind(omp, 0, &(line[i+1]), NULL);
				ObjMgrUnlock();
				if (omtp == NULL)
				{
					FileClose(fp);
					ErrPostEx(SEV_ERROR, 0,0, "Can't read ASN.1 type [%s]",
						&(line[i+1]));
					return ptr;
				}
				break;
			}
		}
	}
	
	if (omtp == NULL)
	{
		FileClose(fp);
		ErrPostEx(SEV_ERROR,0,0,"Don't know how to read file [%s]", filename);
		return ptr;
	}
	
	fseek(fp, (long)(i+1), SEEK_SET);
	aip = AsnIoNew(ASNIO_TEXT_IN, fp, NULL, NULL, NULL);
	if (aip != NULL)
		aip->fname = StringSave(filename);
	ptr = (*(omtp->asnread))(aip, NULL);
	AsnIoClose(aip);
	if (ptr == NULL)
	{
		ErrPostEx(SEV_ERROR,0,0,"Couldn't read [%s], type [%s]", filename, omtp->asnname);
	}
	else
	{
		if (datatypeptr != NULL)
			*datatypeptr = omtp->datatype;
		if (entityIDptr != NULL)
			*entityIDptr = ObjMgrRegister(omtp->datatype, ptr);
	}
	
	return ptr;
}

/***********************************************************************************
*
*  ObjMgrMemCopy(type, ptr)
*    Uses AsnIoMemCopy to make a copy of any ObjMgr supported type
*    ObjMgrType for "type" must have been previously loaded
*
***********************************************************************************/
NLM_EXTERN Pointer LIBCALL ObjMgrMemCopy (Uint2 type, Pointer ptr)
{
	Pointer newptr = NULL;
	ObjMgrPtr omp;

	if (ptr == NULL) return newptr;

 	omp = ObjMgrReadLock();
	newptr = ObjMgrMemCopyFunc(omp, type, ptr, TRUE);

	return newptr;
}

static Pointer NEAR ObjMgrMemCopyFunc (ObjMgrPtr omp, Uint2 type, Pointer ptr, Boolean unlock)
{
	Pointer newptr = NULL;
	ObjMgrTypePtr omtp;

	if (ptr == NULL) {

            if (unlock)
		ObjMgrUnlock();
            
            return newptr;
        }

	omtp = ObjMgrTypeFind(omp, type, NULL, NULL);
	if (unlock)
		ObjMgrUnlock();
	if (omtp == NULL) return newptr;

	newptr = AsnIoMemCopy(ptr, omtp->asnread, omtp->asnwrite);

	return newptr;
}

/***********************************************************************************
*
*  ObjMgrFree(type, ptr)
*    ObjMgrType for "type" must have been previously loaded
*
***********************************************************************************/
NLM_EXTERN Pointer LIBCALL ObjMgrFree (Uint2 type, Pointer ptr)
{
	Pointer newptr = NULL;
	ObjMgrPtr omp;

	if (ptr == NULL) return newptr;

 	omp = ObjMgrReadLock();
	newptr = ObjMgrFreeFunc(omp, type, ptr, TRUE);

	return newptr;
}

NLM_EXTERN Pointer LIBCALL ObjMgrFreeByEntityID (Uint2 entityID)
{
	ObjMgrDataPtr omdp;
	Uint2 type;
	Pointer ptr;

	if (entityID < 1) return NULL;
	omdp = ObjMgrGetData (entityID);
	if (omdp == NULL) return NULL;

	if (omdp->choice != NULL) {
		type = omdp->choicetype;
		ptr = omdp->choice;
	} else {
		type = omdp->datatype;
		ptr = omdp->dataptr;
	}

	return ObjMgrFree (type, ptr);
}

static Pointer NEAR ObjMgrFreeFunc (ObjMgrPtr omp, Uint2 type, Pointer ptr, Boolean unlock)
{
	Pointer newptr = NULL;
	ObjMgrTypePtr omtp;

	if (ptr == NULL) {
            if (unlock)
		ObjMgrUnlock();
            return newptr;
        }

	omtp = ObjMgrTypeFind(omp, type, NULL, NULL);

	if (unlock)
		ObjMgrUnlock();
	if (omtp == NULL) return newptr;
	(*(omtp->freefunc))(ptr);

	return newptr;
}

NLM_EXTERN void LIBCALL ObjMgrResetAll (void)
{
    Int4 ret;
    
    if (global_omp == NULL)
        return;
    
    ret = NlmMutexLockEx(&omp_mutex);  /* protect this section */
    if (ret) { /* error */
        ErrPostEx(SEV_FATAL,0,0,"ObjMgrResetAll failed [%ld]", (long)ret);
        return;
    }
    
    global_omp = (ObjMgrPtr) MemFree(global_omp);
    NlmRWdestroy(omp_RWlock);
    omp_RWlock = NULL;
    
    NlmMutexUnlock(omp_mutex);
    
    return;
}

static CharPtr objmgrtypestrs [] = {
  "OBJ_ALL", "OBJ_SEQENTRY", "OBJ_BIOSEQ", "OBJ_BIOSEQSET", "OBJ_SEQDESC",
  "OBJ_SEQANNOT", "OBJ_ANNOTDESC", "OBJ_SEQFEAT", "OBJ_SEQALIGN", "OBJ_SEQGRAPH",
  "OBJ_SEQSUB", "OBJ_SUBMIT_BLOCK", "OBJ_SEQSUB_CONTACT", "13", "OBJ_BIOSEQ_MAPFEAT",
  "OBJ_BIOSEQ_SEG", "OBJ_SEQHIST", "OBJ_SEQHIST_ALIGN", "OBJ_BIOSEQ_DELTA", "19",
  "OBJ_PUB", "OBJ_SEQFEAT_CIT", "OBJ_SEQSUB_CIT", "OBJ_MEDLINE_ENTRY", "OBJ_PUB_SET",
  "OBJ_SEQLOC", "OBJ_SEQID", "OBJ_SEQCODE", "OBJ_SEQCODE_SET", "OBJ_GENETIC_CODE",
  "OBJ_GENETIC_CODE_SET", "OBJ_TEXT_REPORT", "OBJ_FASTA", "OBJ_VIBRANT_PICTURE", "OBJ_PROJECT"
};

static CharPtr temploadstrs [] = {
  "TL_NOT_TEMP", "TL_LOADED", "TL_CACHED"
};

static CharPtr proctypestrs [] = {
  "0", "OMPROC_OPEN", "OMPROC_DELETE", "OMPROC_VIEW", "OMPROC_EDIT",
  "OMPROC_SAVE", "OMPROC_CUT", "OMPROC_COPY", "OMPROC_PASTE", "OMPROC_ANALYZE",
  "OMPROC_FIND", "OMPROC_REPLACE", "OMPROC_FILTER", "OMPROC_FETCH",
};

static void PrintABool (FILE *fp, CharPtr str, Boolean val)

{
  if (val) {
    fprintf (fp, "%s TRUE\n", str);
  } else {
    fprintf (fp, "%s FALSE\n", str);
  }
}

static void ReportOnEntity (ObjMgrDataPtr omdp, ObjMgrPtr omp, Boolean selected,
                            Uint4 itemID, Uint2 itemtype, Int2 index, FILE *fp)

{
  BioseqPtr      bsp;
  BioseqSetPtr   bssp;
  Char           buf [50];
  OMUserDataPtr  omudp;

  if (omdp == NULL || fp == NULL) return;
  if (selected) {
    fprintf (fp, "Data Element\n\n");
    fprintf (fp, "  EntityID %d selected\n", (int) omdp->EntityID);
    fprintf (fp, "  ItemID %d, Itemtype %d\n", (int) itemID, (int) itemtype);
  } else if (omdp->parentptr == NULL) {
    fprintf (fp, "Top Data Element %d\n\n", (int) index);
    fprintf (fp, "  EntityID %d\n", (int) omdp->EntityID);
  } else {
    fprintf (fp, "Inner Data Element %d\n\n", (int) index);
    fprintf (fp, "  EntityID %d\n", (int) omdp->EntityID);
  }
  if (omdp->datatype < OBJ_MAX) {
    fprintf (fp, "  Datatype %s", objmgrtypestrs [omdp->datatype]);
    if (omdp->datatype == OBJ_BIOSEQ) {
      bsp = (BioseqPtr) omdp->dataptr;
      if (bsp != NULL) {
        SeqIdWrite (bsp->id, buf, PRINTID_FASTA_LONG, sizeof (buf) - 1);
        fprintf (fp, " %s, length %ld", buf, (long) bsp->length);
      }
    } else if (omdp->datatype == OBJ_BIOSEQSET) {
      bssp = (BioseqSetPtr) omdp->dataptr;
      if (bssp != NULL) {
        fprintf (fp, " class %d", (int) bssp->_class);
      }
    }
    fprintf (fp, "\n");
  } else {
    fprintf (fp, "  Unregistered datatype %d\n", (int) omdp->datatype);
  }
  fprintf (fp, "  Lockcnt %d\n", (int) omdp->lockcnt);
  if (omdp->tempload < 3) {
    fprintf (fp, "  Tempload %s\n", temploadstrs [omdp->tempload]);
  } else {
    fprintf (fp, "  Unrecognized tempload %d\n", (int) omdp->tempload);
  }
  PrintABool (fp, "  Clipboard", omdp->clipboard);
  PrintABool (fp, "  Dirty", omdp->dirty);
  PrintABool (fp, "  Being_freed", omdp->being_freed);
  PrintABool (fp, "  Free", omdp->free);
  fprintf (fp, "\n");
  for (omudp = omdp->userdata; omudp != NULL; omudp = omudp->next) {
    if (omudp->proctype <= OMPROC_MAX) {
      fprintf (fp, "    Proctype %s\n", proctypestrs [omudp->proctype]);
    } else {
      fprintf (fp, "    Unrecognized proctype %d\n", (int) omudp->proctype);
    }
    fprintf (fp, "    Procid %d\n", (int) omudp->procid);
    fprintf (fp, "    Userkey %d\n", (int) omudp->userkey);
    fprintf (fp, "\n");
  }
}


NLM_EXTERN void LIBCALL ObjMgrReportProc (FILE *fp)

{
  Uint4          j;
  Uint4          num;
  ObjMgrPtr      omp;
  ObjMgrDataPtr  omdp;
  ObjMgrDataPtr  PNTR omdpp;

  if (fp == NULL) return;
  omp = ObjMgrGet ();
  if (omp == NULL) return;
  fprintf (fp, "Object Manager\n\n");
  fprintf (fp, "  HighestEntityID %d\n", (int) omp->HighestEntityID);
  fprintf (fp, "  Totobj %d\n", (int) omp->totobj);
  fprintf (fp, "  Currobj %d\n", (int) omp->currobj);
  fprintf (fp, "  Maxtemp %d\n", (int) omp->maxtemp);
  fprintf (fp, "  Tempcnt %d\n", (int) omp->tempcnt);
  fprintf (fp, "  Hold %d\n", (int) omp->hold);
  PrintABool (fp, "  Reaping", omp->reaping);
  PrintABool (fp, "  Is_write_locked", omp->is_write_locked);
  fprintf (fp, "\n");
  num = omp->currobj;
  for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
    omdp = *omdpp;
    if (omdp->parentptr == NULL) {
      ReportOnEntity (omdp, omp, FALSE, 0, 0, j + 1, fp);
    }
  }
  for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
    omdp = *omdpp;
    if (omdp->parentptr != NULL) {
      ReportOnEntity (omdp, omp, FALSE, 0, 0, j + 1, fp);
    }
  }
}


NLM_EXTERN void LIBCALL ObjMgrReportFunc (CharPtr filename)

{
  FILE           *fp;
  Uint4          j;
  Uint4          num;
  ObjMgrPtr      omp;
  ObjMgrDataPtr  omdp;
  ObjMgrDataPtr  PNTR omdpp;

  omp = ObjMgrGet ();
  if (omp == NULL) return;
  fp = FileOpen (filename, "w");
  fprintf (fp, "Object Manager\n\n");
  fprintf (fp, "  HighestEntityID %d\n", (int) omp->HighestEntityID);
  fprintf (fp, "  Totobj %d\n", (int) omp->totobj);
  fprintf (fp, "  Currobj %d\n", (int) omp->currobj);
  fprintf (fp, "  Maxtemp %d\n", (int) omp->maxtemp);
  fprintf (fp, "  Tempcnt %d\n", (int) omp->tempcnt);
  fprintf (fp, "  Hold %d\n", (int) omp->hold);
  PrintABool (fp, "  Reaping", omp->reaping);
  PrintABool (fp, "  Is_write_locked", omp->is_write_locked);
  fprintf (fp, "\n");
  num = omp->currobj;
  for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
    omdp = *omdpp;
    if (omdp->parentptr == NULL) {
      ReportOnEntity (omdp, omp, FALSE, 0, 0, j + 1, fp);
    }
  }
  for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
    omdp = *omdpp;
    if (omdp->parentptr != NULL) {
      ReportOnEntity (omdp, omp, FALSE, 0, 0, j + 1, fp);
    }
  }
  FileClose (fp);
}


static Uint4 LIBCALL ObjMgrCountAssignedEntityIDs (void)

{
  Uint4  count = 0, val;
  Int2   idx, jdx;

  if (! assignedIDsInited) {
    ObjMgrInitAssignedIDArray ();
  }

  for (idx = 0; idx < 2048; idx++) {
    val = assignedIDsArray [idx];
    if (val == 0xFFFFFFFF) {
      count += 32;
    } else if (val > 0) {
      for (jdx = 0; jdx < 32; jdx++) {
        if ((val & assignedIDsBitIdx [jdx]) != 0) {
          count++;
        }
      }
    }
  }

  return count;
}

NLM_EXTERN Boolean LIBCALL ObjMgrStatusString (CharPtr str, size_t len)

{
  Char       buf [256];
  ObjMgrPtr  omp;

  if (str == NULL || len < 1) return FALSE;
  *str = '\0';
  buf [0] = '\0';
  omp = ObjMgrGet ();
  if (omp == NULL) return FALSE;

  sprintf (buf, "HighID %5d  NumIDs %5d  TotObj %5d  TempObj %5d",
           (int) omp->HighestEntityID,
           (int) ObjMgrCountAssignedEntityIDs (),
           (int) omp->totobj, (int) omp->tempcnt);

  StringNCpy_0 (str, buf, len);
  return TRUE;
}

NLM_EXTERN void LIBCALL
ObjMgrAddIndexOnEntityID(ObjMgrPtr omp,Uint2 entityID,ObjMgrDataPtr omdp)
{
	Uint1   h,l;

	h=entityID >> 8;
	l=entityID & 0xff;
	if(omp==NULL) omp=ObjMgrGet();
	if(omp){
		if(!omp->entityID_index){
			omp->entityID_index=MemNew(256*sizeof(*omp->entityID_index));
		}
		if(!omp->entityID_index[h]){
			omp->entityID_index[h]=MemNew(256*sizeof(**omp->entityID_index));
		}
		omp->entityID_index[h][l]=omdp;
	}
}


NLM_EXTERN void LIBCALL
ObjMgrDeleteIndexOnEntityID(ObjMgrPtr omp,Uint2 entityID)
{
	Uint1   h,l;

        h=entityID >> 8;
        l=entityID & 0xff;
	if(omp==NULL) omp=ObjMgrGet();
	if(omp && omp->entityID_index && omp->entityID_index[h]){
		omp->entityID_index[h][l]=NULL;
	}

}


NLM_EXTERN ObjMgrDataPtr LIBCALL
ObjMgrLookupIndexOnEntityID(ObjMgrPtr omp,Uint2 entityID)
{
	Uint1   h,l;

        h=entityID >> 8;
        l=entityID & 0xff;
	if(omp==NULL) omp=ObjMgrGet();
	if(omp && omp->entityID_index && omp->entityID_index[h]){
		return omp->entityID_index[h][l];
	} else {
		return NULL;
	}
}

NLM_EXTERN Boolean DeleteRemainingViews (Uint2 entityID)
{
  OMUserDataPtr omudp;
  Boolean found_desktop = FALSE, has_other = FALSE, removed_one;
  Uint2 procid;
  Uint4               i, num;
  ObjMgrDataPtr       omdp;
  ObjMgrDataPtr PNTR  omdpp;
  ObjMgrPtr           omp;
    
  if (entityID == 0) return FALSE;
  omp = ObjMgrWriteLock();
  if (omp == NULL) 
  {
    return FALSE;
  }  
  
  procid = ObjMgrGetProcID(omp, "NCBI DeskTop", OMPROC_EDIT);
  
  omdpp = omp->datalist;
  if (omdpp != NULL) {
    for (i = 0; i < omp->currobj && !has_other; i++) {
      if (omdpp[i] != NULL && omdpp[i]->EntityID == entityID && !omdpp[i]->being_freed) {
        omudp = omdpp[i]->userdata;
        while (omudp != NULL && !has_other) {
            if (omudp->procid != procid) {
                has_other = TRUE;
            }
            omudp = omudp->next;
        }
      }
    }
    if (!has_other) {
      removed_one = TRUE;
      while (removed_one && omp->currobj > 0) {
        removed_one = FALSE;
        num = omp->currobj;
        omdpp = omp->datalist;
        for (i = 0; i < num && ! removed_one; i++) {
          omdp = omdpp [i];
          if (omdp != NULL && omdp->EntityID == entityID) {
            omudp = omdp->userdata;
            if (omudp != NULL) {
              ObjMgrFreeUserDataFunc (omp, entityID, omudp->procid, omudp->proctype, omudp->userkey);
              removed_one = TRUE;
            }
          }
        }
      }
    }
  }

  ObjMgrUnlock ();
  return found_desktop;
}