#if 1
//	OTLN_COutline.c

#include "O_COutline.h"

//IMPLEMENT_RTTC(OTLN_COutline);

Err			OTLNg_GetCOutline(OTLN_OutlineH outlineH, OTLN_COutline **coutline)
{
	return OTLN_GetOutlineCustomData(outlineH, (void **)coutline);
}

Err			OTLNpg_OT_2_COT(
	OTLN_OutlineAndTopicP		source, 
	OTLN_COutlineAndCTopicP		dest
) {
	Err		err = Err_NONE;
	
	if (!err) err = OTLNg_GetCOutline(source->outlineH, &dest->cOutline);
	if (!err) err = OTLNg_GetCTopic(source->topicH, &dest->cTopic);
	
	return err;
}

/**************************
	Initialize
	
	either pass in outlineH0 or it will be created for you
**************************/
Err			OTLN_COutline::OTLNm_IOutline(
	Err				*operator_err, 
	OTLN_OutlineH	outlineH0
) {
	Err					err = Err_NONE;
	OTLN_OutlineH		outline;
	
	OTLNpi_user_cb_dispatch_proc	= NULL;
	OTLNpi_custom_data				= NULL;
	OTLNpi_disposing				= FALSE;
	OTLNpi_operator_err				= operator_err;
	OTLNi_outlineH					= NULL;
	OTLNpi_indent_width0			= 0;
	i_v_bottom						= 0;
	
	if (outlineH0) {
		outline = outlineH0;
	} else {
		//	creates outline and root topic
		if (!err) err = OTLN_NewOutline(NULL, &outline);

		if (!err) err = OTLN_SetOutlineCustomData(
			outline, this, 
			OTLNspm_DuplicateSelfCB, 
			OTLNspm_DisposeSelfCB
		);
	}
	
	//	attatches object to root topic
	if (!err) {
		OTLN_TopicH		root;
		OTLN_CTopic		*ctopic;
		
		err = OTLN_GetRootTopic(outline, &root);
		if (!err) err = OTLNm_NewTopic(NULL, root, &ctopic);
	}

	if (!err) {
		OTLNi_outlineH	= outline;
	}
	
	return err;
}

/**************************
	Duplicate
	
	OTLN_DupOutline and 
	coutline->Duplicate
	produce the same results
	either way, both the OTLN outline and the COutline objects are duplicated
	assuming the callbacks are in place
**************************/
//	static
Err				OTLN_COutline::OTLNspm_DuplicateSelfCB(
	OTLN_OutlineAndTopic	*to_source0, 		//	always passed out, but may be ignored
	void					*custom_source, 
	OTLN_OutlineAndTopic	*to_dest0, 			//	always passed out, but may be ignored
	void 					**custom_dest
) {
	Err					err		= Err_NONE;
	OTLN_COutline		*thiz	= (OTLN_COutline *)custom_source;
	
	err = thiz->OTLNm_DuplicateSelf(to_source0, to_dest0, (OTLN_COutline **)custom_dest);
	
	return err;
}


//	[200]
Err			OTLN_COutline::OTLNm_DuplicateSelf(
	OTLN_OutlineAndTopic	*source_ot, 
	OTLN_OutlineAndTopic	*dest_ot, 
	OTLN_COutline			**duplicate
) {
	Err					err		= Err_NONE;

	*duplicate = new(OTLN_COutline);
	
	if (*duplicate == NULL) {
		err = memFullErr;
		OTLNp_PROB_S(200, OTLNp_STR(19));
	} else {
		
		err = (*duplicate)->OTLNm_IOutline(OTLNpi_operator_err, dest_ot->outlineH);
		
		if (err) {
			(void)(*duplicate)->OTLNm_DisposeOutline();
			*duplicate = NULL;
		}
	}
	
	return err;
}

Err		OTLN_COutline::OTLNm_Duplicate(OTLN_COutline **duplicate)
{
	Err					err			= Err_NONE;
//	OTLN_COutline		*that		= NULL;
	OTLN_OutlineH		outline;
	
	if (!err) err = OTLN_DupOutline(OTLNi_outlineH, &outline);
	if (!err) err = OTLNg_GetCOutline(outline, duplicate);
	
	return err;
}

/**************************
	Dispose
	
	you can call dispose thru the object or by calling
	 the otln dispose call directly.  either way, both 
	 the otln struct and the object get disposed
	 
	 
	 
	 ****  WARNING  ****
		
	 to dispose:  call DisposeObject(), NOT Dispose()
	 DisposeObject returns an error, Dispose does NOT
**************************/

Err				OTLN_COutline::OTLNspm_DisposeSelfCB(void *custom_data)
{
	OTLN_COutline	*thiz	= (OTLN_COutline *)custom_data;
	Err				err		= Err_NONE;
		
	if (!err) err = thiz->OTLNm_DisposeSelf();
	
	return err;
}

/*
	Err			err = Err_NONE;
	
	if (!OTLNpi_disposing) {
		inherited::Dispose();
	}
	
	return err;
*/
Err				OTLN_COutline::OTLNm_DisposeSelf(void)
{
	delete this;

	return Err_NONE;
}

/*
	Err			err = Err_NONE, err2;

	if (!OTLNpi_disposing) {
		OTLNpi_disposing = TRUE;
		if (!err) err = OTLN_DisposeOutline(OTLNi_outlineH);
		OTLNpi_disposing = FALSE;
		err2 = OTLNm_DisposeSelf();
		
		if (!err) err = err2;
	}
	
	return err;
*/
Err				OTLN_COutline::OTLNm_DisposeOutline(void)
{
	return OTLN_DisposeOutline(OTLNi_outlineH);
}


void				OTLN_COutline::Dispose(void)
{
	/*
		you should never call this 
		cuz it does NOT return an error.
		you should call DisposeObject directly
	*/
	
	(void)OTLNm_DisposeOutline();
}


/**************************
	Callback Dispatch and custom user data
**************************/
Err		OTLN_COutline::OTLNm_SetCallbackDispatch(OTLN_CBDispatchProc user_cb_dispatch_proc)
{
	Err		err = Err_NONE;

	OTLNpi_user_cb_dispatch_proc = user_cb_dispatch_proc;

	return err;
}

Err		OTLN_COutline::OTLNm_GetCallbackDispatch(OTLN_CBDispatchProc *user_cb_dispatch_proc)
{
	Err		err = Err_NONE;

	*user_cb_dispatch_proc = OTLNpi_user_cb_dispatch_proc;

	return err;
}

Err		OTLN_COutline::OTLNm_SetCustomData(void *custom_data)
{
	Err		err = Err_NONE;

	OTLNpi_custom_data = custom_data;

	return err;
}

Err		OTLN_COutline::OTLNm_GetCustomData(void **custom_data)
{
	Err		err = Err_NONE;

	*custom_data = OTLNpi_custom_data;

	return err;
}

/**************************
	access
**************************/
Err				OTLN_COutline::OTLNm_SetCellHeight(short height)
{
	DH(OTLNi_outlineH)->cell_height = height;
	
	return Err_NONE;
}

Err				OTLN_COutline::OTLNm_GetCellHeight(short *height)
{
	*height = DH(OTLNi_outlineH)->cell_height;

	return Err_NONE;
}

//	note: the topic0 flag is never used in the whole program
Err				OTLN_COutline::OTLNm_SetVisFlags(Boolean *family0, Boolean *topic0)
{
	return OTLN_SetOutlineInvisableFlags(OTLNi_outlineH, family0, topic0);
}

//	note: the topic0 flag is never used in the whole program
Err				OTLN_COutline::OTLNm_GetVisFlags(Boolean *family0, Boolean *topic0)
{
	return OTLN_GetOutlineInvisableFlags(OTLNi_outlineH, family0, topic0);
}

Err				OTLN_COutline::OTLNm_GetOutlineFrame(M_Rect *frame)
{
	Err							err = Err_NONE;
	
	if (!err && OTLNpi_user_cb_dispatch_proc) {
		OTLN_COutlineAndCTopic		cot;
		
		cot.cOutline	= this;
		cot.cTopic		= NULL;
		
		err = (OTLNpi_user_cb_dispatch_proc)(
			&cot, OTLN_CB_GET_FRAME, (OTLN_CBData *)frame
		);
	}

	return err;
}

Err				OTLN_COutline::OTLNm_SetIndentWidth(short indent_width0)
{
	OTLNpi_indent_width0 = indent_width0;
	
	return Err_NONE;
}

Err		OTLN_COutline::OTLNm_UpdateBounds(void)
{
	Err			err = Err_NONE;
	M_Rect		bounds;
	
	err = OTLNm_GetOutlineFrame(&bounds);
	if (!err) err = OTLN_SetOutlineSpecs(
		OTLNi_outlineH, &bounds, NULL, NULL, 
		NULL, NULL, NULL, FALSE
	);
	
	return err;
}

Err		OTLN_COutline::OTLNm_SetFontInfo(const char *fontName, short fontSize)
{
	Err			err = Err_NONE;
	
	err = OTLN_SetOutlineSpecs(
		OTLNi_outlineH, 
		NULL,	//	bounds 
		NULL, 	//	left edge
		NULL, 	//	right edge
		fontName, fontSize, 
		NULL, 	//	icons
		FALSE	//	shared icons
	);
	
	return err;
}

/********  access  **********/

Err		OTLN_COutline::OTLNm_GetRootTopic(OTLN_CTopic **root_topic)
{
	Err				err = Err_NONE;
	OTLN_TopicH		topic;
	
	err = OTLN_GetRootTopic(OTLNi_outlineH, &topic);
	if (!err) err = OTLNg_GetCTopic(topic, root_topic);
	
	return err;
}

Err		OTLN_COutline::OTLNm_GetFirstTopic(OTLN_CTopic **first_topic)
{
	Err				err = Err_NONE;
	OTLN_TopicH		topic;
	
	*first_topic = NULL;
	
	err = OTLN_GetFirstTopic(OTLNi_outlineH, &topic);
	
	if (!err && topic) {
		err = OTLNg_GetCTopic(topic, first_topic);
	}
	
	return err;
}

/**************************
	recent topic
**************************/
Err				OTLN_COutline::OTLNm_SetRecent(OTLN_CTopic *recent)
{
	return OTLN_SetRecentTopic(OTLNi_outlineH, recent->OTLNpi_topicH);
}

Err				OTLN_COutline::OTLNm_GetRecent(OTLN_CTopic **recent)
{
	return OTLNg_GetCTopic(DH(OTLNi_outlineH)->recent_topic, recent);
}

/**************************
	Add new topic
**************************/
//	[201]
Err		OTLN_COutline::OTLNm_NewTopic(
	const	char			*name0, 
			OTLN_TopicH		topicH0, 
			OTLN_CTopic		**new_topic
) {
	Err				err = Err_NONE;

	*new_topic = new(OTLN_CTopic);
	
	if (*new_topic == NULL) {
		err = memFullErr;
		//	out of memory while creating an outline topic
		OTLNp_PROB_S(201, OTLNp_STR(20));
	} else {
		err = (*new_topic)->OTLNm_ITopic(OTLNpi_operator_err, topicH0, name0);
	}

	return err;
}

Err		OTLN_COutline::OTLNm_AddNewTopic(
	const	char				*name0, 
			OTLN_CTopicIndex	index, 			//	where i go in parent's list of children
			OTLN_CTopic			**new_topic0	//	pass back if not NULL
) {
	Err				err = Err_NONE;
	OTLN_CTopic		*cTopic, *parentTopic;
	
	err = OTLNm_NewTopic(name0, NULL, &cTopic);
	
	if (!err) {
		if (!err) err = OTLNm_GetRecent(&parentTopic);
		if (!err) err = cTopic->OTLNm_Adopt(this, parentTopic, index);
		if (!err) err = cTopic->OTLNm_Insert();
		
		if (!err) {
			if (new_topic0) {
				*new_topic0 = cTopic;
			}
		} else {
			(void)cTopic->OTLNm_DisposeTopic();
		}
	}

	return err;
}

Err		OTLN_COutline::OTLNm_GetOutlineState(OTLN_OutlineSaveH *otln)
{
	return OTLN_SaveTopics(OTLNi_outlineH, otln);
}

Err		OTLN_COutline::OTLNm_SetOutlineState(OTLN_OutlineSaveH *otln)
{
	return OTLN_RestoreTopics(OTLNi_outlineH, otln);
}

/**************************
	apply to items
**************************/
//	static		
Err		OTLN_COutline::OTLNpsm_InternalCBDispatchProcCB(
	OTLN_COutlineAndCTopicP	cot, 
	OTLN_CBType				cbType, 
	OTLN_CBDataP			cbData
) {
	return cot->cOutline->OTLNm_InternalCBDispatchProc(cot, cbType, cbData);
}

//	not static
Err		OTLN_COutline::OTLNm_InternalCBDispatchProc(
	OTLN_COutlineAndCTopicP	cot, 
	OTLN_CBType				cbType, 
	OTLN_CBDataP			cbData
) {
	Err				err = Err_NONE;

	switch (cbType) {
	
		case OTLN_CB_COUNT: {
			if (cbData->count.count_cells) {
				short		height;

				err = cot->cTopic->OTLNm_GetSpecs(NULL, &height, NULL);
				
				if (!err) cbData->count.count += height;
			} else {
				(cbData->count.count)++;
			}
			break;
		}

		case OTLN_CB_HIT_TEST: {
			Boolean		hit;
			
			cot->cTopic->OTLNm_HitTest(cbData->hit_test.hit_point, &hit);
			
			if (hit) {
				cbData->hit_test.cTopic = cot->cTopic;
				err = OTLN_Err_BREAK_FROM_LOOP;
			}
			break;
		}
		
		case OTLN_CB_SELECT: {
			if (cbData->select.except0 != cot->cTopic) {
				cot->cTopic->OTLNm_Select(cbData->select.select, cbData->select.draw);
			}
			break;
		}
		
		case OTLN_CB_CLEAR_RECTS: {
			M_Rect	rect = { 0, 0, 0, 0 };
			
			DH(cot->cTopic->OTLNpi_topicH)->recent_cell = rect;
			break;
		}
	}
	
	return err;
}

typedef	struct {
	OTLN_GenericCallbackDataHeader
	OTLN_CBType		cb_type;
	void			*data;
} OTLN_DeepSelectionCBData;

Err		OTLN_COutline::OTLNpsm_DeepIterateCB(
	OTLN_OutlineH				outline, 
	OTLN_TopicH					topic, 
	void						*custom_data, 
	OTLN_GenericCallbackData	*callback_data
) {
	Err							err			= Err_NONE;
	OTLN_DeepSelectionCBData	*deep_data	= (OTLN_DeepSelectionCBData *)callback_data;
	OTLN_COutlineAndCTopic		cot;
	
	cot.cTopic		= (OTLN_CTopic *)custom_data;
	if (!err) err	= OTLNg_GetCOutline(outline, &cot.cOutline);
	if (!err) err	= (*cot.cTopic->OTLNpi_user_cb_dispatch_proc)(
		&cot, deep_data->cb_type, (OTLN_CBData *)deep_data->data
	);
	
	return err;
}

Err			OTLN_COutline::OTLNm_ApplyToItems(
	OTLN_COutlineIterateType	iterate_type, 
	OTLN_CBType					cb_type, 
	void						*data
) {
	Err				err = Err_NONE;
	Boolean			duplicate = FALSE;
	OTLN_COutline	*thiz = this;

	if (
		iterate_type == OTLN_COutlineIterate_SELECTED_COPY
		|| iterate_type == OTLN_COutlineIterate_DEEP_SELECTED_COPY
	) {
		
		err = OTLNm_Duplicate(&thiz);
		duplicate = !err;
	}

	if (!err) switch (iterate_type) {
		case OTLN_COutlineIterate_UNSHY:
		case OTLN_COutlineIterate_TWIRLED_DOWN:
		case OTLN_COutlineIterate_VISIBLE:
		case OTLN_COutlineIterate_ONSCREEN:
		case OTLN_COutlineIterate_ALL:
		case OTLN_COutlineIterate_SELECTED_COPY: 
		case OTLN_COutlineIterate_SELECTED: {
			OTLN_CTopic		*first_topic;

			err = thiz->OTLNm_GetFirstTopic(&first_topic);

			if (!err && first_topic) {
				err = first_topic->OTLNm_ApplyToItems(
					iterate_type, cb_type, data, TRUE, TRUE
				);
			}
			break;
		}

		case OTLN_COutlineIterate_DEEP_SELECTED_COPY:
		case OTLN_COutlineIterate_DEEP_SELECTED: {
			OTLN_DeepSelectionCBData		deep_data;
			
			deep_data.cb_type	= cb_type;
			deep_data.data		= data;
			
			err = OTLN_IterateDeepSelection(
				thiz->OTLNi_outlineH, OTLNpsm_DeepIterateCB, 
				(OTLN_GenericCallbackData *)&deep_data
			);
		}
	}
	
	if (duplicate) {
		Err		err2 = thiz->OTLNm_DisposeOutline();
		
		if (!err) err = err2;
	}
	
	return err;
}

				/**************************
		specialized Apply rountines bulit from above base
				**************************/

Err			OTLN_COutline::OTLNm_CountVisibleCells(long *items)
{
	Err				err = Err_NONE;
	OTLN_CBData		cb_data;
	
	*items = 0;

	cb_data.count.count_cells = TRUE;
	cb_data.count.count = 0;
	
	err = OTLNm_ApplyToItems(
		OTLN_COutlineIterate_VISIBLE, OTLN_CB_COUNT, &cb_data
	);
	
	if (!err) {
		*items = cb_data.count.count;
	}
	
	return err;
}

Err			OTLN_COutline::OTLNm_CountVisibleItems(long *items)
{
	Err				err = Err_NONE;
	OTLN_CBData		cb_data;
	
	*items = 0;

	cb_data.count.count_cells = FALSE;
	cb_data.count.count = 0;
	
	err = OTLNm_ApplyToItems(
		OTLN_COutlineIterate_VISIBLE, OTLN_CB_COUNT, &cb_data
	);
	
	if (!err) {
		*items = cb_data.count.count;
	}
	
	return err;
}

Err			OTLN_COutline::OTLNm_CountSelectedItems(long *items)
{
	Err				err = Err_NONE;
	OTLN_CBData		cb_data;
	
	*items = 0;
	cb_data.count.count = 0;
	
	err = OTLNm_ApplyToItems(
		OTLN_COutlineIterate_SELECTED, OTLN_CB_COUNT, &cb_data
	);
	
	if (!err) {
		*items = cb_data.count.count;
	}
	
	return err;
}

Err			OTLN_COutline::OTLNm_Draw(Boolean scrub, long *v_bottom0)
{
	Err		err = Err_NONE;
	
	*v_bottom0 = 0;
	
	if (!err) err = OTLNm_ClearRects();
	if (!err) err = OTLNm_UpdateBounds();

	i_v_bottom = 0;

	if (!err) err = OTLNm_ApplyToItems(
		OTLN_COutlineIterate_ONSCREEN, OTLN_CB_DRAW, &scrub
	);
	
	if (!err && v_bottom0) {
		*v_bottom0 = i_v_bottom;
	}
	
	return err;
}

Err			OTLN_COutline::OTLNm_HitTest(Point hit_point, OTLN_CTopic **cTopic)
{
	Err							err = Err_NONE;
	OTLN_CBData_HIT_TEST		hit_test;
	
	*cTopic				= NULL;
	hit_test.hit_point	= hit_point;
	hit_test.cTopic 	= NULL;
	
	if (!err) err = OTLNm_ApplyToItems(
		OTLN_COutlineIterate_ONSCREEN, OTLN_CB_HIT_TEST, &hit_test
	);
	
	if (!err) {
		*cTopic = hit_test.cTopic;
	}
	
	return err;
}

Err				OTLN_COutline::OTLNm_SelectAll(
	Boolean		select, 
	Boolean		draw, 
	OTLN_CTopic	*except0
) {
	OTLN_CBData_SELECT		selData;
	
	selData.select	= select;
	selData.draw	= draw;
	selData.except0	= except0;
	
	return OTLNm_ApplyToItems(
		OTLN_COutlineIterate_ALL, 
		OTLN_CB_SELECT, 
		&selData
	);
}

Err				OTLN_COutline::OTLNm_ClearRects(void)
{
	return OTLNm_ApplyToItems(
		OTLN_COutlineIterate_ALL, OTLN_CB_CLEAR_RECTS, NULL
	);
}

/**************************
	operators
**************************/

OTLN_OutlineH		OTLN_COutline::operator=(OTLN_COutline *cOutline)
{
	return cOutline->OTLNi_outlineH;
}

OTLN_COutline		*OTLN_COutline::operator=(OTLN_OutlineH outlineH)
{
	OTLN_COutline		*cOutline;
	
	if (!(*OTLNpi_operator_err)) {
		*OTLNpi_operator_err = OTLNg_GetCOutline(outlineH, &cOutline);
	}

	return cOutline;
}

#endif