//	OTLN_Iterate.c

#include "OTLNp.h"

/**		OTLN_RecursiveIterate	[20]
			iteration over the outline							

By the time we get to RecursiveIterate, the following iteration
types have been converted to OTLN_Iterate_ALL:
	OTLN_Iterate_CHILDREN
	OTLN_Iterate_SISTERS_AND_CHILDREN
	OTLN_Iterate_FAMILY

 **************************************************************/
static	Err		OTLN_RecursiveIterate(
						OTLN_TopicH					topic, 
						Boolean						hidden, 
						short						horizontal, 
						OTLN_IterateOutlineParams	*params)
{
	Err				err		= Err_NONE;
	Boolean			callOut, incrVertical;
	OTLN_TopicFlags	flags;
	
	if (OTLN_ITERATE_TYPE(params->type) != OTLN_Iterate_NONE && topic != NULL) {
	
		for ( ; topic != NULL && !err && !params->callbackData->done; topic = (**topic).next_sister) {

			err = OTLN_CheckTopicMagic(20, topic);

			if (!err) {
				
				flags = OTLN_GET_TopicH_FLAGS(topic);
				
				callOut			= TRUE;
				incrVertical	= TRUE;
				
				if (OTLN_ITERATE_TYPE(params->type) == OTLN_Iterate_ONE) {
					params->callbackData->done = TRUE;
				}
				
				if (
					OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_ONSCREEN)
					|| OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_ONSCREEN_FAMILIES)
				) {
					short	vBottom = 	params->vertical + 1
										+ (
											params->ol->cell_height 
											* DH(topic)->cell_height_multiple
										);

					if (hidden) {
						callOut			= FALSE;
						incrVertical	= FALSE;
					} else if (vBottom < params->ol->bounds.top)
						callOut = FALSE;
					else {
						short	bottom;
						
						if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_ONSCREEN))
							bottom = params->ol->bounds.bottom;
						else
							bottom = params->ol->bounds.bottom - 2;
							
						if (params->vertical > bottom) {
							callOut = FALSE;
							params->callbackData->done = TRUE;
						}
					}
				}

//				uncollapsed has no bearing on the current topic, only its children				
//				if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNCOLLAPSED)) {
//				}
				
				if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_SELECTED)) {
					if (!OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_SELECTED))
						callOut = FALSE;
				} else if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNSELECTED)) {
					if (OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_SELECTED))
						callOut = FALSE;
				}
				
				if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNSHY)) {
					if (
						params->ol->honor_family_invisable 
						&& OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_FAMILY_INVISABLE)
					) {
						incrVertical	= FALSE;
						callOut			= FALSE;
					}
				}
				
				if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_VISIBLE)) {
					if (OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_TOPIC_INVISABLE)) {
						incrVertical	= FALSE;
						callOut			= FALSE;
					}
				}
				
				if (callOut) {
					OTLN_TopicP		to;
					
					params->cellRect.top	= 0;

					if (hidden) {
						params->cellRect.left	= 0;
						params->cellRect.bottom	= 0;
						params->cellRect.right	= 0;
					} else {
						params->cellRect.left		= params->ol->outline_left;
						params->cellRect.bottom		= (params->ol->cell_height * DH(topic)->cell_height_multiple) + 1;
						OffsetRect(&(params->cellRect), horizontal, params->vertical);
						params->cellRect.right		= params->ol->outline_right;
					}

					to = U_LOCK_DH(topic);
					err = (*(params->callback))(params->ol, to, &(params->cellRect), params->callbackData);
					U_UNLOCK_RH(topic);
					
					if (!err) {
						//	test for user abort.  If you can come up with a more
						//	elegant solution, feel free
						if (params->callbackData->done)
							goto bugout;
			
						flags = OTLN_GET_TopicH_FLAGS(topic);
					}
				}
				
				if (!hidden && incrVertical)
					params->vertical += params->ol->cell_height * DH(topic)->cell_height_multiple;

				if (!err && !params->callbackData->done) {
				
					callOut = TRUE;

					if (OTLN_ITERATE_TYPE(params->type) == OTLN_Iterate_SISTERS) {
						callOut = FALSE;
					}
					
					if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_ONSCREEN)) {
					} else if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_ONSCREEN_FAMILIES)) {
					}
	
					if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNCOLLAPSED)) {
						if (
							   !OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_EXPANDABLE)
							|| OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_COLLAPSED)
						) {
							callOut = FALSE;
						}
					}

//	selectedness has no bearing on children					
//					if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_SELECTED)) {
//					} else if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNSELECTED)) {
//					}
					
					if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_UNSHY)) {
						if (params->ol->honor_family_invisable && OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_IS_FAMILY_INVISABLE))
							callOut = FALSE;
					}

//	visibility has no bearing on children					
//					if (OTLN_IS_ITERATE_MOD(params->type, OTLN_IterateMod_VISIBLE)) {
//					}
	
					if (callOut) {
						Boolean		oldHidden = hidden;
						
						if (OTLN_IS_TopicFlag_SET(flags, OTLN_TopicFlags_COLLAPSED))
							hidden = TRUE;

						err = OTLN_RecursiveIterate((**topic).daughters, hidden, horizontal + params->ol->cell_height, params);
						
						if (!err) {
							//	test for user abort.  If you can come up with a more
							//	elegant solution, feel free
							if (params->callbackData->done)
								goto bugout;		//	not really an error, just jumping out
						}

						hidden = oldHidden;
					}
				}
			}
		}
	}
	
	bugout:
	return err;
}

/**		OTLN_IterateOutline		[21]
**/
Err			OTLN_IterateOutline(
					OTLN_OutlineH					outline, 
					OTLN_TopicH						startTopic0, 
					OTLN_IterateType				iterateType, 
					OTLN_IterateOutlineCallback		callback, 
					OTLN_GenericCallbackData		*callbackData0)
{
	Err							err = Err_NONE;
	OTLN_IterateOutlineParams	params;
	OTLN_GenericCallbackData	localCallbackData;
	Boolean						keep_going = TRUE;
	
	U_ASSERT(OTLN_ITERATE_TYPE(iterateType) != OTLN_Iterate_FAMILY);
	
	if (!callback) {
		//	"outline without iterate function"
		OTLN_Report(21, OTLNp_STR(6), OTLNp_STR(7));
		err = Err_PARAMETER;
	}
	
	if (!err)
		err = OTLN_CheckOutlineMagic(21, outline);

	if (!err) {
		
		if (OTLN_ITERATE_TYPE(iterateType) == OTLN_Iterate_SISTERS_AND_CHILDREN) {
		
			if (startTopic0 == NULL) {
				//	"family without family reference."
				OTLN_Report(21, OTLNp_STR(6), OTLNp_STR(9));
				err = Err_PARAMETER;
			}
			
			OTLN_SET_ITERATE_TYPE(iterateType, OTLN_Iterate_ALL);

		} else if (OTLN_ITERATE_TYPE(iterateType) == OTLN_Iterate_CHILDREN) {
		
			if (startTopic0 == NULL) {
				//	"children without child reference."
				OTLN_Report(21, OTLNp_STR(6), OTLNp_STR(10));
				err = Err_PARAMETER;
			}
			
			if (!err)
				err = OTLN_GetFirstDaughter(startTopic0, &startTopic0);
			
			if (!err) {
			
				if (startTopic0 == NULL) {
					keep_going = FALSE;
				} else {
					OTLN_SET_ITERATE_TYPE(iterateType, OTLN_Iterate_ALL);
				}
			}
		}
	}
	
	if (!err) {
		if (startTopic0 == NULL)
			err = OTLN_GetFirstTopic(outline, &startTopic0);
		
//		if (!err) {
//			
//			if (startTopic0 == NULL) {
//				//	"outline without outline reference."
//				OTLN_Report(21, OTLNp_STR(6), OTLNp_STR(8));
//				err = Err_PARAMETER;
//			}
//		}
	}
	
	if (!err && keep_going) {
		
		if (callbackData0)
			params.callbackData		= callbackData0;
		else
			params.callbackData		= &localCallbackData;
		
		if (!err) {
			params.ol					= U_LOCK_DH(outline);
			params.type					= iterateType;
			params.callback				= callback;
			params.vertical				= 0;
			params.callbackData->done	= FALSE;
		
			err = OTLN_RecursiveIterate(startTopic0, FALSE, 0, &params);
		
			U_UNLOCK_RH(outline);
		}
	}
	
	return(err);
}

typedef	struct {
	OTLN_OutlineH					outline;
	OTLN_TopicH						root_topic;
	OTLN_IterateDeepSelectionCB		callback;
	OTLN_GenericCallbackData		*callback_data;
} OTLN_IDSData;

/** OTLN_IterateDeepSelectionR [70] (recursive)
**/
static	Err		OTLN_IterateDeepSelectionR(
	OTLN_TopicH		topic, 
	OTLN_IDSData	*data
) {
	Err				err = Err_NONE;

	if (topic)	{
		err = OTLN_CheckTopicMagic(70, topic);
		if (!err) {
			err = OTLN_IterateDeepSelectionR(DH(topic)->daughters, data);
			if (!err && !data->callback_data->done) {
				err = OTLN_IterateDeepSelectionR(DH(topic)->next_sister, data);
				if (!err && !data->callback_data->done) {
					OTLN_TopicH		loop_topic	= topic;
					Boolean			is_selected	= FALSE;
					
					do {
						if (OTLN_IS_TopicFlag_SELECTED(loop_topic)) {
							is_selected = TRUE;
							loop_topic = NULL;
						} else {
							err = OTLN_GetMother(loop_topic, &loop_topic);
							if (err || loop_topic == data->root_topic)
								loop_topic = NULL;
						}
					} while (loop_topic);
					
					if (!err && is_selected) {
						err = (*data->callback)(
							data->outline, topic, 
							(**topic).custom_data, 
							data->callback_data
						);
					}
				}
			}
		}
	}
	
	return err;
}

/** OTLN_IterateDeepSelection [71]
**/
Err		OTLN_IterateDeepSelection(
	OTLN_OutlineH					outline, 
	OTLN_IterateDeepSelectionCB		callback, 
	OTLN_GenericCallbackData		*callback_data
) {
	Err				err	= Err_NONE;
	OTLN_TopicH		topic;
	OTLN_IDSData	data;

	data.outline					= outline;
	data.callback					= callback;
	data.callback_data				= callback_data;
	callback_data->done				= FALSE;
	callback_data->skip_this_family	= FALSE;
	
	if (!err)
		err = OTLN_GetRootTopic(outline, &data.root_topic);
	
	if (!err)
		err = OTLN_GetFirstTopic(outline, &topic);
	
	if (!err)
		err = OTLN_IterateDeepSelectionR(topic, &data);
	
	return err;
}
