#help_index "DolDoc/Misc"

U0 EdReplaceTroubleOne(CDoc *doc, U8 *st_original, U8 *st_safe, I64 num, Bool to_safe, Bool sel)
{
	U8 buf[STR_LEN];

	StrPrint(buf, st_safe,num);
	if (to_safe)
		EdReplace(doc, st_original, buf, sel);
	else
		EdReplace(doc, buf, st_original, sel);
}

U0 EdReplaceTroubleAll(CDoc *doc,Bool to_safe,Bool sel)
{
	I64 i = 0;

	EdReplaceTroubleOne(doc, "#assert" , "//<@%d@>",	i++, to_safe, sel);
	EdReplaceTroubleOne(doc, "#define" , "//<@%d@>",	i++, to_safe, sel);
	EdReplaceTroubleOne(doc, "#include", "//<@%d@>",	i++, to_safe, sel);

//#if will match #if,#ifdef,#ifndef,#ifaot and #ifjit
	EdReplaceTroubleOne(doc, "#if",		 "//<@%d@>",	i++, to_safe, sel);
	EdReplaceTroubleOne(doc, "#endif",	 "//<@%d@>",	i++, to_safe, sel);

//Convert #exe to union because we want that indent pattern.
	EdReplaceTroubleOne(doc, "#exe",	 "union @%d@",	i++, to_safe, sel);
	EdReplaceTroubleOne(doc, "'{'",		 "'<@%d@>'",	i++, to_safe, sel);
	EdReplaceTroubleOne(doc, "'}'",		 "'<@%d@>'",	i++, to_safe, sel);
}

#define C_INDENT_SPACES 	2
#define ASM_RENUM_SPACING 	5

#define EF_REINDENT 		0
#define EF_CMP_CHK			1
#define EF_RENUM_ASM		2
#define EF_CTRL_SLIDER		3
#define EF_CH_SC			4

I64 PopUpEdFormat()
{
	I64   i;
	CDoc *doc = DocNew;

	DocPrint(doc,	"$$LTBLUE$$$$MU,\"Compile Check\",LE=EF_CMP_CHK$$\n"
					"$$MU,\"Reindent CosmiC Fun (Beware braces in strings.)\","
					"LE=EF_REINDENT$$\n"
					"$$MU,\"Renum Asm Local @@ Labels for Fun\",LE=EF_RENUM_ASM$$\n"
					"$$MU,\"Insert Template Code: Ctrl Slider\",LE=EF_CTRL_SLIDER$$\n"
					"$$MU,\"Insert ASCII/Scan Code Hex Codes for key pressed\","
					"LE=EF_CH_SC$$\n\n"
					"$$MU,\"CANCEL\",LE=DOCM_CANCEL$$\n\n"
					"$$GREEN$$<ALT-BACKSPACE>$$FG$$ to undo if not happy\n"
					"with the ress.\n");
	i = PopUpMenu(doc);
	DocDel(doc);

	return i;
}

class CRILex
{
	CCompCtrl	 *cc1, *cc2;
	CQueueVectU8 *indent;
	I64			  depth, exp_depth, one_shot;
	Bool		  was_new_line, is_not_cont;
};

I64 EdRILex(CRILex *rx)
{
	rx->is_not_cont = FALSE;

	I64		  i;
	CLexFile *tmpf;

	do
	{
		Lex(rx->cc1);
		Lex(rx->cc2);
		i = ParseKeyWord(rx->cc2);
		if (	rx->cc1->token == '\n' && rx->cc2->token == ';' ||
				rx->cc2->token == '{' || rx->cc2->token == '}'  || rx->cc2->token == ':' || rx->cc2->token == ')' &&
				!rx->exp_depth || i == KW_ELSE || i == KW_CATCH || i == KW_DO)
			rx->is_not_cont = TRUE;
		if (rx->was_new_line && (rx->cc1->token != ':' || i == KW_CASE || i == KW_DEFAULT || i == KW_START || i == KW_END))
		{
			tmpf = rx->cc2->lex_include_stack;
			while (tmpf->next)
				tmpf = tmpf->next;
			QueueVectU8Put(rx->indent, tmpf->cur_entry->y, rx->depth + rx->one_shot);
			rx->one_shot = 0;
		}
		if (rx->cc2->token == '\n')
			rx->was_new_line = TRUE;
		else
			rx->was_new_line = FALSE;
	}
	while (rx->cc1->token == '\n');

	return rx->cc1->token;
}

U0 EdRIExp(CRILex *rx)
{
	if (rx->cc1->token == '(')
	{
		if (!rx->exp_depth++)
			rx->depth += 3;
		EdRILex(rx);
		while (rx->cc1->token && rx->cc1->token != ')')
			EdRIExp(rx);
		if (!--rx->exp_depth)
		{
			rx->depth -= 3;
			if (rx->depth < 0)
				rx->depth = 0;
		}
	}
	else if (rx->cc1->token == '[')
	{
		if (!rx->exp_depth++)
			rx->depth += 3;
		EdRILex(rx);
		while (rx->cc1->token && rx->cc1->token != ']')
			EdRIExp(rx);
		if (!--rx->exp_depth)
		{
			rx->depth -= 3;
			if (rx->depth < 0)
				rx->depth = 0;
		}
	}
	EdRILex(rx);
}

U0 EdRIStatement(CRILex *rx, Bool indent)
{
	I64  i;
	Bool cont;

	if (rx->cc1->token == '{')
	{
		rx->depth++;
		EdRILex(rx);
		while (rx->cc1->token && rx->cc1->token != '}')
			EdRIStatement(rx, FALSE);
		if (--rx->depth < 0)
			rx->depth = 0;
		EdRILex(rx);
	}
	else
	{
		if (indent)
			rx->depth++;
		do
		{
			cont = FALSE;
			switch (ParseKeyWord(rx->cc1))
			{
				case KW_IF:
					EdRILex(rx);
					EdRIExp(rx);
					EdRIStatement(rx, TRUE);
					if (ParseKeyWord(rx->cc1) == KW_ELSE)
					{
						EdRILex(rx);
						if (ParseKeyWord(rx->cc1) == KW_IF && rx->cc2->token != '\n')
							EdRIStatement(rx, FALSE);
						else
							EdRIStatement(rx, TRUE);
					}
					break;

				case KW_TRY:
					EdRILex(rx);
					EdRIStatement(rx, TRUE);
					if (ParseKeyWord(rx->cc1) == KW_CATCH)
					{
						EdRILex(rx);
						EdRIStatement(rx, TRUE);
					}
					break;

				case KW_LOCK:
					EdRILex(rx);
					EdRIStatement(rx, TRUE);
					break;

				case KW_FOR:
				case KW_WHILE:
					EdRILex(rx);
					EdRIExp(rx);
					EdRIStatement(rx, TRUE);
					break;

				case KW_ASM:
				case KW_CLASS:
				case KW_UNION:
					if (EdRILex(rx) == TK_IDENT)
						EdRILex(rx);
					EdRIStatement(rx, TRUE);
					break;

				case KW_DO:
					EdRILex(rx);
					EdRIStatement(rx, TRUE);
					if (ParseKeyWord(rx->cc1) == KW_WHILE)
					{
						EdRILex(rx);
						EdRIExp(rx);
					}
					if (rx->cc1->token == ';')
						EdRILex(rx);
					break;

				case KW_SWITCH:
					EdRILex(rx);
					EdRIExp(rx);
					if (rx->cc1->token == '{')
					{
						rx->depth++;
						EdRILex(rx);
						i = 0;
						while (rx->cc1->token && rx->cc1->token != '}')
						{
							switch (ParseKeyWord(rx->cc1)) {
								case KW_START:
									rx->depth += i;
									i = 0;
									while (EdRILex(rx) && rx->cc1->token != ':');
									EdRILex(rx);
									i++;
									break;

								case KW_END:
									rx->depth += i;
									i = 0;
									if (--rx->depth < 0)
										rx->depth = 0;
									while (EdRILex(rx) && rx->cc1->token != ':');
									EdRILex(rx);
									break;

								case KW_CASE:
								case KW_DEFAULT:
									rx->depth += i;
									i = 0;
									while (EdRILex(rx) && rx->cc1->token != ':');
									EdRILex(rx);
									break;

								default:
									if (rx->cc1->token)
										EdRIStatement(rx, TRUE);
							}
						}
						if (--rx->depth < 0)
							rx->depth = 0;
						EdRILex(rx);
					}
					break;

				default:
					if (rx->cc1->token == TK_IDENT &&
						rx->cc1->hash_entry && rx->cc1->hash_entry->type & (HTT_OPCODE | HTT_ASM_KEYWORD))
					{
//					rx->one_shot=4-rx->depth;
						do EdRILex(rx);
						while (rx->cc2->token && rx->cc2->token != '\n');
						rx->is_not_cont = TRUE;
					}
					else
					{
						while (rx->cc1->token && rx->cc1->token != ';' && rx->cc1->token != ':')
						{
							if (rx->cc2->token == '\n' && !rx->is_not_cont)
								rx->one_shot = 3;
							EdRILex(rx);
						}
						if (rx->cc1->token == ':')
							cont = TRUE;
						EdRILex(rx);
					}
			}
		}
		while (cont && rx->cc1->token != '}');

		if (indent && --rx->depth < 0)
			rx->depth = 0;
	}
}

CQueueVectU8 *EdRICode(CDoc *doc)
{
	CQueueVectU8 *res;
	CRILex		 *rx = CAlloc(sizeof(CRILex));

	rx->cc1 = CompCtrlNew(, CCF_KEEP_NEW_LINES | CCF_DONT_FREE_BUF, doc->filename.name);
	Free(rx->cc1->lex_include_stack->full_name);
	LexAttachDoc(rx->cc1, rx->cc1->lex_include_stack, doc,, doc->cur_entry, doc->cur_col);

	rx->cc2 = CompCtrlNew(, CCF_KEEP_NEW_LINES | CCF_DONT_FREE_BUF, doc->filename.name);
	Free(rx->cc2->lex_include_stack->full_name);
	LexAttachDoc(rx->cc2, rx->cc2->lex_include_stack, doc,, doc->cur_entry, doc->cur_col);

	rx->indent = QueueVectU8New(doc->cur_entry->y);

	Lex(rx->cc1);
	EdRIStatement(rx, FALSE);

	CompCtrlDel(rx->cc1);
	CompCtrlDel(rx->cc2);
	res = rx->indent;
	Free(rx);

	return res;
}

U0 EdRemFunLeadingSpace(CDoc *doc)
{
	Bool		 unlock = DocLock(doc), start_of_line = TRUE;
	U8			*ptr;
	I64			 ch, levels = 1;
	CDocEntry	*doc_e, *doc_e2;

	EdGoToFun(doc, FALSE, FALSE);
	doc_e = doc->cur_entry->next;
	do
	{
		doc_e2 = doc_e->next;
		if (doc_e != doc && doc_e != doc->cur_entry && !(doc_e->de_flags & (DOCEG_DONT_EDIT - DOCEF_SCROLLING_X)))
			switch (doc_e->type_u8)
			{
				case DOCT_TEXT:
					ptr = doc_e->tag;
					if (start_of_line)
					{
						while (*ptr == CH_SPACE)
							ptr++;
						if (*ptr)
							start_of_line = FALSE;
						ptr = StrNew(ptr, doc->mem_task);
						Free(doc_e->tag);
						doc_e->tag = ptr;
					}
					if (!*ptr)
						DocEntryDel(doc, doc_e);
					else
					{
						while (ch = *ptr++)
							if (ch == '{')
								levels++;
							else if (ch == '}')
							{
								if (!--levels)
									break;
							}
						if (!levels)
							goto ls_done;
					}
					break;

				case DOCT_TAB:
					if (start_of_line)
						DocEntryDel(doc, doc_e);
					break;

				case DOCT_NEW_LINE:
					start_of_line = TRUE;
					break;

				default:
					start_of_line = FALSE;
			}
		doc_e = doc_e2;
	}
	while (doc_e != doc->cur_entry);

ls_done:
	DocRecalc(doc);
	DocCenter(doc);
	if (unlock)
		DocUnlock(doc);
}

class CRenum
{
	CRenum	*next, *last;
	U8		 label[sizeof(CEdFindText.find_text)];
};

I64 EdRAGetU8(CDoc *doc)
{
	I64 res = -1;

	while (doc->cur_entry != doc && doc->cur_entry->type & DOCET_SEL && res < 0)
	{
		res = EdCurU8(doc);
		EdCursorRight(doc);
	}

	return res;
}

U0 EdRACollect(CDoc *doc, CRenum *head)
{
	I64		 ch, i;
	CRenum	*tmpr;
	U8		 buf[sizeof(CEdFindText.find_text)];

	ch = EdRAGetU8(doc);
	while (ch >= 0)
	{
		if (ch != '@')
			ch = EdRAGetU8(doc);
		else
		{
			ch = EdRAGetU8(doc);
			if (ch == '@')
			{
				ch = EdRAGetU8(doc);
				StrCopy(buf, "@@");
				i = 2;
				while (ch >= 0 && i < sizeof(CEdFindText.find_text))
				{
					if (Bt(char_bmp_alpha_numeric, ch))
						buf[i++] = ch;
					else
						break;
					ch = EdRAGetU8(doc);
				}
				if (i < sizeof(CEdFindText.find_text))
				{
					buf[i++] = 0;
					while (ch >= 0 && Bt(char_bmp_white_space, ch))
						ch = EdRAGetU8(doc);
					if (ch == ':')
					{
						ch = EdRAGetU8(doc);
						tmpr = MAlloc(sizeof(CRenum));
						StrCopy(tmpr->label,buf);
						QueueInsert(tmpr, head->last);
					}
				}
			}
		}
	}
//This is needed because we moved the
	//cursor and it didn't recalc.
	DocRecalc(doc);
}

U0 EdRenumAsm(CDoc *doc)
{
	Bool	unlock = DocLock(doc);
	I64		num = 0;
	CRenum	head, *tmpr, *tmpr1;
	U8		buf[sizeof(CEdFindText.find_text)], buf2[sizeof(CEdFindText.find_text)];

	QueueInit(&head);
	EdSelFun(doc, TRUE);
	EdRACollect(doc, &head);

	tmpr = head.next;
	while (tmpr != &head)
	{
		tmpr1 = tmpr->next;
		num += ASM_RENUM_SPACING;
		StrPrint(buf, "@#%02d", num);
		EdReplace(doc, tmpr->label, buf, TRUE, TRUE, TRUE);
		Free(tmpr);
		tmpr = tmpr1;
	}

	while (num)
	{
		StrPrint(buf,  "@#%02d", num);
		StrPrint(buf2, "@@%02d", num);
		EdReplace(doc, buf, buf2, TRUE, TRUE, TRUE);
		num -= ASM_RENUM_SPACING;
	}
	EdSelAll(doc, FALSE);
	DocRecalc(doc);
	DocCenter(doc);
	if (unlock)
		DocUnlock(doc);
}

U0 EdCodeTools2(CDoc *doc, I64 tool_action, Bool beep=TRUE)
{
	Bool			 okay, unlock = DocLock(doc), start_of_line = TRUE;
	CDocEntry		*doc_e, *doc_ne;
	I64				 i, start_y, end_y, x, r, goto_line_num;
	U8				*b, *st, *st2, *prj_file;
	CTask			*task = NULL;
	CJob			*tmpc;
	CQueueVectU8	*indent;

	DocRecalc(doc);
	goto_line_num = doc->cur_entry->y + 1;

	DocCaptureUndo(doc, TRUE);
	switch (tool_action)
	{
		case EF_CMP_CHK:
			okay = FALSE;
			if (doc->flags & DOCF_PLAIN_TEXT)
				DocFlagsToggle(doc, DOCF_PLAIN_TEXT);
			DocWrite(doc);
			task = Spawn(&ServerCmdLine, NULL, "Server",, Fs);
			st2 = DirCur;
			st = MStrPrint("Cd(\"%s\");", st2);
			tmpc = TaskExe(task, Fs, st, 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
			Free(st2);
			Free(st);
			WinHorz(Fs->win_left, Fs->win_right,  task);
			WinVert(Fs->win_top,  Fs->win_bottom, task);
			if (JobResScan(tmpc, &r))
			{
				st = DirFile(doc->filename.name,, "PRJ");
				prj_file = FileNameAbs(st);
				Free(st);
				if (FileFind(prj_file))
				{
					st2 = DirFile(prj_file), st = MStrPrint("Cd(\"%s\");", st2);
					Free(st2);
					tmpc = TaskExe(task, Fs, st, 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER | 1 << JOBf_FREE_ON_COMPLETE);
					Free(st);
					st = MStrPrint("\"$$WW,1$$\";Comp(\"%s\",\"SysTmp\",\"SysTmp\");", prj_file);
					tmpc = TaskExe(task, Fs, st, 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
					Free(st);
					if (JobResScan(tmpc, &r))
						if (!r)
						{
							tmpc = TaskExe(task, Fs, "Load(\"SysTmp\",LDF_JUST_LOAD);",
										   1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
							if (JobResScan(tmpc, &r))
								okay = TRUE;
						}
					tmpc = TaskExe(task, Fs, "Del(\"SysTmp.*\");", 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
					JobResScan(tmpc, &r);
				}
				else
				{
					Free(prj_file);
					st = DirFile(doc->filename.name, "Load", "CC");
					prj_file = FileNameAbs(st);
					Free(st);
					if (FileFind(prj_file))
						st = MStrPrint("\"$$WW,1$$\";ExeFile(\"%s\",CCF_JUST_LOAD);", prj_file);
					else
						st = MStrPrint("\"$$WW,1$$\";ExeFile(\"%s\",CCF_JUST_LOAD);", doc->filename.name);
					tmpc=TaskExe(task, Fs, st, 1 << JOBf_WAKE_MASTER | 1 << JOBf_FOCUS_MASTER);
					Free(st);
					if (JobResScan(tmpc, &r) && r)
						okay = TRUE;
				}
				Free(prj_file);
			}
			if (!okay)
			{
				PopUpOk("Has Errors");
				while (LBts(&sys_semas[SEMA_FIX], 0))
					Yield;
				ToFileLine(debug.fix_file_line, &st, &i);
				LBtr(&sys_semas[SEMA_FIX], 0);
				if (!StrCompare(st, doc->filename.name))
					goto_line_num = i;
				Free(st);
			}
			break;

		case EF_REINDENT:
			start_y = doc->cur_entry->y;
			EdReplaceTroubleAll(doc, TRUE, FALSE);
			DocGoToLine(doc, start_y + 1);
			if (EdGoToFun(doc, FALSE, FALSE))
			{
				start_y = doc->cur_entry->y;
				indent = EdRICode(doc);
				DocUnlock(doc);
				if (beep)
				{
					Sound(86);
					Sleep(150);
					Sound;
					Sleep(100);
					Sound(86);
					Sleep(150);
					Sound;
				}
				DocLock(doc);
				EdRemFunLeadingSpace(doc);
				DocGoToLine(doc, start_y + 1);
				doc_e = doc->cur_entry;
				end_y = start_y + indent->total_count;
				while (start_y <= doc_e->y < end_y)
				{
					if (doc_e != doc && doc_e != doc->cur_entry && !(doc_e->de_flags & (DOCEG_DONT_EDIT - DOCEF_SCROLLING_X)))
					{
						if (doc_e->type_u8 == DOCT_NEW_LINE || doc_e->type_u8 == DOCT_SOFT_NEW_LINE)
							start_of_line = TRUE;
						else
						{
							if (start_of_line)
							{
								i = QueueVectU8Get(indent, doc_e->y) * C_INDENT_SPACES;
								x = doc_e->x + 1;
								while (i > 8)
								{
									doc_ne = DocEntryNewBase(doc,
												DOCT_TAB | doc->settings_head.default_text_attr << 8,,
												x, doc_e->y, doc_e->page_line_num);
									MemCopy(&doc_ne->settings, &doc_e->settings, sizeof(CDocSettings));
									QueueInsert(doc_ne, doc_e->last);
									i -= 8;
									x += 8;
								}
								if (i > 0)
								{
									b = MAlloc(i + 1, doc->mem_task);
									MemSet(b, CH_SPACE, i);
									b[i] = 0;
									doc_ne = DocEntryNewBase(doc,
												DOCT_TEXT | doc->settings_head.default_text_attr << 8,,
												x, doc_e->y, doc_e->page_line_num);
									doc_ne->tag = b;
									doc_ne->max_col = 1;
									MemCopy(&doc_ne->settings, &doc_e->settings, sizeof(CDocSettings));
									QueueInsert(doc_ne, doc_e->last);
								}
							}
							start_of_line = FALSE;
						}
					}
					doc_e = doc_e->next;
				}
				QueueVectU8Del(indent);
			}
			start_y = doc->cur_entry->y;
			EdReplaceTroubleAll(doc, FALSE, FALSE);
			DocGoToLine(doc, start_y + 1);
			break;

		case EF_RENUM_ASM:
			if (EdGoToFun(doc, FALSE, TRUE))
			{
				if (EdCurU8(doc) == '{')
				{
					EdCursorRight(doc);
					DocRecalc(doc);
				}
				else if (EdCurU8(doc) == ':')
				{
					EdCursorRight(doc);
					if (EdCurU8(doc) == ':')
						EdCursorRight(doc);
					DocRecalc(doc);
				}
				DocUnlock(doc);
				if (beep)
				{
					Sound(86);
					Sleep(150);
					Sound;
					Sleep(100);
					Sound(86);
					Sleep(150);
					Sound;
				}
				DocLock(doc);
				EdRenumAsm(doc);
			}
			break;
	}

	DocRecalc(doc);
	DocGoToLine(doc, goto_line_num);

	DocUnlock(doc);
	if (!unlock)
		DocLock(doc);
	if (task)
		Kill(task, FALSE);
}

U0 EdPopUpChSC(I64 *_ch, I64 *_sc)
{
	I64 sc;

	"Press A Key\n";
	DocPut->flags |= DOCF_SIZE_MIN;
	do MessageGet(_ch, &sc, 1 << MESSAGE_KEY_DOWN);
	while (sc.u8[0] == SC_SHIFT || sc.u8[0] == SC_CTRL || sc.u8[0] == SC_ALT);
	*_sc = sc;
}

U0 EdChSC(CDoc *doc)
{
	I64 ch, sc;
	U8  buf[STR_LEN];

	StrPrint(buf, "EdPopUpChSC(%d,%d);", &ch, &sc);
	PopUp(buf, Fs);
	if (ch == CH_BACKSPACE)
		DocPrint(doc, "CH_BACKSPACE,0x%X", sc);
	else if (ch == '\n')
		DocPrint(doc, "'\n',0x%X", sc);
	else if (CH_CTRLA <= ch <= CH_CTRLZ)
		DocPrint(doc, "CH_CTRL%C,0x%X", ch + '@', sc);
	else if (ch == '$$')
		DocPrint(doc, "'$$$$',0x%X", sc);
	else if (ch == '\\')
		DocPrint(doc, "'\\\\',0x%X", sc);
	else if (ch == '\'')
		DocPrint(doc, "'\\\'',0x%X", sc);
	else if (ch == CH_ESC)
		DocPrint(doc, "CH_ESC,0x%X", sc);
	else if (ch == CH_SHIFT_ESC)
		DocPrint(doc, "CH_SHIFT_ESC,0x%X", sc);
	else if (ch == CH_SPACE)
		DocPrint(doc,"CH_SPACE,0x%X", sc);
	else if (Bt(char_bmp_displayable, ch))
		DocPrint(doc, "'%c',0x%X", ch, sc);
	else
		DocPrint(doc, "0x%X,0x%X", ch, sc);
}

U0 EdCodeTools(CDoc *doc)
{
	I64 tool_action = PopUpEdFormat;

	switch (tool_action)
	{
		case EF_CMP_CHK:
		case EF_REINDENT:
		case EF_RENUM_ASM:
			EdCodeTools2(doc, tool_action);
			break;

		case EF_CTRL_SLIDER:
			TemplateCtrlSlider(doc);
			break;

		case EF_CH_SC:
			EdChSC(doc);
			break;
	}
}
