//		OTLN.c

#include <string.h>
#include "OTLNp.h"

//----------------------------------------------------------------------------------------//
#define		OTLNp_IsRootQuick(topic)	\
	((topic) == DH(topic)->next_sister && (topic) == DH(topic)->prev_sister)

//----------------------------------------------------------------------------------------//

/** OTLN_Birth [2]
**/
Err		OTLN_Birth(void)
{
	#ifdef	USING_U
		(void)(STR_VERIFY_INIT(OTLN));		// ignore error, we don't really care THAT much
	#endif
		
	return OTLN_AllocateTwirlies();
}

/** OTLN_Death [3]
**/
Err		OTLN_Death(void)
{
	return OTLN_DisposeTwirlies();
}

//----------------------------------------------------------------------------------------//

/** OTLN_NewOutline [4]
**/
Err		OTLN_NewOutline(M_Rect *bounds0, OTLN_OutlineH *outline)
{
				Err				err = Err_NONE;
				OTLN_TopicH		topic;
	register	OTLN_OutlineP	ol;
	
	*outline = U_ALLOC_HANDLE_CLR(OTLNp_LOC(4), OTLN_OutlineRec, OTLNp_STR(0));
	if (*outline == NULL) {
		err = Err_ALLOC;
	}
	
	if (!err) {
		ol = U_LOCK_DH(*outline);
		
		ol->magic1		= OTLN_MAGIC;
		ol->magic2		= OTLN_MAGIC;
		if (bounds0) {
			ol->bounds			= *bounds0;
			ol->outline_left	= bounds0->left;
			ol->outline_right	= bounds0->right;
		}
		ol->self		= *outline;
		
		err = OTLN_SetFontInfo(ol, OTLNp_STR(2), 9);
		
		U_UNLOCK_RH(*outline);
		
		if (!err) {
			err = OTLN_NewTopic(OTLNp_STR(3), &topic);
	
			if (!err) {
				err = OTLN_AddTopic(*outline, topic, NULL, 0);
				
				if (!err)
					topic = NULL;
			}
		}
	}
	
	if (err) {

		if (*outline) {
			(void)OTLN_DisposeOutline(*outline);
			*outline = NULL;
		}

		if (topic)
			(void)OTLN_DisposeTopic(topic);
	}
	
	return err;
}

/** OTLN_DisposeOutline [5]
**/
Err		OTLN_DisposeOutline(OTLN_OutlineH outline)
{
				Err				err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(5, outline);
	if (!err)	{
		OTLN_OutlineP	ol;

		ol = U_LOCK_DH(outline);

		//	kill off the icons
		if (!ol->shared_icons && ol->iconsH)
			U_FREE_HANDLE(ol->iconsH);
		
		if (ol->outline_root) {
			OTLN_TopicH		topic;
		
			//	dispose of the outline
			err = OTLN_GetRootTopic(outline, &topic);
			if (!err)
				err = OTLN_DisposeTopic(topic);
			
			//	dispose of the custom outline data
			if (!err) {
				if (ol->dispose_custom_data && ol->custom_data) {
					err = (*ol->dispose_custom_data)(ol->custom_data);
				}
			}
		}
		
		U_UNLOCK_RH(outline);

		//	dispose of self
		if (!err)
			U_FREE_HANDLE(outline);
	}
	
	return err;
}

/** OTLN_SetOutlineSpecs [6]
**/
Err		OTLN_SetOutlineSpecs(	
				OTLN_OutlineH		outline, 
				M_Rect				*bounds0, 
				short				*outline_left0, 
				short				*outline_right0, 
			#if defined(OS_MAC)
				const char			*font_name0, 
				short				font_size0, 
			#endif
				void 				*iconsH0, 
				Boolean				shared_icons)
{
				Err				err = Err_NONE;
	register	OTLN_OutlineP	ol;
	
	err = OTLN_CheckOutlineMagic(6, outline);
	if (!err) {
		ol = U_LOCK_DH(outline);
		
		if (bounds0)
			ol->bounds = *bounds0;

		ol->outline_left = outline_left0 ? *outline_left0 : ol->bounds.left;
		ol->outline_right = outline_right0 ? *outline_right0 : ol->bounds.right;
		
		err = OTLN_SetFontInfo(ol, font_name0, font_size0);
		
		if (!err && iconsH0) {

			if (!ol->shared_icons && ol->iconsH)
				U_FREE_HANDLE(ol->iconsH);

			ol->iconsH			= iconsH0;
			ol->shared_icons	= shared_icons;
		}
		
		U_UNLOCK_RH(outline);
	}
		
	return err;
}

/** OTLN_SetOutlineCustomData [61]
**/
Err		OTLN_SetOutlineCustomData(
	OTLN_OutlineH			outline, 
	void					*custom_data, 
	OTLN_DupCustomData		dup_custom_data, 
	OTLN_DisposeCustomData	dispose_custom_data
) {
	Err				err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(61, outline);
	if (!err) {
		DH(outline)->custom_data			= custom_data;
		DH(outline)->dup_custom_data		= dup_custom_data;
		DH(outline)->dispose_custom_data	= dispose_custom_data;
	}
	
	return err;
}

/** OTLN_GetOutlineCustomData [62]
**/
Err		OTLN_GetOutlineCustomData(
	OTLN_OutlineH			outline, 
	void					**custom_data
) {
	Err				err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(62, outline);
	if (!err) {
		*custom_data = DH(outline)->custom_data;
	}
	
	return(err);
}

/** OTLN_SetOutlineInvisableFlags [53]
**/
Err		OTLN_SetOutlineInvisableFlags(
				OTLN_OutlineH	outline, 
				Boolean			*honor_family_invisable, 
				Boolean			*honor_topic_invisable)
{
	Err				err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(53, outline);
	if (!err) {
		if (honor_family_invisable)
			(**outline).honor_family_invisable	= *honor_family_invisable;
		
		if (honor_topic_invisable)
			(**outline).honor_topic_invisable	= *honor_topic_invisable;
	}
	
	return(err);
}

/** OTLN_GetOutlineInvisableFlags [54]
**/
Err		OTLN_GetOutlineInvisableFlags(
				OTLN_OutlineH	outline, 
				Boolean			*honor_family_invisable, 
				Boolean			*honor_topic_invisable)
{
	Err				err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(54, outline);
	if (!err) {
		if (honor_family_invisable)
			*honor_family_invisable	= (**outline).honor_family_invisable;
		
		if (honor_topic_invisable)
			*honor_topic_invisable	= (**outline).honor_topic_invisable;
	}
	
	return err;
}


/** OTLN_DupOutline [65]
**/
Err		OTLN_DupOutline(
	OTLN_OutlineH	outline, 
	OTLN_OutlineH	*outlineCopy
) {
	Err				err		= Err_NONE;
	OTLN_TopicH		topic	= NULL;
		
	err = OTLN_CheckOutlineMagic(65, outline);
	
	if (!err) {
		err = OTLN_NewOutline(NULL, outlineCopy);
	}
	
	if (!err) {
		OTLN_TopicH		tempRoot;
		OTLN_OutlineP	oc;
	
		oc = U_LOCK_DH(*outlineCopy);
		
		//	save the root topic that was magically created
		topic			= oc->outline_root;
		
		//	copy all fields from source to dest
		*oc				= *DH(outline);
		
		//	then restore the root topic to the dest
		oc->outline_root	= topic;
		
		//	for posterity
		topic = NULL;
		
		oc->self			= *outlineCopy;
		oc->recent_topic	= NULL;
		
		//	replicate any custom data for the outline
		if (oc->custom_data && oc->dup_custom_data) {
			OTLN_OutlineAndTopic		to_source, to_dest;
			
			to_source.outlineH	= outline;
			to_source.topicH	= NULL;
			
			to_dest.outlineH	= *outlineCopy;
			to_dest.topicH		= NULL;
			
			err = (*(oc->dup_custom_data))(
				&to_source, 
				oc->custom_data, 
				&to_dest, 
				&(oc->custom_data)
			);
		}

		//	duplicate the root info
		if (!err) {
			OTLN_CustomHitCheck		custom_hit_check;
			OTLN_CustomDraw			custom_draw;
			OTLN_DupCustomData		dup_custom_data;
			OTLN_DisposeCustomData	dispose_custom_data;
			
			tempRoot = DH(outline)->outline_root;

			err = OTLN_GetTopicCallbacks(
				tempRoot, 
				&custom_hit_check, 
				&custom_draw, 
				NULL, 
				&dup_custom_data, 
				&dispose_custom_data
			);

			if (!err) err = OTLN_SetTopicCallbacks(
				oc->outline_root, 
				custom_hit_check, 
				custom_draw, 
				NULL, 
				dup_custom_data, 
				dispose_custom_data
			);
		}
		
		//	replicate any custom data for the root
		if (!err && DH(tempRoot)->custom_data && DH(tempRoot)->dup_custom_data) {
			void						*custom_data;
			OTLN_OutlineAndTopic		to_source, to_dest;
			
			to_source.outlineH	= outline;
			to_source.topicH	= tempRoot;
			
			to_dest.outlineH	= *outlineCopy;
			to_dest.topicH		= oc->outline_root;
			
			err = (*(DH(tempRoot)->dup_custom_data))(
				&to_source, 
				DH(tempRoot)->custom_data, 
				&to_dest, 
				&custom_data
			);
			
			if (!err)
				DH(tempRoot)->custom_data = custom_data;
		}
		
		if (!err && DH(tempRoot)->daughters) {
		
			err = OTLN_DupTopic(DH(tempRoot)->daughters, &topic);
			
			if (!err) {
				tempRoot = oc->outline_root;

				DH(tempRoot)->daughters		= topic;

				for (
					topic = DH(tempRoot)->daughters; 
					topic != NULL; 
					topic = DH(topic)->next_sister
				) {
					DH(topic)->mother	= tempRoot;
				}
			}
		}
	}
	
	U_UNLOCK_RH(*outlineCopy);

	if (err) {
		if (topic) {
			(void)OTLN_DisposeTopic(topic);
		}
		
		if (*outlineCopy) {
			(void)OTLN_DisposeOutline(*outlineCopy);
			*outline = NULL;
		}
	}
	
	return err;
}

//----------------------------------------------------------------------------------------//

static	void	OTLNp_SetTopicName(OTLN_TopicP to, char *name0)
{
	if (name0) {
		size_t		len = strlen(name0);
		char		tempNameAC[OTLN_MAX_NAME_LENGTH + 1];

		if (len > OTLN_MAX_NAME_LENGTH) {
			len = OTLN_MAX_NAME_LENGTH;
		}

		memcpy(tempNameAC, name0, len);	
		tempNameAC[len] = 0;
		strcpy(to->name, tempNameAC);
	}
}

/** OTLN_NewTopic [7]
**/
Err		OTLN_NewTopic(const char *name0, OTLN_TopicH *topic)
{
				Err			err = Err_NONE;
	register	OTLN_TopicP	to;
	
	*topic = U_ALLOC_HANDLE_CLR(OTLNp_LOC(7),  OTLN_TopicRec, OTLNp_STR(1));
	if (*topic == NULL) {
		err = Err_ALLOC;
	}
	
	if (!err) {
		to = U_LOCK_DH(*topic);
		
		to->magic1 = OTLN_MAGIC;
		to->magic2 = OTLN_MAGIC;
		
		OTLNp_SetTopicName(to, (char *)name0);
		
		to->flags |= OTLN_TopicFlags_IS_SELECTABLE;
		to->flags |= OTLN_TopicFlags_COLLAPSED;
		to->flags |= OTLN_TopicFlags_IS_EXPANDABLE;
		
		to->cell_height_multiple = 1;
	
		to->self = *topic;

		U_UNLOCK_RH(*topic);
	}
	
	return err;
}

/** OTLN_DisposeTopic [8]
**/
Err		OTLN_DisposeTopic(OTLN_TopicH topic)
{
	Err				err = Err_NONE;

	if (topic)	{

		err = OTLN_CheckTopicMagic(8, topic);
		if (!err) {
			//	if it is the root, then there's no sisters
			//	this "if" tests if it is NOT the root
			if (!OTLNp_IsRootQuick(topic))
				err = OTLN_DisposeTopic(DH(topic)->next_sister);

			if (!err) {
				err = OTLN_DisposeTopic(DH(topic)->daughters);

				if (!err) {	
					OTLN_TopicP		to;
					
					to = U_LOCK_DH(topic);
					
					if (to->custom_data && to->dispose_custom_data)
						err = (*(to->dispose_custom_data))(to->custom_data);

					U_UNLOCK_RH(topic);
					
					U_FREE_HANDLE(topic);
				}
			}
		}
	}
	
	return err;
}

/** OTLN_SetTopicSpecs [9]
**/
Err		OTLN_SetTopicSpecs(
				OTLN_TopicH			topic, 
				const char			*name0,
				short				cell_height_multiple0, 
				OTLN_TopicFlags		flags0, 
				short				*icon_reference0
			#if defined(OS_MAC)
				, Style				*font_style0)
			#else
				)
			#endif
			
{
				Err			err = Err_NONE;
	register	OTLN_TopicP	to;
	
	err = OTLN_CheckTopicMagic(9, topic);
	if (!err) {
	
		to = U_LOCK_DH(topic);

		OTLNp_SetTopicName(to, (char *)name0);
		
		if (cell_height_multiple0)
			to->cell_height_multiple = cell_height_multiple0;
		
		if (icon_reference0)
			to->icon_reference = *icon_reference0;
		
		err = OTLN_SetTopicFontStyle(to, font_style0);
		
		to->flags |= flags0;
		
		if (icon_reference0)
			to->icon_reference = *icon_reference0;
		
		U_UNLOCK_RH(topic);
	}	

	return err;
}


/** OTLN_SetTopicCallbacks [43]
**/
Err		OTLN_SetTopicCallbacks(
	OTLN_TopicH				topic, 
	OTLN_CustomHitCheck		custom_hit_check0, 
	OTLN_CustomDraw			custom_draw0, 
	void					*custom_data0, 
	OTLN_DupCustomData		dup_custom_data0, 
	OTLN_DisposeCustomData	dispose_custom_data0
) {
				Err			err = Err_NONE;
	register	OTLN_TopicP	to;
	
	err = OTLN_CheckTopicMagic(43, topic);
	if (!err) {
	
		to = U_LOCK_DH(topic);

		if (custom_hit_check0)
			to->custom_hit_check = custom_hit_check0;
		
		if (custom_draw0)
			to->custom_draw = custom_draw0;

		if (custom_data0)
			to->custom_data = custom_data0;
		
		if (dup_custom_data0)
			to->dup_custom_data = dup_custom_data0;

		if (dispose_custom_data0)
			to->dispose_custom_data = dispose_custom_data0;

		U_UNLOCK_RH(topic);
	}	

	return err;
}

/** OTLN_GetTopicCallbacks [51]
**/
Err		OTLN_GetTopicCallbacks(
	OTLN_TopicH				topic, 
	OTLN_CustomHitCheck		*custom_hit_check0, 
	OTLN_CustomDraw			*custom_draw0, 
	void					**custom_data0, 
	OTLN_DupCustomData		*dup_custom_data0, 
	OTLN_DisposeCustomData	*dispose_custom_data0
) {
				Err			err = Err_NONE;
	register	OTLN_TopicP	to;
	
	err = OTLN_CheckTopicMagic(51, topic);
	if (!err) {
	
		to = U_LOCK_DH(topic);

		if (custom_hit_check0)
			*custom_hit_check0 = to->custom_hit_check;
		
		if (custom_draw0)
			*custom_draw0 = to->custom_draw;

		if (custom_data0)
			*custom_data0 = to->custom_data;
		
		if (dup_custom_data0)
			*dup_custom_data0 = to->dup_custom_data;

		if (dispose_custom_data0)
			*dispose_custom_data0 = to->dispose_custom_data;

		U_UNLOCK_RH(topic);
	}	

	return err;
}

/**	OTLN_GetCustomData [50]
**/
Err		OTLN_GetTopicCustomData(
				OTLN_TopicH				topic, 
				void					**customData)
{
	Err			err = Err_NONE;

	U_ASSERT(customData);
	*(Ptr *)customData = NULL;

	err = OTLN_CheckTopicMagic(50, topic);
	
	if (!err) {
		*customData = DH(topic)->custom_data;
	}

	return err;
}

//----------------------------------------------------------------------------------------//

/**	OTLN_AddTopic [10]

	if the outline has no topics, then topic is added as
	the first topic
	
	else
	
	if there is no reference topic, then the last topic is
	used as reference and 

	>> if NULL, then 'where' is ignored
	and topic added at end of first generation

*/
Err		OTLN_AddTopic(
				OTLN_OutlineH			outline, 
				OTLN_TopicH				topic, 
				OTLN_TopicH				referenceTopic0, 	
				OTLN_AddTopicAsType		where)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckOutlineMagic(10, outline);
	
	if (!err)
		err = OTLN_CheckTopicMagic(10, topic);

	if (!err) {
	
		if (DH(outline)->outline_root == NULL) {
			DH(topic)->prev_sister		= topic;
			DH(topic)->next_sister		= topic;
			DH(topic)->mother			= (OTLN_TopicH)outline;
			DH(outline)->outline_root	= topic;
		} else {
			if (referenceTopic0 == NULL) {
				referenceTopic0 = DH(outline)->outline_root;
				where = OTLN_AddTopicAs_LAST_DAUGHTER;
			}
			
			err = OTLN_CheckTopicMagic(10, referenceTopic0);
			if (!err) {
			
				if (where < 0) {
					
					for	(
							referenceTopic0 = DH(referenceTopic0)->mother, where++;
							referenceTopic0 && where < 0;
							referenceTopic0 = DH(referenceTopic0)->mother, where++
					)
						;
					
					if (referenceTopic0 == NULL) {
						OTLNp_PROB_S(10, OTLNp_STR(4));
						err = Err_PARAMETER;
					}
					
					where = OTLN_AddTopicAs_SISTER;
				}
				
				U_ASSERT(where <= 1 || where == OTLN_AddTopicAs_FIRST_DAUGHTER);
				
				if (!err) {
				
					switch (where)	{
					
						case OTLN_AddTopicAs_SISTER: {
							OTLN_TopicH		nextSister;
							
							nextSister = DH(referenceTopic0)->next_sister;

							DH(topic)->next_sister				= nextSister;
							DH(topic)->prev_sister				= referenceTopic0;
							DH(topic)->mother					= DH(referenceTopic0)->mother;

							DH(referenceTopic0)->next_sister	= topic;

							if (nextSister != NULL)
								DH(nextSister)->prev_sister = topic;
							
							break;
						}

						case OTLN_AddTopicAs_FIRST_DAUGHTER: {
							OTLN_TopicH		nextSister;

							nextSister = (**referenceTopic0).daughters;
							
							(**topic).next_sister = nextSister;
							if (nextSister)
								(**nextSister).prev_sister = topic;
							
							(**topic).mother = referenceTopic0;
							(**referenceTopic0).daughters = topic;
							break;
						}
						
						case OTLN_AddTopicAs_LAST_DAUGHTER: {
							OTLN_TopicH		sisterTopic;
							
							err = OTLN_GetLastDaughter(referenceTopic0, &sisterTopic);
							
							if (!err) {
								if (sisterTopic == NULL) {
									DH(referenceTopic0)->daughters	= topic;
									DH(topic)->prev_sister			= NULL;
								} else {
									DH(sisterTopic)->next_sister	= topic;
									DH(topic)->prev_sister			= sisterTopic;
								}
				
								DH(topic)->next_sister			= NULL;
								DH(topic)->mother				= referenceTopic0;
							}
							break;
						}
					}
				}
			}
		}
	}
	
	if (!err)
		DH(outline)->recent_topic = topic;
	else
		DH(outline)->recent_topic = NULL;

	return err;
}

/** OTLN_AddNewTopic [11]
**/
Err		OTLN_AddNewTopic(
				OTLN_OutlineH		outline, 
				const char			*name0, 
				OTLN_AddTopicAsType	where, 
				OTLN_TopicH			*return0)
{
	Err				err = Err_NONE;
	OTLN_TopicH		topic;
	
	if (return0)
		*return0 = NULL;
	
	err = OTLN_NewTopic(name0, &topic);
	
	if (!err) {
		OTLN_TopicH		ref;
		
		if ((ref = DH(outline)->recent_topic) == NULL) {
			ref = DH(outline)->outline_root;
			
			if (where != OTLN_AddTopicAs_LAST_DAUGHTER)
				where = OTLN_AddTopicAs_FIRST_DAUGHTER;
		}
				
		err = OTLN_AddTopic(outline, topic, ref, where);
		if (!err) {
			if (return0)
				*return0 = topic;
			topic = NULL;
		}
	}
	
	if (err && topic)
		(void)OTLN_DisposeTopic(topic);

	return err;
}

//----------------------------------------------------------------------------------------//

/** OTLN_RemoveTopic [12]
**/
Err		OTLN_RemoveTopic(
				OTLN_OutlineH		outline,
				OTLN_TopicH			topic)
{
	Err				err = Err_NONE;
	OTLN_TopicH		refTopic;
	
	err = OTLN_CheckTopicMagic(12, topic);
	if (!err) {
	
		//	fail if root topic is null
		err = OTLN_GetFirstTopic(outline, &refTopic);
		if (!err) {
		
			if (refTopic == NULL) {
				OTLNp_PROB_S(12, OTLNp_STR(5));
				err = Err_PARAMETER;
			}
			
			if (!err) {
				OTLN_TopicH		nextSister;

				nextSister = DH(topic)->next_sister;

				if ((refTopic = DH(topic)->prev_sister) != NULL) {	/*=*/
					//	I have a previous sister
					DH(refTopic)->next_sister = nextSister;
					if (nextSister)
						DH(nextSister)->prev_sister = refTopic;
				} else {
					//	I am the first sister (possibly of many) in line
					DH(DH(topic)->mother)->daughters = nextSister;
					if (nextSister)
						DH(nextSister)->prev_sister = NULL;
				}

				DH(topic)->mother		= NULL;
				DH(topic)->prev_sister	= NULL;
				DH(topic)->next_sister	= NULL;
			}
		}		
	}
	
	return err;
}

/** OTLN_DupTopic [64]
**/
Err		OTLN_DupTopic(
	OTLN_TopicH	topic, 			//	>>
	OTLN_TopicH	*topicCopy		//	<<	will be allocated and passed back
) {
	Err		err = Err_NONE;

	err = OTLN_CheckTopicMagic(62, topic);
	
	if (!err) {
		err = OTLN_NewTopic(NULL, topicCopy);
		
		if (!err) {
			err = OTLNp_RecurseDupTopic(topic, *topicCopy, NULL, NULL);
		}
	}
	
	if (err) {
		if (*topicCopy) {
			(void)OTLN_DisposeTopic(*topicCopy);
			*topicCopy = NULL;
		}
	}
	
	return err;
}

/**	OTLN_GetTopicIndex	[69]
**/
Err		OTLN_ReorderTopics(
	OTLN_OutlineH	outline, 
	OTLN_TopicH		parent0, 
	OTLN_TopicH		topic0, 	//	either pass the topic that is to be moved
	long			oldIndex0, 	//	or pass its index.  you MUST pass one
	long			newIndex
) {
	Err						err = Err_NONE;
	OTLN_TopicH				referenceTopic;
	OTLN_AddTopicAsType		addTopicType;	
	
	err = OTLN_CheckOutlineRootMagic(69, outline);

	if (!err && !parent0) {
		err = OTLN_GetRootTopic(outline, &parent0);
	}
	
	if (!err) {
		if (!topic0) {
			err = OTLN_GetIndTopic(outline, parent0, oldIndex0, &topic0);
		} else {
			err = OTLN_GetTopicIndex(outline, topic0, &oldIndex0);
		}
	}

	if (!err) err = OTLN_RemoveTopic(outline, topic0);

	if (newIndex == 0) {
		referenceTopic	= parent0;
		addTopicType	= OTLN_AddTopicAs_FIRST_DAUGHTER;
	} else {
		if (!err) err = OTLN_GetIndTopic(
			outline, parent0, 
			newIndex - 1, 
			&referenceTopic
		);
		addTopicType = OTLN_AddTopicAs_SISTER;
	}
	
	if (!err) err = OTLN_AddTopic(outline, topic0, referenceTopic, addTopicType);
	
	return err;
}


//----------------------------------------------------------------------------------------//

/**	OTLN_GetTopicIndex	[60]
**/
Err		OTLN_GetTopicIndex(
	OTLN_OutlineH	outline, 			//	>>	the Outline
	OTLN_TopicH		topic, 				//	>>	the topic that you want to know about
	long			*index				//	<<	here's its index
) {
	Err		err		= Err_NONE;
	
	err = OTLN_CheckTopicMagic(60, topic);
	
	if (!err)
		for (*index = -1; topic != NULL; topic = (**topic).prev_sister, (*index)++)
			;
	
	return(err);
}


/**	OTLN_GetTopicIndex	[68]
**/
Err		OTLN_GetIndTopic(
	OTLN_OutlineH 	outline, 
	OTLN_TopicH		parent0, 
	long			index, 
	OTLN_TopicH		*topic
) {
	Err		err = Err_NONE;
	
	if (!parent0) {
		err = OTLN_GetRootTopic(outline, &parent0);
	}
	
	if (!err) {
		err = OTLN_GetFirstDaughter(parent0, topic);
	}
	
	if (!err && topic) {
		for (; index > 0 && topic != NULL; index--, *topic = DH(*topic)->next_sister)
			;
	}
	
	return err;
}
//----------------------------------------------------------------------------------------//
/** OTLN_GetTopicOutline [77]
**/
Err		OTLN_GetTopicOutline(
				OTLN_TopicH		topic, 
				OTLN_OutlineH	*outline)
{
	Err			err = Err_NONE;
	Boolean		done = FALSE;
	
	*outline = NULL;
	err = OTLN_CheckTopicMagic(77, topic);

	if (!err) do {
		if (
			DH(topic)->prev_sister == topic
			&& DH(topic)->next_sister == topic
		) {
			done = TRUE;
			err	= OTLN_GetMother(topic, &topic);

			if (!err && !topic) {
				//	"topic has no owning outline."
				OTLNp_PROB_S(77, OTLNp_STR(18));
				err = Err_PARAMETER;
			}

			if (!err) {
				*outline = (OTLN_OutlineH)topic;
			}
		} else {
			err = OTLN_GetMother(topic, &topic);
			
			if (!err && !topic) {
				//	"topic has no owning outline."
				OTLNp_PROB_S(77, OTLNp_STR(18));
				err = Err_PARAMETER;
			}
		}
	} while (!err && !done);
	
	return err;
}

/** OTLN_IsRootTopic [66]
**/

Err		OTLN_IsRootTopic(
				OTLN_OutlineH	outline, 
				OTLN_TopicH		topic, 
				Boolean			*isRoot)
{
	Err			err = Err_NONE;
	
	U_ASSERT(isRoot);
	*isRoot = FALSE;
	
	err = OTLN_CheckOutlineRootMagic(66, outline);
	
	if (!err) {
		OTLN_TopicH		root;
		
		err = OTLN_GetRootTopic(outline, &root);
		if (!err) {
			*isRoot = root == topic;
			
			if (*isRoot) {
				U_ASSERT(OTLNp_IsRootQuick(root));
			}
		}
	}
	
	return err;
}

/** OTLN_GetRootTopic [13]
**/
Err		OTLN_GetRootTopic(
				OTLN_OutlineH	outline, 
				OTLN_TopicH		*root)
{
	Err			err = Err_NONE;
	
	U_ASSERT(root);
	*root = NULL;
	
	err = OTLN_CheckOutlineRootMagic(13, outline);
	if (!err)
		*root = DH(outline)->outline_root;
	
	return err;
}

/** OTLN_GetFirstTopic [67]
**/
Err		OTLN_GetFirstTopic(
				OTLN_OutlineH	outline, 
				OTLN_TopicH		*topic)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckOutlineRootMagic(67, outline);
	
	if (!err) {
		err = OTLN_GetRootTopic(outline, topic);
	
		if (!err)
			err = OTLN_GetFirstDaughter(*topic, topic);
	}
	
	return err;
}

/** OTLN_GetRecentTopic [42]
**/
Err		OTLN_GetRecentTopic(
				OTLN_OutlineH	outline, 
				OTLN_TopicH		*recent)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckOutlineRootMagic(42, outline);
	if (!err)
		*recent = (**outline).recent_topic;
	
	return err;
}

/** OTLN_SetRecentTopic [44]
**/
Err		OTLN_SetRecentTopic(
				OTLN_OutlineH	outline, 
				OTLN_TopicH		recent)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckOutlineRootMagic(44, outline);

	if (!err && recent)
		err = OTLN_CheckTopicMagic(44, recent);
	
	if (!err)
		(**outline).recent_topic = recent;
	
	if (DH(recent)->mother == DH(recent)->daughters) {
		ReportErrorStr(-1, "You're fucked!");
	}
	
	return err;
}

/** OTLN_GetFirstDaughter [14]
**/
Err		OTLN_GetFirstDaughter(
				OTLN_TopicH mother, 
				OTLN_TopicH *first_daughter)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckTopicMagic(14, mother);
	if (!err)
		*first_daughter = DH(mother)->daughters;

	return err;
}

/** OTLN_GetLastDaughter [15]
**/
Err		OTLN_GetLastDaughter(
				OTLN_TopicH mother, 
				OTLN_TopicH *last_daughter)
{
	Err		err = Err_NONE;
	
	err = OTLN_GetFirstDaughter(mother, last_daughter);
	if (!err) {
		if (*last_daughter != NULL)
			err = OTLN_GetLastSister(*last_daughter, last_daughter);
	}
	
	return err;
}

/** OTLN_GetMother [49]
**/
Err		OTLN_GetMother(
				OTLN_TopicH daughter, 
				OTLN_TopicH *mother)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckTopicMagic(49, daughter);
	if (!err)
		*mother = DH(daughter)->mother;

	return err;
}

/** OTLN_GetNextSister [16]
**/
Err		OTLN_GetNextSister(
				OTLN_TopicH sister, 
				OTLN_TopicH *next_sister)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckTopicMagic(16, sister);
	if (!err)
		*next_sister = DH(sister)->next_sister;

	return err;
}

/** OTLN_GetPrevSister [17]
**/
Err		OTLN_GetPrevSister(
				OTLN_TopicH sister, 
				OTLN_TopicH *prev_sister)
{
	Err			err = Err_NONE;
	
	err = OTLN_CheckTopicMagic(17, sister);
	if (!err)
		*prev_sister = DH(sister)->prev_sister;

	return err;
}

/** OTLN_GetFirstSister [18]
**/
Err		OTLN_GetFirstSister(
				OTLN_TopicH sister, 
				OTLN_TopicH *first_sister)
{
	Err				err = OTLN_CheckTopicMagic(18, sister);

	if (!err) {
		do {
		
			*first_sister = sister;
			sister = DH(sister)->prev_sister;
			
		} while (sister != NULL);
	}

	return err;
}

/** OTLN_GetLastSister [19]
**/
Err		OTLN_GetLastSister(
				OTLN_TopicH sister, 
				OTLN_TopicH *last_sister)
{
	Err				err;
	
	err = OTLN_CheckTopicMagic(19, sister);

	if (!err) {
		do {
		
			*last_sister = sister;
			sister = DH(sister)->next_sister;
			
		} while (sister != NULL);
	}

	return err;
}
