word nowCurX,nowCurY;
word screenX,screenY;
word scrollX,scrollY;
word realFileX,realFileY;
//word lineLength;
word realLength;
word possibleX;

:void fastcall RESETXY()
{
	scrollX =
	scrollY =
	possibleX = 0;
	GOTOLINEX(0,0);
}

:word fastcall getRealLength(CX)
{
	$push ds
	SETLINEDSSI(CX);
	STRLEN(SI);
	$pop ds
}

:void fastcall countScroll()
{
	DX = screenY;
	CX = scrollY;
	IF(DX < CX)
	{
		BX = 0;
		CX = DX;
	}
	ELSE IF(DX - CX >= usefullRows)
	{
		BX = usefullRows - 1;
		CX = DX - BX;
	}
	ELSE
	{
		BX = DX - CX;
	}
	nowCurY = BX;
	scrollY = CX;

	DX = screenX;
	CX = scrollX;
	IF(DX < CX)
	{
		BX = 0;
		CX = DX;
	}
	ELSE IF(DX - CX + 1 >= screenCols)
	{
		BX = screenCols - horizontalScroll;
		CX = DX - BX;
	}
	ELSE
	{
		BX = DX - CX;
	}
	nowCurX = BX;
	scrollX = CX;
}

:byte fastcall chineseCheckFromSI(CX)
{
	$push si
	BX = 0;

	LOOPNZ(CX)
	{
		$lodsb
		IF(!$test AL,0FEh) BREAK;
		//IF(AL == 0) || (AL == 1) BREAK;
		IF(BH)
		{
			BH = 0;
		}
		ELSE IF(AL >= 81h)
		{
			BH = 1;
		}
	}

	$pop si
	AL = BH;
}

:byte fastcall chineseCheck(CX,BX)
{
	$push ds
	SETLINEDSSI(CX);
	chineseCheckFromSI(BX);
	$pop ds
}

:void fastcall checkLongLine()
{
	DI++;
	IF(DI < CX)
	{
		showMessage("Line too long! ");
		screenX = 65530;
		realFileX = CX - DI - 5;
	}
}

:void resetCountRealScreen()
{
	CX = screenY = realFileY;
	SETLINEDSSI(CX);
	DI = 0;
	CX = 0;
}

:void countTabSizeRealScreen()
{
	IF(AL == '\t')
	{
		$push cx
		AX = DI;
		DX = 0;
		CX = CSWORD[#tabSize];
		$div cx
		CX -= DX;
		DI += CX;
		$pop cx
		CX++;
	}
	ELSE
	{
		DI++;
		CX++;
	}
}

:void fastcall UNCHANGEX()
//:void fastcall screen2real()
{
	word realX = 0;
	word min = 0ffffh;

	screenX = possibleX;

	resetCountRealScreen();
	BX = CSWORD[#screenX];

	for(;;)
	{
		AX = BX - DI;
		IF(CARRYFLAG) -AX;
		IF(AX < min)
		{
			min = AX;
			realX = CX;
			CSWORD[#screenX] = DI;
		}

		$lodsb
		IF(!$test AL,0FEh) BREAK;
		//IF(AL == 0) || (AL == 1) BREAK;
		countTabSizeRealScreen();
	}

	DS = CS;
//	lineLength = DI;
	realLength = CX;
	IF(screenX > DI) screenX = DI;
	ELSE realFileX = realX;
	#ifdef LongLineLock
	checkLongLine();
	#endif
}

//:void fastcall real2screen()
:void fastcall CHANGEX()
{
	word scrX = 0;

	resetCountRealScreen();
	BX = CSWORD[#realFileX];

	for(;;)
	{
		$lodsb
		IF(!$test AL,0FEh) BREAK;
		//IF(AL == 0) || (AL == 1) BREAK;
		countTabSizeRealScreen();

		IF(BX != 0)
		{
			BX--;
			scrX = DI;
		}
	}

	DS = CS;
//	lineLength = DI;
	realLength = CX;
	screenX = scrX;
	#ifdef LongLineLock
	checkLongLine();
	#endif
	possibleX = screenX;
}


:void fastcall CHANGEX1()
{
	real2screen();
	possibleX = screenX;
}

:void fastcall UNCHANGEX1()
{
	screenX = possibleX;
	screen2real();
}

:void fastcall ADJUSTSCROLL()
{
	DX = realFileY;
	AX = scrollY;

	IF(DX < AX) || (AX + usefullRows <= DX)
	{
		IF(DX << 1 > screenRows)
		{
			DX -= screenRows / 2;
		}
		ELSE DX = 0;

		scrollY = DX;
	}
}

:void fastcall GOTOLINE(AX)
{
	realFileY = AX;
	UNCHANGEX();
}

:void fastcall GOTOX(AX)
{
	realFileX = AX;
	CHANGEX();
}

:void fastcall GOTOLINEX(AX,BX)
{
	realFileX = BX;
	realFileY = AX;
	CHANGEX();
}

:fastcall MOVEUP()
{
//	IF(realFileY > 0) realFileY--;
//	UNCHANGEX();
	AX = realFileY;
	IF(AX > 0) AX--;
	GOTOLINE(AX);
}

:void fastcall MOVEDOWN()
{
//	IF(realFileY + 1 < fileLines) realFileY++;
//	UNCHANGEX();
	AX = realFileY;
	IF(AX + 1 < fileLines) 
	GOTOLINE(AX);
}

:void fastcall MOVELEFT()
{
//	IF(realFileX > 0) GOTOX(realFileX - 1);
	AX = realFileX;
	IF(AX > 0) GOTOX(AX - 1);
	ELSE IF(realFileY > 0)
	{
		MOVEUP();
		MOVELINEEND();
	}

}

:void fastcall MOVERIGHT()
{
	#ifdef VariableChineseMode
	IF(chineseMode)
	{
	#endif
		#ifdef ChineseSupport
		chineseCheck(realFileY,realFileX + 1);
		IF(AL) realFileX++;
		#endif
	#ifdef VariableChineseMode
	}
	#endif
	
//	IF(realFileX < realLength) realFileX++;
	AX = realFileX;
	IF(AX < realLength) GOTOX(AX + 1);
	ELSE IF(realFileY + 1 < fileLines)
	{
		//realFileY++;
		//realFileX = 0;
		MOVEDOWN();
		MOVELINEHOME();
	}
//	CHANGEX();
}

:void fastcall MOVELINEHOME()
{
	GOTOX(0);
}

:void fastcall MOVELINEEND()
{
	GOTOX(realLength);
}

:void fastcall MOVELINESTART()
{
	SETLINEDSSI(realFileY);
	CX = 0;
	for(;;)
	{
		$lodsb
		IF(AL != ' ') && (AL != '\t') BREAK;
		CX++;
	}

	DS = CS;
	GOTOX(CX);
}

:void fastcall MOVEFILEHOME()
{
	GOTOLINEX(0,0);
}

:void fastcall MOVEFILEEND()
{
	GOTOLINE(fileLines - 1);
	MOVELINEEND();
}

:void fastcall SCROLLPAGEUP()
{
//	AX = usefullRows - 2;
//	realFileY -= AX;
//	IF(CARRYFLAG) realFileY = 0;
//	scrollY -= AX;
//	IF(CARRYFLAG) scrollY = 0;
//	UNCHANGEX();
//	AX = realFileY;
	CX = usefullRows - 2;
	AX = realFileY - CX;
	IF(CARRYFLAG) AX = 0;
	scrollY -= CX;
	IF(CARRYFLAG) scrollY = 0;	
	GOTOLINE(AX);
}

:void fastcall SCROLLPAGEDOWN()
{
	#ifdef VariableWindowsSize
	BX = fileLines;
	CX = usefullRows - 2;
	AX = realFileY + CX;
	IF(AX > BX) AX = BX - 1;
	DX = scrollY + CX;
	BX -= CX;
	IF(DX > BX) DX = BX;
	scrollY = DX;
	GOTOLINE(AX);
	#else
	BX = fileLines;
	AX = realFileY + usefullRows - 2;
	IF(AX > BX) AX = BX - 1;
	DX = scrollY + usefullRows - 2;
	BX -= usefullRows - 2;
	IF(DX > BX) DX = BX;
	scrollY = DX;
	GOTOLINE(AX);
	#endif
}

/*
:fastcall SCROLLUP()
{
	IF(scrollY > 0) scrollY--;
	UNCHANGEX();
}

:fastcall SCROLLDOWN()
{
	scrollY++;
	UNCHANGEX();
}
*/

:void fastcall NEWLINE()
{
	getRealPointDSSIByFile();
	getMarkFromSI();
	_INSERTCHARN(SI,AL,1);
	IF(AL) switchMarkFromSI();
	DS = CS;
	fileLines++;
	GOTOLINEX(realFileY + 1,0);
}

:void fastcall AUTOINDENT()
{
	SETLINEDSSI(realFileY - 1);
	BX = SI;
	_GONEXTLINE();
	DX = 0;
	for(;;)
	{
		AL = DSBYTE[BX];
		BX++;
		IF(AL != ' ') && (AL != '\t') BREAK;
		_INSERTCHARN(SI,AL,1);
		DX++;
	}
	DS = CS;
	GOTOX(DX);
}

:void fastcall _INSERTSPACE(SI,CX)
{
	$push cx
	memcpy(SI + CX,SI,CSWORD[#fileSize] - SI + 2);
	$pop cx

	CSWORD[#fileSize] += CX;
	CSBYTE[#isSave] = FALSE;
}

:void fastcall _INSERTMEM(SI,AX,BX,CX)
{
/*	DX = CSWORD[#fileSize];
	$add dx,cx
	IF(CARRYFLAG) || (DX >= 65530) return;
*/
	_INSERTSPACE(SI,CX);
	$push es
	ES = DS;
	DS = AX;
	@COPY(SI,BX,CX);
	$pop es
}

:void fastcall _INSERTCHARN(SI,AL,CX)
{
	_INSERTSPACE(SI,CX);
	LOOPNZ(CX)
	{
		DSBYTE[SI] = AL;
		SI++;
	}
}

:void fastcall INSERTCHAR(AL)
{
	$push ax
	getRealPointDSSIByFile();
	$pop ax
	_INSERTCHARN(SI,AL,1);
	DS = CS;
	realFileX++;
	CHANGEX();
}

:void fastcall MODIFYCHAR(AL)
{
	$push ax
	getRealPointDSSIByFile();
	$pop ax
	DSBYTE[SI] = AL;
	DS = CS;
	isSave = FALSE;
	#ifdef ChineseSupport
	realFileX++;
	CHANGEX();
	#else
	MOVERIGHT();
	#endif
}

:void fastcall _DELETECHARN(SI,CX)
{
	$push cx
	memcpy(SI,SI + CX,CSWORD[#fileSize] - SI + 2);
	$pop cx
	CSWORD[#fileSize] -= CX;
	CSBYTE[#isSave] = FALSE;
}

:void fastcall DELETECHAR()
{
	getRealPointDSSIByFile();
	AX = CSWORD[#fileSize];
	IF(SI == AX) GOTO exitDeleteChar;
	AH = DSBYTE[SI];

	#ifdef ChineseSupport
	#ifdef VariableChineseMode
	IF(CSBYTE[#chineseMode])
	{
	#endif
		$push si
		SI -= BX;
		chineseCheckFromSI(CSWORD[#realFileX] + 1);
		$pop si
		IF(AL) CX = 2;
		ELSE CX = 1;
		_DELETECHARN(SI,CX);
	#ifdef VariableChineseMode
	}
	ELSE _DELETECHARN(SI,1);
	#endif
	#else
	_DELETECHARN(SI,1);
	#endif


	IF(AH & 0FEh == 0)
	{
		CSWORD[#fileLines]--;
		#ifdef MarkerFunction
		IF(AH == 1)
		{
			IF(DSBYTE[DI] & 0FEh == 0) DSBYTE[DI] = 1;
			ELSE
			{
				_GONEXTLINE();
				DSBYTE[SI - 1] = 1;
			}
		}
		#endif
	}
	

exitDeleteChar:
	DS = CS;
	CHANGEX();
}

:void fastcall DELETELINE()
{
	CX = realFileY;
	DX = 1;
	AX = fileLines - 1;
	IF(CX == AX)
	{
		IF(CX == 0)
		{
			DX = 0;
			AX++;
		}
		ELSE realFileY--;
	}
	fileLines = AX;

	SETLINEDSSI(CX);
	_DELETECHARN(SI,STRLEN(SI) + DX);
	DS = CS;
	GOTOX(0);
}

:void fastcall DELETELINE1()
{
	DX = fileLines;
	CX = realFileY;
	SETLINEDSSI(CX);
	CX = STRLEN(SI);
	BX = CSWORD[#fileSize];
	IF(DX > 1) CX++;
	ELSE DSBYTE[BX] = 0;
	$push cx
	DI = SI;
	SI += CX;
	CX = BX - SI + 1;
	memcpy(DI,SI,CX);
	$pop cx
	DS = CS;
	isSave = FALSE;
	fileSize -= CX;
	DX--;
	IF(!ZEROFLAG)
	{
		fileLines--;
		IF(realFileY == DX) realFileY--;
	}
	CHANGEX();
	IF(realFileX > realLength)
	MOVELINEEND();
}

:void fastcall trimTail1(CX,DX)
{
	$push ds
	SETLINEDSSI(CX);
	DI = SI;
	AX = DX - CX;
	_SKIPLINES(AX);
	DX = SI;
	SI = DI;

	WHILE(SI < DX)
	{
		$push dx
		SI += STRLEN(SI);
		DI = SI - 1;
		WHILE(DSBYTE[DI] == ' ') || (DSBYTE[DI] == '\t')
		{
			DI--;
		}
		DI++;
		CX = STRLEN(SI + 1);
		memcpy(DI,SI,CX);
		$pop dx
	}
	CX = CSWORD[#fileSize] - BX + 1;
	memcpy(DI,SI,CX);

	$pop ds
}

:void fastcall trimTail(SI,DX)
{
	$push es
	DS = fileSeg;
	DI = SI;

	$push si
	CX = 0;
	WHILE(SI < DX)
	{
		SI += STRLEN(SI);
		DI = SI - 1;
		WHILE(DSBYTE[DI] == ' ') || (DSBYTE[DI] == '\t')
		{
			$pop ax
			$push ax
			IF(DI < AX) BREAK;
			DSBYTE[DI] = specialFillingChar;
			DI--;
			CSBYTE[#isSave] = FALSE;
			CX++;
		}
		DI++;
		SI++;
	}
	BX = CX;

	$pop si
	DI = SI;
	WHILE(SI < DX)
	{
		$lodsb
		IF(AL != specialFillingChar)
		{
			DSBYTE[DI] = AL;
			DI++;
		}
	}

	CX = CSWORD[#fileSize] - DX + 1;
	ES = DS;
	@COPY();

	$pop es
	DS = CS;
	fileSize -= BX;
	UNCHANGEX();
}

:void fastcall incIndent1(CX)
{
	$push screenX,realFileY;
	realFileY = CX;
	realFileX = 0;
	INSERTCHAR('\t');
	$pop realFileY,possibleX;
	UNCHANGEX();
}

:void fastcall incIndent(CX)
{
	getRealPointDSSI(0,CX);
	_INSERTCHARN(SI,'\t',1);
	DS = CS;
}

:word fastcall decIndent(CX)
{
	SETLINEDSSI(CX);
	AL = DSBYTE[SI];
	IF(AL == '\t')
	{
		_DELETECHARN(SI,1);
		AX = 1;
	}
	ELSE IF(AL == ' ')
	{
		CX = CSWORD[#tabSize];
		BX = 0;
		$push si
		loop(CX)
		{
			$lodsb
			IF(AL != ' ') BREAK;
			BX++;
		}
		$pop si

		IF(CX != 0) && (AL == '\t') AX = BX + 1;
		ELSE AX = BX;
		_DELETECHARN(SI,AX);
	}
	ELSE AX = 0;
	DS = CS;
	$push ax
	UNCHANGEX();
	$pop ax
}

:void fastcall spaceToTab(CX)
{
}

:void fastcall tabToSpace(CX)
{
}

:word BIOSREADKEY()
{
	AH = 0;
	$int 16h
}

:byte BIOSPUSHKEY(CX)
{
	AH = 05h;
	$int 16h
}

:byte BIOSKEYCHECK()
{
	AH = 01h;
	$int 16h
}

:byte fastcall ISSHIFT()
{
	#ifdef MarcoFunction
	IF(isPlayback)
	{
		AL = keyShift[playCount - 1];
		return;
	}
	#endif

	AH = 2;
	$int 16h
	AL &= 11b;
}

:byte fastcall ISSCROLLLOCK()
{
	AH = 2;
	$int 16h
	AL &= 10000b;
}

:byte fastcall CLEANSCROLLLOCK()
{
	$push ds
	DS = 0;
	BX = 417h;
	DSBYTE[BX] &= !10h;
	$pop ds
}

:void fastcall setRepeatRate()
{
	AX = 305h;
	BX = 0;
	$int 16h;
}

:word pcb[7] = -1;
:word saveSP,saveSS;
:byte cmd = "C:\\COMMAND.COM";
:byte childDTA[128] = {0,"/k "};

:void dosShell()
{
	pcb[0] = 0;
	pcb[2] = #childDTA;
	pcb[4] = CS;

	#ifdef MoreDosShellMemory
	IF(saveFile(fileName) == 0) freeMemory(fileSeg);
	freeMemory(clipboardSeg);
	#ifdef Undo
	freeMemory(undoSeg);
	undoLine = -1;
	#endif
	#endif

	#ifdef MouseFunction
	HIDEMOUSE();
	#endif

	CLS();
	GOTOXY(0,0);
	DX = #cmd;
	BX = #pcb;

	ES = CS;
	saveSS = SS;
	saveSP = SP;
	AX = 4B00h;
	$int 21h
	SS = saveSS;
	SP = saveSP;
	DS = CS;

	#ifdef MoreDosShellMemory
	allocSegsForEditor();
	loadFile(fileName);
	isSave = TRUE;
	clipboardSize = 0;
	#endif
}

:word keyRecord[MaxRecordKey];
:byte keyShift[MaxRecordKey];
:word keyRecCount = 0;
:word playCount = -1;
:byte isPlayback = FALSE;

:void fastcall keyRec()
{
	IF(AX == C_R)
	{
		showMessage("Press Ctrl+P to playback !");
		//waitKey = #NOTHING;
		mountNothingOnWaitkey();
		AX = 0;
		return;
	}
	IF(AX == ESC)
	{
		//waitKey = #NOTHING;
		mountNothingOnWaitkey();
		AX = 0;
		keyRecCount = AX;
		return;
	}
	IF(keyRecCount >= MaxRecordKey)
	{
		showMessage("Too many keys, press Ctrl+R to stop recording");
		AX = 0;
		return;
	}
	DX = AX;
	$push ax
	BX = keyRecCount << 1;
	keyRecord[BX] = AX;
	keyShift[BX] = ISSHIFT();
	keyRecCount++;
	showMessage("Recording ...  (Ctrl+R to stop)");
	$pop ax
}

:void fastcall keyPlay()
{
	IF(playCount < keyRecCount)
	{
		BIOSPUSHKEY(keyRecord[playCount]);
		playCount++;
	}
	ELSE
	{
		setKey = #NOTHING;
		isPlayback = FALSE;
	}

}

:word handleKeyForScrollLock()
{
	BX = rows;
	CX = cols;

	SWITCH(AX)
	{
	CASE UP:
		IF(BX > minRows)
		{
			BX--;
			editRows--;
		}
		BREAK;
	CASE DOWN:
		IF(BX < realRows)
		{
			BX++;
			editRows++;
		}
		BREAK;
	CASE LEFT:
		IF(CX > minCols)
		{
			CX--;
			bufferLength--;
		}
		BREAK;
	CASE RIGHT:
		IF(CX < realCols)
		{
			CX++;
			bufferLength++;
		}
		BREAK;
	CASE ESC:
		CLEANSCROLLLOCK();
	}

/*	IF(rows != BX) || (cols != CX) 
	{
		$push bx,cx
		CLS();
		$pop cx,bx
	}*/
	rows = BX;
	cols = CX;
	CLS();
}

:void saveUndo()
{
	$push ds,es
	CX = fileSize;
	undoSaveY = undoLine = realFileY;
	undoSaveX = realFileX;
	undoSaveSize = CX;
	CX++;
	undoSaveLines = fileLines;
	ES = undoSeg;
	DS = fileSeg;
	SI = 0;
	DI = 0;
	@COPY(DI,SI,CX);
	$pop es,ds
}

:void restoreUndo()
{
	$push ds,es
	CX = undoSaveSize;
	fileSize = CX;
	CX++;
	ES = fileSeg;
	DS = undoSeg;
	SI = 0;
	DI = 0;
	@COPY(DI,SI,CX);
	$pop es,ds
	fileLines = undoSaveLines;
	GOTOLINEX(undoSaveY,undoSaveX);
	ADJUSTSCROLL();
	INSELECTION = FALSE;
}


:word handleKeyForView()
{
	IF(AX == ESC)
	{
		mountNothingOnWaitkey();
		exitView();
		#ifdef EnableReadOnly 
		readOnlyMode = FALSE;
		#endif
		AX = 0;
	}
	ELSE IF(AX == Help)
	{
		AX = 0;
	}
}

:byte saveViewInformation[20];
:word saveFileName;
:word saveIsSave;
:word saveFileSeg;
:word saveLines;
:word saveFileSize;

:void fastcall enterView(BX)
{
	$push ds,es
	ES = CS;
	DS = CS;
	
	saveFileSeg = fileSeg;
	fileSeg = BX;
	saveLines = fileLines;

	@COPY(#saveViewInformation,#nowCurX,20);
	saveFileName = fileName;
	saveFileSize = fileSize;
	saveIsSave = isSave;
	INSELECTION = FALSE;
	$pop es,ds
}

:void exitView()
{
	$push ds,es
	ES = CS;
	DS = CS;

	freeMemory(fileSeg);
	fileSeg = saveFileSeg;
	fileLines = saveLines;

	@COPY(#nowCurX,#saveViewInformation,20);
	fileName = saveFileName;
	fileSize = saveFileSize;
	isSave = saveIsSave;
	
	GOTOLINEX(realFileY,realFileX);
	ADJUSTSCROLL();
	INSELECTION = FALSE;
	$pop es,ds
}