#define CaseSensitive 0
#define NonCaseSensitive 20h

// void search(substring,substringSize,startPoint,endPoint);
// DS point to substring
// ES point to target
:word fastcall search(SI,CX,DI,DX,AH)
{
//	IF(CX == 0) RETURN DI;
	for(;;)
	{
		for(;;)
		{
			AL = DSBYTE[SI];
			IF(DI > DX) RETURN -1;
			IF(AL == ESBYTE[DI]) BREAK;
			SWITCH(AL)
			{
			CASE 'A'...'Z':
			CASE 'a'...'z':
				IF(AL ^ AH == ESBYTE[DI]) BREAK 1;
			}
			DI++;
		}
		DI++;
		BX = DI;
		$push cx,si
		SI++;
		CX--;
		LOOPNZ(CX)
		{
			$lodsb
			IF(AL == ESBYTE[BX])
			{
				BX++;
				CONTINUE;
			}
			ELSE SWITCH(AL)
			{
			CASE 'A'...'Z':
			CASE 'a'...'z':
				IF(AL ^ AH == ESBYTE[BX])
				{
					BX++;
					CONTINUE;
				}
			}
			$pop si,cx
			CONTINUE 1;

		}
		$pop si,cx
		RETURN DI - 1;
	}
}

byte findBuffer[128];
:byte replaceBuffer[128];
:word findNext;
:word findSize = 0;
:word replaceSize = 0;
:byte findForReplaceAll = FALSE;
:byte findCaseSensitive = NonCaseSensitive;
:byte warpAtEnd = TRUE;
:enum {OneByOne = 3,AllInFile = 28,AllInSelection = 53};
:byte replaceMethod = OneByOne;

:word fastcall resetFind(SI)
{
	CX = STRLEN(SI);
	findSize = CX;
	@COPY(#findBuffer,SI,CX);
}

:word fastcall doSearch(DI)
{
STARTSEARCH:
	search(#findBuffer,findSize,DI,fileSize,findCaseSensitive);

	#ifdef VariableChineseMode
	IF(chineseMode)
	{
	#endif
		#ifdef ChineseSupport
		IF(AX != -1)
		{
			$push ax,ds
			DS = ES;
			BX = SI = AX;
			WHILE($test DSBYTE[SI],0FEh) && (SI > 0) SI--;
			IF(!$test DSBYTE[SI],0FEh) SI++;
			chineseCheckFromSI(BX - SI);
			BL = AL;
			$pop ds,ax
			DI = AX + 1;
			IF(BL) GOTO STARTSEARCH;
		}
		#endif
	#ifdef VariableChineseMode
	}
	#endif
}

:word fastcall findNextFromNow()
{
	getRealPointByFile();
	findFrom(AX);
}

:word fastcall findNextFrom0()
{
	findFrom(0);
}

:word fastcall findNextContinue()
{
	findFrom(findNext);
}

:word fastcall findFrom(DI)
{
	$push es

	ES = fileSeg;
/*	IF(findSize == 0)
	{
		$pop es
		return -1;
	}
*/
	doSearch(DI);
//	IF(AX == -1) && (warpAtEnd)
//	{
//		doSearch(0,findFrom);
//		$stc;
//	}
//	ELSE $clc ;

//	$pushf
	$push ax

	IF(AX != -1)
	{
		$push ax
		findNext = AX + findSize;
		$pop ax
		IF(!findForReplaceAll)
		{
			setSelection(AX,findSize);
			GOTOLINEX(CX,BX);
			ADJUSTSCROLL();
		}
	}

	$pop ax
//	$popf

	$pop es
}

#ifdef SearchFunction
void fastcall changeCaseSensitive()
inline
{
	findCaseSensitive ^= NonCaseSensitive;
}
void fastcall showCaseSensitive()
{
	VWRITESTRXYA("[ ] Case sensitive (Alt + C)",2,usefullRows - 1,messageLineColor);
	BL = ' ';
	IF(findCaseSensitive == CaseSensitive) BL = 'X';
	VWRITEXYA(BL,3,usefullRows - 1,messageLineColor);
	AL = 0;
}

void fastcall changeWarpAtEnd()
inline
{
	warpAtEnd ^= TRUE;
}
void fastcall showWarpAtEnd()
{
	VWRITESTRXYA("[ ] Wrap at the end of file (Alt + W)",2,usefullRows - 0,messageLineColor);
	BL = ' ';
	IF(warpAtEnd) BL = 'X';
	VWRITEXYA(BL,3,usefullRows,messageLineColor);
	AL = 0;
}

:void fastcall showReplaceMethod(BL)
{
	BH = replaceMethod;
	#ifdef FloatWindow
	IF(cols < 77) 
	{
		BH >>= 1;
	}
	#endif
	VWRITEXYA(BL,BH,usefullRows - 2,messageLineColor);
}

:void fastcall changeReplaceMethod(BH)
{
	$push bx
	showReplaceMethod(' ');
	$pop bx
	replaceMethod = BH;
	showReplaceMethod(09);
	AL = 0;
}

:void fastcall showInputLine(BH,AL)
{
	VWRITEXYNA(0,BH,AL,inputBufferLength,inputLineColor);
}
#endif

:byte getFindString()
{
	IF(INSELECTION)
	{
		getSelection(CS,#inputBuffer,inputBufferLength);
		#ifdef VariableChineseMode
		IF(chineseMode)
		{
		#endif
			#ifdef ChineseSupport
			SI = #inputBuffer;
			chineseCheckFromSI(DI - SI - 2);
			IF(AL) CSBYTE[DI - 1] = 0;
			#endif
		#ifdef VariableChineseMode
		}
		#endif
	}
	ELSE
	{
		@COPY(#inputBuffer,#findBuffer,findSize);
		DSBYTE[DI] = 0;
	}

	IF(getStringDefault(#inputBuffer) & 0FEh != 0)
	{
		resetFind(#inputBuffer);
		AL = 1;
	}
}

:byte fastcall showFindDialog()
{
	CLEANLASTLINEN(messageLineColor,3);

	VWRITESTRXYA(" Find what: ",0,usefullRows - 2,messageLineColor);
	GOTOXY(CX,usefullRows - 2);
	showInputLine(12,usefullRows - 2);
	showCaseSensitive();
	showWarpAtEnd();

	handleOtherKey = #handleKeyForFind;
	getFindString();
	handleOtherKey = #NOTHING;
}

:byte fastcall handleKeyForFind()
{
	$push cx,si
	SWITCH(AX)
	{
	CASE A_C:
		changeCaseSensitive();
		BREAK;
	CASE A_W:
		changeWarpAtEnd();
	}
	$pop si,cx
}

:byte fastcall showReplaceDialog()
{
	CLEANLASTLINEN(messageLineColor,5);
	VWRITESTRXYA(" Find what: ",0,usefullRows - 4,messageLineColor);
	GOTOXY(CX,usefullRows - 4);
	showInputLine(12,usefullRows - 4);
	VWRITESTRXYA("Replace with: ",1,usefullRows - 3,messageLineColor);
	VWRITESTRXYA("{ } One by one (F1)      { } All in file (F2)",2,usefullRows - 2,messageLineColor);

	IF(INSELECTION) AH = messageLineColor;
	ELSE AH = unusableColor;

	#ifdef FloatWindow
	IF(cols > 77)
	{
	#endif
		VWRITESTRA("     { } All in selection (F3)",AH);
	#ifdef FloatWindow
	}
	ELSE
	{
		$push ax
		VWRITESTRXYA(" One by one   All file ",2,usefullRows - 2,messageLineColor);
		$pop ax		
		VWRITESTRA("   All selection (F1 2 3)",AH);		
	}
	#endif

	showCaseSensitive();
	showWarpAtEnd();

	IF(replaceMethod == AllInSelection) && (!INSELECTION)
		replaceMethod = OneByOne;

	changeReplaceMethod(replaceMethod);

	handleOtherKey = #handleKeyForReplace;
	IF(getFindString() == 0) GOTO EXIT;

	GOTOXY(15,usefullRows - 3);
	VWRITEXYNA(0,12,usefullRows - 4,inputBufferLength,messageLineColor);
	showInputLine(15,usefullRows - 3);

	@COPY(#inputBuffer,#replaceBuffer,replaceSize);
	DSBYTE[DI] = 0;
	getStringDefault(#inputBuffer);
	$push ax
	replaceSize = STRLEN(#inputBuffer);
	@COPY(#replaceBuffer,#inputBuffer,AX + 1);
	$pop ax
EXIT:
	handleOtherKey = #NOTHING;
}

:byte fastcall handleKeyForReplace()
{
	$push cx,si
	SWITCH(AX)
	{
	CASE A_C:
		changeCaseSensitive();
		BREAK;
	CASE A_W:
		changeWarpAtEnd();
		BREAK;
	CASE F1:
		changeReplaceMethod(OneByOne);
		BREAK;
	CASE F2:
		changeReplaceMethod(AllInFile);
		BREAK;
	CASE F3:
		IF(INSELECTION) changeReplaceMethod(AllInSelection);
	}
	$pop si,cx
}

:void fastcall showOccurrences()
{
	INT2ASC(AX,#inputBuffer,10);
	memcpy(#inputBuffer + STRLEN(#inputBuffer)," occurrences have been replaced",32);
	showMessage(#inputBuffer);
}

:word fastcall checkWarpAtEnd()
{
	IF(AX == -1) && (warpAtEnd)
	{
		findNextFrom0();
		$stc
	}
	ELSE $clc
}

:word fastcall replaceIt()
{
	CX = findSize;
	BX = replaceSize;
	AX = fileSize - CX + BX;
	IF(CARRYFLAG) || (AX >= 65530)
	{
		$stc
		return -1;
	}

	SI = #replaceBuffer;
	DI = findNext - CX;

	$push es
	ES = fileSeg;

	for(;;)
	{
		IF(CX == 0)
		{
			//SI,AX,BX,CX
			CX = BX;
			findNext += CX;
			DS = fileSeg;
			BX = SI;
			_INSERTMEM(DI,CS,BX,CX);
			BREAK;
		}
		IF(BX == 0)
		{
			findNext -= CX;
			DS = fileSeg;			
			_DELETECHARN(DI,CX);
			BREAK;
		}
		$movsb
		CX--;
		BX--;
	}
	DS = CS;
	$pop es
//	findNext();
	$clc
}

:word fastcall replaceAll()
{
	findForReplaceAll = TRUE;
	IF(warpAtEnd) findNextFrom0();
	ELSE findNextFromNow();
	CX = 0;
	WHILE(AX != -1)
	{
		$push cx
		replaceIt();
		IF(CARRYFLAG)
		{
			$pop cx
			BREAK;
		}
		findNextContinue();
		$pop cx
		CX++;
	}
	findForReplaceAll = FALSE;
	INSELECTION = FALSE;
	$push cx
	UNCHANGEX();
	$pop cx
	return CX;
}

:word fastcall replaceAllInSelection()
{
	getRealPointDSSI(selectionFirstX,selectionFirstY);
	DS = CS;

	findForReplaceAll = TRUE;
	findFrom(SI);
	CX = 0;
	WHILE(AX != -1) && (AX  < selectionSecondPoint)
	{
		$push cx
		replaceIt();
		IF(CARRYFLAG)
		{
			$pop cx
			BREAK;
		}
		findNextContinue();
		$pop cx
		$push ax
		AX = findSize - replaceSize;
		selectionSecondPoint -= AX;
		selectionSize -= AX;
		$pop ax
		CX++;
	}
	findForReplaceAll = FALSE;
	return CX;
}

:word saveScrollKeyHandle;

:word handleKeyForReplaceNext()
{
	IF(AX == ESC) || (AH == 2Eh)
	{
		//waitKey = #NOTHING;
		mountNothingOnWaitkey();
	}
	else IF(AH == 1Eh)
	{
		replaceIt();
		IF(CARRYFLAG)
		{	
			showMessage("Size limit is 65530 bytes!");
		}
		ELSE
		{
			replaceAll();
			AX++;
			showOccurrences();
		}
		//waitKey = #NOTHING;
		mountNothingOnWaitkey();
	}
	ELSE IF(AH == 15h)
	{
		replaceIt();
		IF(CARRYFLAG)
		{
			showMessage("Size limit is 65530 bytes!");
			//waitKey = #NOTHING;
			mountNothingOnWaitkey();
			return;
		}

		findNextContinue();
		
		#ifdef Undo
		$push ax
		undoLine = realFileY;
		$pop ax
		#endif
		checkWarpAtEnd();
		IF(AX == -1)
		{
			INSELECTION = FALSE;
			showMessage("Not found");
			//waitKey = #NOTHING;
			mountNothingOnWaitkey();
		}
		ELSE
		{
			showMessage("Replace this occurence? (Yes/No/All/Cancel)");
		}
	}
	ELSE IF(AH == 31h)
	{
		findNextContinue();
		checkWarpAtEnd();
		IF(AX == -1)
		{
			INSELECTION = FALSE;
			showMessage("Not found");
			//waitKey = #NOTHING;
			mountNothingOnWaitkey();
		}
		ELSE
		{
			showMessage("Replace this occurence? (Yes/No/All/Cancel)");
		}
	}
	ELSE showMessage("Replace this occurence? (Yes/No/All/Cancel)");
	AX = 0;
}