#help_index "DolDoc/Editor"

public Bool DocGoToLine(CDoc *doc, I64 line_num) //one based
{//Nearest to specified line num.  Move cur_entry & center.
	Bool res = FALSE, unlock;

	if (doc)
	{
		unlock = DocLock(doc);
		doc->x = 0;
		doc->y = line_num - 1;
		DocRecalc(doc, RECALCt_FIND_CURSOR);
		DocCenter(doc);
		if (doc->cur_entry->y == line_num - 1)
			res = TRUE;
		if (unlock)
			DocUnlock(doc);
	}

	return res;
}

public Bool DocFind(CDoc *haystack_doc, I64 start_line_num=I64_MIN, U8 *needle, I64 match=1)
{//Find str by searching tags. Move cur_entry & center.
	Bool		 res = FALSE, unlock;
	CDocEntry	*doc_e;
	U8			*ptr;
	I64			 i;

	if (haystack_doc)
	{
		unlock = DocLock(haystack_doc);
		if (start_line_num == I64_MIN)
		{
			res = TRUE;
			doc_e = haystack_doc->head.next;
		}
		else
		{
			res = DocGoToLine(haystack_doc, start_line_num);
			doc_e = haystack_doc->cur_entry;
		}
		if (res)
		{
			if (needle)
			{
				res = FALSE;
				while (doc_e != haystack_doc)
				{
					if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && //TODO: handle multi-DocEntry strs
						(ptr = StrIMatch(needle, doc_e->tag)))
					{
						i = ptr-doc_e->tag;
						if (!--match)
						{
							haystack_doc->cur_entry = doc_e;
							if (i < doc_e->min_col)
								i = doc_e->min_col;
							if (i > doc_e->max_col)
								i = doc_e->max_col;
							haystack_doc->cur_col = i;
							res = TRUE;
							break;
						}
					}
					doc_e = doc_e->next;
				}
			}
			else
				res = FALSE;
		}
		if (!res)
			DocBottom(haystack_doc);
		DocCenter(haystack_doc);
		if (unlock)
			DocUnlock(haystack_doc);
	}

	return res;
}

public Bool DocAnchorFind(CDoc *haystack_doc, U8 *needle_str)
{//Find named anchor. Move cur_entry & center.
	Bool		 res = FALSE, unlock;
	CDocEntry	*doc_e;

	if (haystack_doc)
	{
		unlock = DocLock(haystack_doc);
		doc_e = haystack_doc->head.next;
		if (needle_str)
			while (doc_e != haystack_doc)
			{
				if (doc_e->type_u8 == DOCT_ANCHOR && doc_e->de_flags & DOCEF_AUX_STR)
				{
					if (!StrCompare(needle_str, doc_e->aux_str))
					{
						haystack_doc->cur_entry	= doc_e;
						haystack_doc->cur_col	= doc_e->min_col;
						res = TRUE;
						break;
					}
				}
				doc_e = doc_e->next;
			}
		if (!res)
			DocBottom(haystack_doc);
		DocCenter(haystack_doc);
		if (unlock)
			DocUnlock(haystack_doc);
	}

	return res;
}

public U0 EdFindNext(CDoc *doc)
{//Editor F3 find next,  possibly doing replaces.
	Bool		 unlock = DocLock(doc);
	U8			*ptr, *ptr2, *ptr3;
	CDocEntry	*doc_ce = doc->cur_entry, *doc_e = doc_ce;
	I64			 sf_flags;

	if (doc->find_replace->match_case)
		sf_flags = 0;
	else
		sf_flags = SFF_IGNORE_CASE;
	if (doc->find_replace->whole_labels)
		sf_flags |= SFG_WHOLE_LABELS;
	do
	{
		if (doc_e != doc)
		{
			if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && !(doc_e->de_flags & (DOCEG_DONT_EDIT | DOCEF_FILTER_SKIP)))
			{
				if (doc_e->type & DOCET_SEL || !doc->find_replace->scan_sel_text)
				{
					if (doc->find_replace->scan_fwd)
					{
						if (doc_e == doc_ce)
						{
							ptr = doc_ce->tag + doc->cur_col + 1;
							if (ptr - doc_ce->tag >= doc_ce->max_col)
								goto fn_skip;
							if (ptr - doc_ce->tag < doc_ce->min_col)
								ptr = doc_ce->tag + doc_ce->min_col;
						}
						else
							ptr = doc_e->tag;
						if (ptr = StrFind(doc->find_replace->find_text, ptr, sf_flags))
						{
							doc->cur_entry	= doc_e;
							doc->cur_col	= ptr-doc_e->tag;
							if (doc->cur_col >= doc_e->max_col)
								doc->cur_col = doc_e->max_col - 1;
							if (doc->cur_col < doc_e->min_col)
								doc->cur_col = doc_e->min_col;
							DocCenter(doc);
							if (unlock)
								DocUnlock(doc);
							return;
						}
					}
					else
					{
						ptr2 = NULL;
						ptr = doc_e->tag + doc_e->min_col;
						if (doc_e == doc_ce)
							ptr3 = doc_ce->tag + doc->cur_col;
						else
							ptr3 = doc_e->tag + doc_e->max_col;
						while (ptr = StrFind(doc->find_replace->find_text, ptr, sf_flags))
						{
							if (ptr >= ptr3)
								break;
							ptr2 = ptr++;
						}
						if (ptr2 && ptr2 < ptr3)
						{
							doc->cur_entry	= doc_e;
							doc->cur_col	= ptr2 - doc_e->tag;
							if (doc->cur_col >= doc_e->max_col)
								doc->cur_col = doc_e->max_col - 1;
							if (doc->cur_col < doc_e->min_col)
								doc->cur_col = doc_e->min_col;
							DocCenter(doc);
							if (unlock)
								DocUnlock(doc);

							return;
						}
					}
				}
			}
		}
fn_skip:
		if (doc->find_replace->scan_fwd)
			doc_e = doc_e->next;
		else
			doc_e = doc_e->last;
	}
	while (doc_e != doc_ce);

	if (unlock)
		DocUnlock(doc);
}

public U0 EdSelAll(CDoc *doc, Bool sel)
{//Set state of $LK,"DOCET_SEL",A="MN:DOCET_SEL"$ on all entries.
	Bool		 unlock = DocLock(doc);
	CDocEntry	*doc_e = doc->head.next;

	while (doc_e != doc)
	{
		BEqual(&doc_e->type, DOCEt_SEL, sel);
		doc_e = doc_e->next;
	}
	if (unlock)
		DocUnlock(doc);
}

public Bool EdFindPaired(CDoc *doc, U8 plus, U8 minus, 
Bool forward, Bool abort_on_dbl_colon=FALSE)
{//Find { } or ( ) pair. Move cur_entry & center.
	Bool		 unlock = DocLock(doc), res = FALSE;
	U8			*ptr;
	I64			 ch, levels = 0, colons = 0, original_col = doc->cur_col;
	CDocEntry	*doc_ce = doc->cur_entry, *doc_e = doc_ce, *original_ce = doc_ce;

	if (abort_on_dbl_colon && EdCurU8(doc) == ':')
		colons = 1;
	else
		colons = 0;
	do
	{
		if (doc_e != doc)
		{
			if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && !(doc_e->de_flags & DOCEF_FILTER_SKIP))
			{
				if (forward)
				{
					if (doc_e == doc_ce)
						ptr = doc_e->tag + doc->cur_col + 1;
					else
						ptr = doc_e->tag;
					if (ptr - doc_e->tag < doc_e->min_col)
						ptr = doc_e->tag + doc_e->min_col;
					if (ptr - doc_e->tag >= doc_e->max_col)
						goto pa_skip;
					while (ch = *ptr++)
						if (abort_on_dbl_colon && ch == ':')
						{
							if (++colons == 2)
							{
								doc->cur_entry	= doc_e;
								doc->cur_col	= ptr - doc_e->tag - 1;
								EdCursorLeft(doc);
								res = FALSE;
								goto pa_done;
							}
						}
						else
						{
							colons = 0;
							if (ch == plus)
								levels++;
							else if (ch == minus)
							{
								if (!levels--)
								{
									doc->cur_entry	= doc_e;
									doc->cur_col	= ptr - doc_e->tag - 1;
									res = doc->cur_entry != original_ce || doc->cur_col != original_col;
									goto pa_done;
								}
							}
						}
				}
				else
				{
					if (doc_e == doc_ce)
					{
						ptr = doc_e->tag + doc->cur_col - 1;
						if (ptr - doc_e->tag >= doc_e->max_col)
							ptr = doc_e->tag + doc_e->max_col - 1;
					}
					else
						ptr = doc_e->tag + doc_e->max_col - 1;
					if (ptr - doc_e->tag < doc_e->min_col)
						goto pa_skip;
					while (ptr >= doc_e->tag + doc_e->min_col)
					{
						ch = *ptr--;
						if (abort_on_dbl_colon && ch == ':')
						{
							if (++colons == 2)
							{
								doc->cur_entry	= doc_e;
								doc->cur_col	= ptr - doc_e->tag + 1;
								res = FALSE;
								goto pa_done;
							}
						}
						else
						{
							colons = 0;
							if (ch == plus)
								levels++;
							else if (ch == minus)
							{
								if (!levels--)
								{
									doc->cur_entry	= doc_e;
									doc->cur_col	= ptr-doc_e->tag+1;
									res = doc->cur_entry != original_ce || doc->cur_col != original_col;
									goto pa_done;
								}
							}
						}
					}
				}
			}
		}
pa_skip:
		if (forward)
			doc_e = doc_e->next;
		else
			doc_e = doc_e->last;
	}
	while (doc_e != doc_ce);

pa_done:
	DocRecalc(doc);
	DocCenter(doc);
	if (unlock)
		DocUnlock(doc);

	return res;
}

public Bool EdGoToFun(CDoc *doc, Bool forward, Bool abort_on_dbl_colon)
{//Move cur_entry to start of cur fun and center.(Shoddy)
	Bool unlock = DocLock(doc), res = FALSE;
	I64  ch, levels, colons;

	if (forward)
	{
		levels = 0;
		colons = 0;
		while (doc->cur_entry != doc)
		{
			ch = EdCurU8(doc);
			if (abort_on_dbl_colon && ch == ':')
			{
				if (++colons == 2)
				{
					EdCursorLeft(doc);
					break;
				}
			}
			else
			{
				colons = 0;
				if (ch == '{')
					levels++;
				else if (ch == '}' && !levels--)
					break;
			}
			EdCursorRight(doc);
		}
		DocRecalc(doc);
		if (doc->cur_entry != doc)
			res = TRUE;
	}
	else
	{
		while (EdFindPaired(doc, '}', '{', FALSE, abort_on_dbl_colon));
		if (doc->cur_entry != doc)
		{
			ch = EdCurU8(doc);
			if (abort_on_dbl_colon && ch == ':')
				res = TRUE;
			else
			{
				if (ch == '{')
					res = TRUE;
			}
		}
	}
	if (unlock)
		DocUnlock(doc);

	return res;
}

public U0 EdSelFun(CDoc *doc, Bool abort_on_dbl_colon=FALSE)
{//Set $LK,"DOCET_SEL",A="MN:DOCET_SEL"$ on all entries in cur fun.
	Bool		 unlock = DocLock(doc);
	U8			*ptr;
	I64			 ch, levels = 0, colons = 0;
	CDocEntry	*doc_e;

	EdSelAll(doc, FALSE);
	EdGoToFun(doc, FALSE, abort_on_dbl_colon);
	if (EdCurU8(doc) == '{')
		levels--;
	else if (abort_on_dbl_colon && EdCurU8(doc) == ':')
	{
		EdCursorRight(doc);
		if (EdCurU8(doc) == ':')
			EdCursorRight(doc);
	}
	doc_e = doc->cur_entry;
	while (doc_e != doc)
	{
		doc_e->type |= DOCET_SEL;
		if (doc_e->de_flags & DOCEF_TAG && doc_e->tag)
		{
			ptr = doc_e->tag;
			if (doc_e == doc->cur_entry)
				ptr += doc->cur_col;
			while (ch = *ptr++)
				if (abort_on_dbl_colon && ch == ':')
				{
					if (++colons == 2)
						goto sf_done;
				}
				else
				{
					colons = 0;
					if (ch == '{')
						levels++;
					else if (ch == '}' && !levels--)
						goto sf_done;
				}
		}
		doc_e = doc_e->next;
	}
sf_done:
	DocRecalc(doc);
	if (unlock)
		DocUnlock(doc);
}

#define RSAC_REPLACE	0
#define RSAC_SKIP 		1
#define RSAC_ALL		2

I64 PopUpReplaceSkipAllCancel(U8 *header=NULL, U8 *footer=NULL)
{
	I64   i;
	CDoc *doc = DocNew;

	if (header)
		DocPrint(doc, "%s", header);
	DocPrint(doc,
				"$$CM+LX,1,4$$$$BT,\"REPLACE\",LE=RSAC_REPLACE$$"
				"$$CM+LX,17,0$$$$BT,\"SKIP\",LE=RSAC_SKIP$$"
				"$$CM+LX,1,3$$$$BT,\"ALL\",LE=RSAC_ALL$$"
				"$$CM+LX,17,0$$$$BT,\"CANCEL\",LE=DOCM_CANCEL$$\n");
	if (footer)
		DocPrint(doc, "%s", footer);
	i = PopUpMenu(doc);
	DocDel(doc);

	return i;
}

I64 EdFindReplace(CDoc *doc)
{
	Bool		 found, unlock;
	I64			 cmd, i, j, plen, rlen, dlen, res = -1, sf_flags;
	U8			*src, *dst, *dst2;
	CDocEntry	*doc_ce, *doc_e, *doc_marker = NULL;

	if (doc->find_replace->prompt)
		cmd = RSAC_REPLACE;
	else
		cmd = RSAC_ALL;
	if (!doc->find_replace->prompt || DocForm(doc->find_replace))
	{
		res = 0;
		unlock = DocLock(doc);
		if (doc->find_replace->match_case || doc->find_replace->local_var)
			sf_flags = 0;
		else
			sf_flags = SFF_IGNORE_CASE;
		if (doc->find_replace->whole_labels || doc->find_replace->local_var)
			sf_flags |= SFG_WHOLE_LABELS;

		if (i = doc->find_replace->filter_lines)
		{
			doc_ce = doc->head.next;
			while (doc_ce != doc)
			{
				if (doc_ce->de_flags & DOCEF_TAG && doc_ce->tag && !(doc_ce->de_flags & DOCEF_FILTER_SKIP) &&
					StrFind(doc->find_replace->find_text, doc_ce->tag, sf_flags))
				{
					doc_ce->type |= DOCET_SEL;
					res++;
				}
				else
					doc_ce->type &= ~DOCET_SEL;
				doc_ce = doc_ce->next;
			}

			doc_ce = doc->head.next;
			while (doc_ce != doc)
			{
				if (!(doc_ce->de_flags & DOCEF_FILTER_SKIP))
				{
					found = FALSE;

					doc_e = doc_ce;
					while (doc_e != doc && doc_e->y > doc_ce->y - i)
					{
						if (doc_e->type & DOCET_SEL)
						{
							found = TRUE;
							break;
						}
						else
							doc_e = doc_e->last;
					}

					if (!found)
					{
						doc_e = doc_ce;
						while (doc_e != doc && doc_e->y < doc_ce->y + i)
						{
							if (doc_e->type & DOCET_SEL)
							{
								found = TRUE;
								break;
							}
							else
								doc_e = doc_e->next;
						}
					}

					if (!found)
						doc_ce->de_flags |= DOCEF_FILTER_SKIP;
				}

				doc_ce = doc_ce->next;
			}
			EdSelAll(doc, FALSE);
			goto fr_unlock_done;
		}

		if (doc->find_replace->local_var)
			EdSelFun(doc);

		if (!doc->find_replace->replace && !doc->find_replace->local_var)
		{
			EdFindNext(doc);
			goto fr_unlock_done;
		}
		plen = StrLen(doc->find_replace->find_text);
		if (!plen)
			goto fr_unlock_done;
		rlen = StrLen(doc->find_replace->replace_text);
		if (doc->head.next != doc)
		{
			doc_e = doc_marker = DocSplitTag(doc, doc->cur_entry, doc->cur_col,
									doc->cur_entry->x + doc->cur_col, doc->cur_entry->y, DOCT_MARKER);
			do
			{
				if (doc_e == doc)
				{
					if (doc->find_replace->scan_fwd)
						doc_e = doc_e->next;
					else
						doc_e = doc_e->last;
					if (doc_e == doc_marker)
						break;
				}
				if (doc_e->type_u8 == DOCT_TEXT &&
					!(doc_e->de_flags & (DOCEG_DONT_EDIT | DOCEF_FILTER_SKIP)) &&
					(doc_e->type & DOCET_SEL || !doc->find_replace->scan_sel_text && !doc->find_replace->local_var))
				{
					src = doc_e->tag;
					while (src)
					{
						src = StrFind(doc->find_replace->find_text, src, sf_flags);
						if (src)
						{
							doc->cur_col	= src - doc_e->tag;
							doc->cur_entry	= doc_e;
							if (cmd != RSAC_ALL)
								DocCenter(doc);
							if (cmd != RSAC_ALL)
							{
								DocUnlock(doc);
								cmd = PopUpReplaceSkipAllCancel("");
								DocLock(doc);
								if (cmd < 0)
									goto fr_unlock_done;
							}
							doc_e = doc->cur_entry;
							src = doc->cur_col + doc_e->tag;
							if (cmd == RSAC_REPLACE || cmd == RSAC_ALL)
							{
								dlen = StrLen(doc_e->tag);
								doc_e->max_col = dlen + rlen - plen;
								dst = MAlloc(doc_e->max_col + 1, doc->mem_task);
								dst2 = dst;
								j = src - doc_e->tag;
								for (i = 0; i < j; i++)
									*dst++ = doc_e->tag[i];
								for (i = 0; i < rlen; i++)
									*dst++ = doc->find_replace->replace_text[i];
								src = dst;
								for (i = j + plen; i <= dlen; i++)
									*dst++ = doc_e->tag[i];
								Free(doc_e->tag);
								doc_e->tag = dst2;
								doc->cur_col	= src - doc_e->tag;
								doc->cur_entry	= doc_e;
								if (cmd != RSAC_ALL)
								{
									DocRemSoftNewLines(doc, doc->cur_entry);
									DocRecalc(doc);
								}
								doc_e = doc->cur_entry;
								src = doc->cur_col + doc_e->tag;
								res++;
							}
							else
								src++;
						}
					}
				}
				if (doc->find_replace->scan_fwd)
					doc_e = doc_e->next;
				else
					doc_e = doc_e->last;
			}
			while (doc_e != doc_marker);
		}
fr_unlock_done:
		if (doc_marker)
			DocEntryDel(doc, doc_marker);
		DocRemSoftNewLines(doc, NULL);
		DocRecalc(doc);
		DocCenter(doc);
		if (unlock)
			DocUnlock(doc);
	}

	return res;
}

public I64 EdReplace(CDoc *doc, U8 *find, U8 *replace, Bool sel=TRUE, Bool match_case=TRUE, Bool whole_labels=FALSE)
{//Find & replace using editor's cmd.
	CEdFindText	old_find_replace;
	Bool		unlock;
	I64			i, res = -1;

	if (!doc)
		return -1;
	unlock = DocLock(doc);
	MemCopy(&old_find_replace, doc->find_replace, sizeof(CEdFindText));
	MemSet(doc->find_replace, 0, sizeof(CEdFindText));
	i = StrLen(find);
	if (i < sizeof(CEdFindText.find_text))
	{
		MemCopy(doc->find_replace->find_text, find, i + 1);
		i = StrLen(replace);
		if (i < sizeof(CEdFindText.replace_text))
		{
			MemCopy(doc->find_replace->replace_text, replace, i + 1);
			doc->find_replace->replace		 = TRUE;
			doc->find_replace->scan_sel_text = sel;
			doc->find_replace->match_case	 = match_case;
			doc->find_replace->whole_labels	 = whole_labels;
			doc->find_replace->prompt		 = FALSE;
			res = EdFindReplace(doc);
		}
	}
	MemCopy(doc->find_replace, &old_find_replace, sizeof(CEdFindText));
	if (unlock)
		DocUnlock(doc);

	return res;
}

class CEdLineGoTo
{
	I64 line format "$$DA,A=\"Go to Line:%d\"$$";
};

U0 EdGoToLine(CDoc *doc)
{//Prompt with form and go to line num.
	CEdLineGoTo gtl;

	gtl.line = 1;
	if (DocForm(&gtl))
		DocGoToLine(doc, gtl.line);
}
