
---------------------------------------------------- Libraries ----------------------------------------------------

-- "/MineOS/Applications/MineCode IDE.app/MineCode IDE.lua" open OS.lua

package.loaded.syntax = nil
-- package.loaded.ECSAPI = nil
-- package.loaded.GUI = nil
-- package.loaded.windows = nil
-- package.loaded.MineOSCore = nil

require("advancedLua")
local computer = require("computer")
local component = require("component")
local fs = require("filesystem")
local buffer = require("doubleBuffering")
local GUI = require("GUI")
local windows = require("windows")
local MineOSCore = require("MineOSCore")
local event = require("event")
local syntax = require("syntax")
local unicode = require("unicode")
local ecs = require("ECSAPI")
local image = require("image")
local keyboard = require("keyboard")
local palette = require("palette")
local term = require("term")

---------------------------------------------------- Constants ----------------------------------------------------

local args = {...}

local about = {
	"MineCode IDE",
	"Copyright © 2014-2017 ECS Inc.",
	" ",
	"Developers:",
	" ",
	"Timofeev Igor, vk.com/id7799889",
	"Trifonov Gleb, vk.com/id88323331",
	" ",
	"Testers:",
	" ",
	"Semyonov Semyon, vk.com/id92656626",
	"Prosin Mihail, vk.com/id75667079",
	"Shestakov Timofey, vk.com/id113499693",
	"Bogushevich Victoria, vk.com/id171497518",
	"Vitvitskaya Yana, vk.com/id183425349",
	"Golovanova Polina, vk.com/id226251826",
}

local config = {
	syntaxColorScheme = syntax.colorScheme,
	scrollSpeed = 8,
	cursorColor = 0x00A8FF,
	cursorSymbol = "┃",
	cursorBlinkDelay = 0.5,
	doubleClickDelay = 0.4,
	screenScale = 1,
	enableAutoBrackets = true,
	highlightLuaSyntax = true,
}

local colors = {
	topToolBar = 0xDDDDDD,
	bottomToolBar = {
		background = 0x3C3C3C,
		buttons = 0x2D2D2D,
		buttonsText = 0xFFFFFF,
	},
	topMenu = {
		backgroundColor = 0xEEEEEE,
		textColor = 0x444444,
		backgroundPressedColor = 0x3366CC,
		textPressedColor = 0xFFFFFF,
	},
	title = {
		default = {
			sides = 0x555555,
			background = 0x3C3C3C,
			text = 0xEEEEEE,
		},
		onError = {
			sides = 0xCC4940,
			background = 0x880000,
			text = 0xEEEEEE,
		},
	},
	leftTreeView = {
		background = 0xCCCCCC,
	},
	highlights = {
		onError = 0xFF4940,
		onBreakpoint = 0x990000,
	}
}

local possibleBrackets = {
	openers = {
		["{"] = "}",
		["["] = "]",
		["("] = ")",
		["\""] = "\"",
		["\'"] = "\'"
	},
	closers = {
		["}"] = "{",
		["]"] = "[",
		[")"] = "(",
		["\""] = "\"",
		["\'"] = "\'"
	}
}

local cursor = {
	position = {
		symbol = 1,
		line = 1
	},
	blinkState = false
}

local resourcesPath = MineOSCore.getCurrentApplicationResourcesDirectory() 
local configPath = resourcesPath .. "Config.cfg"
local localization = MineOSCore.getLocalization(resourcesPath .. "Localization/")
local findStartFrom
local clipboard
local lastErrorLine
local breakpointLine = 5
local lastClickUptime = computer.uptime()
local mainWindow = {}

---------------------------------------------------- Functions ----------------------------------------------------

local function saveConfig()
	table.toFile(configPath, config)
end

local function loadConfig()
	if fs.exists(configPath) then
		config = table.fromFile(configPath)
		syntax.colorScheme = config.syntaxColorScheme
	else
		saveConfig()
	end
end

local function calculateSizes()
	mainWindow.width, mainWindow.height = buffer.screen.width, buffer.screen.height
	mainWindow.leftTreeView.width = math.floor(mainWindow.width * 0.16)

	if mainWindow.leftTreeView.isHidden then
		mainWindow.codeView.localPosition.x, mainWindow.codeView.width = 1, mainWindow.width
		mainWindow.bottomToolBar.localPosition.x, mainWindow.bottomToolBar.width = mainWindow.codeView.localPosition.x, mainWindow.codeView.width
	else
		mainWindow.codeView.localPosition.x, mainWindow.codeView.width = mainWindow.leftTreeView.width + 1, mainWindow.width - mainWindow.leftTreeView.width
		mainWindow.bottomToolBar.localPosition.x, mainWindow.bottomToolBar.width = mainWindow.codeView.localPosition.x, mainWindow.codeView.width
	end

	if mainWindow.topToolBar.isHidden then
		mainWindow.leftTreeView.localPosition.y, mainWindow.leftTreeView.height = 2, mainWindow.height - 1
		mainWindow.codeView.localPosition.y, mainWindow.codeView.height = 2, mainWindow.height - 1
		mainWindow.errorMessage.localPosition.y = 2
	else
		mainWindow.leftTreeView.localPosition.y, mainWindow.leftTreeView.height = 5, mainWindow.height - 4
		mainWindow.codeView.localPosition.y, mainWindow.codeView.height = 5, mainWindow.height - 4
		mainWindow.errorMessage.localPosition.y = 5
	end

	if mainWindow.bottomToolBar.isHidden then

	else
		mainWindow.codeView.height = mainWindow.codeView.height - 3
	end

	mainWindow.settingsContainer.width, mainWindow.settingsContainer.height = mainWindow.width, mainWindow.height
	mainWindow.settingsContainer.backgroundPanel.width, mainWindow.settingsContainer.backgroundPanel.height = mainWindow.settingsContainer.width, mainWindow.settingsContainer.height

	mainWindow.bottomToolBar.localPosition.y = mainWindow.height - 2
	mainWindow.bottomToolBar.findButton.localPosition.x = mainWindow.bottomToolBar.width - mainWindow.bottomToolBar.findButton.width + 1
	mainWindow.bottomToolBar.inputTextBox.width = mainWindow.bottomToolBar.width - mainWindow.bottomToolBar.inputTextBox.localPosition.x - mainWindow.bottomToolBar.findButton.width + 1

	mainWindow.topToolBar.width, mainWindow.topToolBar.backgroundPanel.width = mainWindow.width, mainWindow.width
	mainWindow.titleTextBox.width = math.floor(mainWindow.topToolBar.width * 0.32)
	mainWindow.titleTextBox.localPosition.x = math.floor(mainWindow.topToolBar.width / 2 - mainWindow.titleTextBox.width / 2)
	mainWindow.runButton.localPosition.x = mainWindow.titleTextBox.localPosition.x - mainWindow.runButton.width - 2
	mainWindow.toggleSyntaxHighlightingButton.localPosition.x = mainWindow.runButton.localPosition.x - mainWindow.toggleSyntaxHighlightingButton.width - 2
	mainWindow.addBreakpointButton.localPosition.x = mainWindow.toggleSyntaxHighlightingButton.localPosition.x - mainWindow.addBreakpointButton.width - 2
	mainWindow.toggleLeftToolBarButton.localPosition.x = mainWindow.titleTextBox.localPosition.x + mainWindow.titleTextBox.width + 2
	mainWindow.toggleBottomToolBarButton.localPosition.x = mainWindow.toggleLeftToolBarButton.localPosition.x + mainWindow.toggleLeftToolBarButton.width + 2
	mainWindow.toggleTopToolBarButton.localPosition.x = mainWindow.toggleBottomToolBarButton.localPosition.x + mainWindow.toggleBottomToolBarButton.width + 2

	mainWindow.RAMUsageProgressBar.localPosition.x = mainWindow.toggleTopToolBarButton.localPosition.x + mainWindow.toggleTopToolBarButton.width + 3
	mainWindow.RAMUsageProgressBar.width = mainWindow.topToolBar.width - mainWindow.RAMUsageProgressBar.localPosition.x - 3

	mainWindow.errorMessage.localPosition.x, mainWindow.errorMessage.width = mainWindow.titleTextBox.localPosition.x, mainWindow.titleTextBox.width
	mainWindow.errorMessage.backgroundPanel.width, mainWindow.errorMessage.errorTextBox.width = mainWindow.errorMessage.width, mainWindow.errorMessage.width - 4

	mainWindow.topMenu.width = mainWindow.width
end

local function changeScale(newScale)
	buffer.changeResolution(ecs.getScaledResolution(newScale))
	calculateSizes()
	mainWindow:draw()
	buffer.draw()
	config.screenScale = newScale
end

local function scalePlus()
	if config.screenScale > 0.3 then changeScale(config.screenScale - 0.1); saveConfig() end
end

local function scaleMinus()
	if config.screenScale < 1 then changeScale(config.screenScale + 0.1); saveConfig() end
end

local function updateTitle()
	if not mainWindow.topToolBar.isHidden then
		if mainWindow.errorMessage.isHidden then
			mainWindow.titleTextBox.lines[1] = string.limit(localization.file .. ": " .. (mainWindow.leftTreeView.currentFile or localization.none), mainWindow.titleTextBox.width - 4)
			mainWindow.titleTextBox.lines[2] = string.limit(localization.cursor .. cursor.position.line .. localization.line .. cursor.position.symbol .. localization.symbol, mainWindow.titleTextBox.width - 4)
			if mainWindow.codeView.selections[1] then
				local countOfSelectedLines = mainWindow.codeView.selections[1].to.line - mainWindow.codeView.selections[1].from.line + 1
				local countOfSelectedSymbols
				if mainWindow.codeView.selections[1].from.line == mainWindow.codeView.selections[1].to.line then
					countOfSelectedSymbols = unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol))
				else
					countOfSelectedSymbols = unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1))
					for line = mainWindow.codeView.selections[1].from.line + 1, mainWindow.codeView.selections[1].to.line - 1 do
						countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(mainWindow.codeView.lines[line])
					end
					countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol))
				end
				mainWindow.titleTextBox.lines[3] = string.limit(localization.selection .. countOfSelectedLines .. localization.lines .. countOfSelectedSymbols .. localization.symbols, mainWindow.titleTextBox.width - 4)
			else
				mainWindow.titleTextBox.lines[3] = string.limit(localization.selection .. localization.none, mainWindow.titleTextBox.width - 4)
			end
		else
			mainWindow.titleTextBox.lines[1], mainWindow.titleTextBox.lines[3] = " ", " "
			if breakpointLine then
				mainWindow.titleTextBox.lines[2] = localization.debugging .. cursor.position.line
			else
				mainWindow.titleTextBox.lines[2] = localization.runtimeError
			end
		end
	end
end

local function calculateErrorMessageSizeAndBeep()
	mainWindow.errorMessage.height = 2 + #mainWindow.errorMessage.errorTextBox.lines
	mainWindow.errorMessage.backgroundPanel.height = mainWindow.errorMessage.height
	mainWindow.errorMessage.errorTextBox.height = mainWindow.errorMessage.height - 2

	updateTitle()
	mainWindow:draw()
	buffer.draw()

	for i = 1, 3 do component.computer.beep(1500, 0.08) end
end

local function showBreakpointMessage(variables)
	mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
	mainWindow.errorMessage.isHidden = false

	mainWindow.errorMessage.errorTextBox:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
	mainWindow.errorMessage.errorTextBox.lines = {}

	for variable, value in pairs(variables) do
		table.insert(mainWindow.errorMessage.errorTextBox.lines, variable .. " = " .. value)
	end

	if #mainWindow.errorMessage.errorTextBox.lines > 0 then
		table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, " ")
		table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, {text = localization.variables, color = 0x0})
	else
		table.insert(mainWindow.errorMessage.errorTextBox.lines, 1, {text = localization.variablesNotAvailable, color = 0x0})
	end

	calculateErrorMessageSizeAndBeep()
end

local function showErrorMessage(text)
	mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.onError.background, colors.title.onError.text
	mainWindow.errorMessage.isHidden = false

	mainWindow.errorMessage.errorTextBox:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top)
	mainWindow.errorMessage.errorTextBox.lines = string.wrap({text}, mainWindow.errorMessage.errorTextBox.width)	
	
	calculateErrorMessageSizeAndBeep()
end

local function hideErrorMessage()
	mainWindow.titleTextBox.colors.background, mainWindow.titleTextBox.colors.text = colors.title.default.background, colors.title.default.text
	mainWindow.errorMessage.isHidden = true
end

local function hideSettingsContainer()
	for childIndex = 2, #mainWindow.settingsContainer.children do mainWindow.settingsContainer.children[childIndex] = nil end
	mainWindow.settingsContainer.isHidden = true
	mainWindow:draw()
	buffer.draw()
end

local function clearHighlights()
	if lastErrorLine then
		mainWindow.codeView.highlights[lastErrorLine] = nil
		lastErrorLine = nil
	end

	if breakpointLine then
		mainWindow.codeView.highlights[breakpointLine] = nil
		breakpointLine = nil
	end
end

local function clearSelection()
	mainWindow.codeView.selections[1] = nil
end

local function fixFromLineByCursorPosition()
	if mainWindow.codeView.fromLine > cursor.position.line then
		mainWindow.codeView.fromLine = cursor.position.line
	elseif mainWindow.codeView.fromLine + mainWindow.codeView.height - 2 < cursor.position.line then
		mainWindow.codeView.fromLine = cursor.position.line - mainWindow.codeView.height + 2
	end
end

local function fixFromSymbolByCursorPosition()
	if mainWindow.codeView.fromSymbol > cursor.position.symbol then
		mainWindow.codeView.fromSymbol = cursor.position.symbol
	elseif mainWindow.codeView.fromSymbol + mainWindow.codeView.codeAreaWidth - 3 < cursor.position.symbol then
		mainWindow.codeView.fromSymbol = cursor.position.symbol - mainWindow.codeView.codeAreaWidth + 3
	end
end

local function fixCursorPosition(symbol, line)
	if line < 1 then
		line = 1
	elseif line > #mainWindow.codeView.lines then
		line = #mainWindow.codeView.lines
	end

	local lineLength = unicode.len(mainWindow.codeView.lines[line])
	if symbol < 1 or lineLength == 0 then
		symbol = 1
	elseif symbol > lineLength then
		symbol = lineLength + 1
	end

	return symbol, line
end

local function setCursorPosition(symbol, line)
	cursor.position.symbol, cursor.position.line = fixCursorPosition(symbol, line)
	fixFromLineByCursorPosition()
	fixFromSymbolByCursorPosition()
	clearHighlights()
	hideErrorMessage()
end

local function setCursorPositionAndClearSelection(symbol, line)
	setCursorPosition(symbol, line)
	clearSelection()
end

local function convertScreenCoordinatesToCursorPosition(x, y)
	return x - mainWindow.codeView.codeAreaPosition + mainWindow.codeView.fromSymbol - 1, y - mainWindow.codeView.y + mainWindow.codeView.fromLine
end

local function isClickedOnCodeArea(x, y)
	return
		x >= mainWindow.codeView.codeAreaPosition and
		y >= mainWindow.codeView.y and
		x < mainWindow.width and
		y < mainWindow.codeView.y + mainWindow.codeView.height - 1
end

local function moveCursor(symbolOffset, lineOffset)
	if mainWindow.codeView.selections[1] then
		if symbolOffset < 0 or lineOffset < 0 then
			setCursorPositionAndClearSelection(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].from.line)
		else
			setCursorPositionAndClearSelection(mainWindow.codeView.selections[1].to.symbol, mainWindow.codeView.selections[1].to.line)
		end
	else
		local newSymbol, newLine = cursor.position.symbol + symbolOffset, cursor.position.line + lineOffset
		
		if symbolOffset < 0 and newSymbol < 1 then
			newLine, newSymbol = newLine - 1, math.huge
		elseif symbolOffset > 0 and newSymbol > unicode.len(mainWindow.codeView.lines[newLine] or "") + 1 then
			newLine, newSymbol = newLine + 1, 1
		end

		setCursorPositionAndClearSelection(newSymbol, newLine)
	end
end

local function setCursorPositionToHome()
	setCursorPositionAndClearSelection(1, 1)
end

local function setCursorPositionToEnd()
	setCursorPositionAndClearSelection(unicode.len(mainWindow.codeView.lines[#mainWindow.codeView.lines]) + 1, #mainWindow.codeView.lines)
end

local function scroll(direction, speed)
	if direction == 1 then
		if mainWindow.codeView.fromLine > speed then
			mainWindow.codeView.fromLine = mainWindow.codeView.fromLine - speed
		else
			mainWindow.codeView.fromLine = 1
		end
	else
		if mainWindow.codeView.fromLine < #mainWindow.codeView.lines - speed then
			mainWindow.codeView.fromLine = mainWindow.codeView.fromLine + speed
		else
			mainWindow.codeView.fromLine = #mainWindow.codeView.lines
		end
	end
end

local function pageUp()
	scroll(1, mainWindow.codeView.height - 2)
end

local function pageDown()
	scroll(-1, mainWindow.codeView.height - 2)
end

local function gotoLine(line)
	mainWindow.codeView.fromLine = math.floor(line - mainWindow.codeView.height / 2) + 1
	if mainWindow.codeView.fromLine < 1 then
		mainWindow.codeView.fromLine = 1
	elseif mainWindow.codeView.fromLine > #mainWindow.codeView.lines then
		mainWindow.codeView.fromLine = #mainWindow.codeView.lines
	end
end

local function selectWord()
	local shittySymbolsRegexp, from, to = "[%s%c%p]"

	for i = cursor.position.symbol, 1, -1 do
		if unicode.sub(mainWindow.codeView.lines[cursor.position.line], i, i):match(shittySymbolsRegexp) then break end
		from = i
	end

	for i = cursor.position.symbol, unicode.len(mainWindow.codeView.lines[cursor.position.line]) do
		if unicode.sub(mainWindow.codeView.lines[cursor.position.line], i, i):match(shittySymbolsRegexp) then break end
		to = i
	end

	if from and to then
		mainWindow.codeView.selections[1] = {
			from = {symbol = from, line = cursor.position.line},
			to = {symbol = to, line = cursor.position.line},
		}
		cursor.position.symbol = to
	end
end

------------------------------------------------------------------------------------------------------------------

local function removeTabs(text)
	local result = text:gsub("\t", string.rep(" ", mainWindow.codeView.indentationWidth))
	return result
end

local function removeWindowsLineEndings(text)
	local result = text:gsub("\r\n", "\n")
	return result
end

local function createInputTextBoxForSettingsWindow(title, placeholder)
	mainWindow.settingsContainer.isHidden = false
	local elementWidth = math.floor(mainWindow.width * 0.3)
	local x, y = math.floor(mainWindow.width / 2 - elementWidth / 2), math.floor(mainWindow.height / 2) - 3
	mainWindow.settingsContainer:addLabel(1, y, mainWindow.settingsContainer.width, 1, 0xFFFFFF, title):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
	return mainWindow.settingsContainer:addInputTextBox(x, y, elementWidth, 3, 0xCCCCCC, 0x777777, 0xCCCCCC, 0x2D2D2D, "", placeholder)
end

local function newFile()
	mainWindow.codeView.lines = {""}
	mainWindow.codeView.maximumLineLength = 1
	mainWindow.leftTreeView.currentFile = nil
	setCursorPositionAndClearSelection(1, 1)
end

local function loadFile(path)
	newFile()
	local file = io.open(path, "r")
	for line in file:lines() do
		line = removeWindowsLineEndings(removeTabs(line))
		table.insert(mainWindow.codeView.lines, line)
		mainWindow.codeView.maximumLineLength = math.max(mainWindow.codeView.maximumLineLength, unicode.len(line))
	end
	file:close()
	mainWindow.leftTreeView.currentFile = path
end

local function saveFile(path)
	fs.makeDirectory(fs.path(path))
	local file = io.open(path, "w")
	for line = 1, #mainWindow.codeView.lines do
		file:write(mainWindow.codeView.lines[line], "\n")
	end
	file:close()
end

local function open()
	local inputTextBox = createInputTextBoxForSettingsWindow(localization.openFile, localization.pathToFile)
	inputTextBox.validator = function(text)
		if fs.exists(text) then return true end
	end
	inputTextBox.onInputFinished = function()
		loadFile(inputTextBox.text)
		hideSettingsContainer()
	end
end

local function saveAs()
	local inputTextBox = createInputTextBoxForSettingsWindow(localization.saveAs, localization.pathToFile)
	inputTextBox.onInputFinished = function()
		saveFile(inputTextBox.text)
		mainWindow.leftTreeView.currentFile = inputTextBox.text
		mainWindow.leftTreeView:updateFileList()
		hideSettingsContainer()
	end
end

local function save()
	saveFile(mainWindow.leftTreeView.currentFile)
end

local function splitStringIntoLines(s)
	s = removeWindowsLineEndings(removeTabs(s))

	local lines, searchLineEndingFrom, maximumLineLength, lineEndingFoundAt, line = {}, 1, 0
	repeat
		lineEndingFoundAt = string.unicodeFind(s, "\n", searchLineEndingFrom)
		if lineEndingFoundAt then
			line = unicode.sub(s, searchLineEndingFrom, lineEndingFoundAt - 1)
			searchLineEndingFrom = lineEndingFoundAt + 1
		else
			line = unicode.sub(s, searchLineEndingFrom, -1)
		end

		table.insert(lines, line)
		maximumLineLength = math.max(maximumLineLength, unicode.len(line))
	until not lineEndingFoundAt

	return lines, maximumLineLength
end

local function downloadFromWeb()
	local inputTextBox = createInputTextBoxForSettingsWindow(localization.getFromWeb, localization.url)
	inputTextBox.onInputFinished = function()
		local success, reason = ecs.internetRequest(inputTextBox.text)
		if success then
			newFile()
			mainWindow.codeView.lines, mainWindow.codeView.maximumLineLength = splitStringIntoLines(reason)
			hideSettingsContainer()
		else
			GUI.error(reason, {title = {color = 0xFFDB40, text = "Failed to connect to URL"}})
		end
	end
end

------------------------------------------------------------------------------------------------------------------

local function addErrorLine(line)
	lastErrorLine = line
	mainWindow.codeView.highlights[line] = colors.highlights.onError
end

local function addBreakpoint()
	clearHighlights()
	breakpointLine = cursor.position.line
	mainWindow.codeView.highlights[breakpointLine] = colors.highlights.onBreakpoint
end

local function getVariables(codePart)
	local variables = {}
	-- Сначала мы проверяем участок кода на наличие комментариев
	if
		not codePart:match("^%-%-") and
		not codePart:match("^[\t%s]+%-%-")
	then
		-- Затем заменяем все строковые куски в участке кода на "ничего", чтобы наш "прекрасный" парсер не искал переменных в строках
		codePart = codePart:gsub("\"[^\"]+\"", "")
		-- Потом разбиваем код на отдельные буквенно-цифровые слова, не забыв точечку с двоеточием
		for word in codePart:gmatch("[%a%d%.%:%_]+") do
			-- Далее проверяем, не совпадает ли это слово с одним из луа-шаблонов, то бишь, не является ли оно частью синтаксиса
			if
				word ~= "local" and
				word ~= "return" and
				word ~= "while" and
				word ~= "repeat" and
				word ~= "until" and
				word ~= "for" and
				word ~= "in" and
				word ~= "do" and
				word ~= "if" and
				word ~= "then" and
				word ~= "else" and
				word ~= "elseif" and
				word ~= "end" and
				word ~= "function" and
				word ~= "true" and
				word ~= "false" and
				word ~= "nil" and
				word ~= "not" and
				word ~= "and" and
				word ~= "or"  and
				-- Также проверяем, не число ли это в чистом виде
				not word:match("^%d+$") and
				not word:match("^0x%x+$") and
				-- Или символ конкатенации, например
				not word:match("^%.+$")
			then
				variables[word] = true
			end
		end
	end

	return variables
end

local function createBreakpointError(variables)
	local errorMessage = "error({variables={"

	for variable in pairs(variables) do
		errorMessage = errorMessage .. "[\"" .. variable .. "\"] = type(" .. variable .. ") == \"string\" and \"\\\"\" .. " .. variable .. " .. \"\\\"\" or tostring(" .. variable .. "),"
	end

	return errorMessage .. "}})"
end

local function run()
	if breakpointLine then
		table.insert(mainWindow.codeView.lines, breakpointLine, createBreakpointError(getVariables(mainWindow.codeView.lines[breakpointLine])))
	end

	local loadSuccess, loadReason = load(table.concat(mainWindow.codeView.lines, "\n"))
	if loadSuccess then
		local oldResolutionX, oldResolutionY = component.gpu.getResolution()
		component.gpu.setBackground(0x1B1B1B)
		component.gpu.setForeground(0xFFFFFF)
		component.gpu.fill(1, 1, oldResolutionX, oldResolutionY, " ")
		term.setCursor(1, 1)
		
		local xpcallSuccess, xpcallReason = xpcall(loadSuccess, debug.traceback)
		local xpcallReasonType = type(xpcallReason)

		if breakpointLine then
			table.remove(mainWindow.codeView.lines, breakpointLine)
		end

		if xpcallSuccess or (xpcallReasonType == "table" and xpcallReason.terminated == true) then
			MineOSCore.waitForPressingAnyKey()
		end

		buffer.changeResolution(oldResolutionX, oldResolutionY)	

		if not xpcallSuccess then
			if xpcallReasonType == "table" then
				if xpcallReason.variables then
					gotoLine(breakpointLine)
					showBreakpointMessage(xpcallReason.variables)
				end
			else
				showErrorMessage(xpcallReason)
			end
		end

		mainWindow:draw()
		buffer:draw()		
	else
		addErrorLine(tonumber(loadReason:match("^%[.+%]%:(%d+)%:")))
		gotoLine(lastErrorLine)
		showErrorMessage(loadReason)
	end
end

local function deleteLine(line)
	if #mainWindow.codeView.lines > 1 then
		table.remove(mainWindow.codeView.lines, line)
		setCursorPositionAndClearSelection(1, cursor.position.line)
	end
end

local function deleteSpecifiedData(fromSymbol, fromLine, toSymbol, toLine)
	local upperLine = unicode.sub(mainWindow.codeView.lines[fromLine], 1, fromSymbol - 1)
	local lowerLine = unicode.sub(mainWindow.codeView.lines[toLine], toSymbol + 1, -1)
	for line = fromLine + 1, toLine do
		table.remove(mainWindow.codeView.lines, fromLine + 1)
	end
	mainWindow.codeView.lines[fromLine] = upperLine .. lowerLine
	setCursorPositionAndClearSelection(fromSymbol, fromLine)
end

local function deleteSelectedData()
	if mainWindow.codeView.selections[1] then
		deleteSpecifiedData(
			mainWindow.codeView.selections[1].from.symbol,
			mainWindow.codeView.selections[1].from.line,
			mainWindow.codeView.selections[1].to.symbol,
			mainWindow.codeView.selections[1].to.line
		)
		clearSelection()
	end
end

local function copy()
	if mainWindow.codeView.selections[1] then
		if mainWindow.codeView.selections[1].to.line == mainWindow.codeView.selections[1].from.line then
			clipboard = { unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol) }
		else
			clipboard = { unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1) }
			for line = mainWindow.codeView.selections[1].from.line + 1, mainWindow.codeView.selections[1].to.line - 1 do
				table.insert(clipboard, mainWindow.codeView.lines[line])
			end
			table.insert(clipboard, unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol))
		end
	end
end

local function cut()
	if mainWindow.codeView.selections[1] then
		copy()
		deleteSelectedData()
	end
end

local function paste(pasteLines)
	if pasteLines then
		if mainWindow.codeView.selections[1] then
			deleteSelectedData()
		end

		local firstPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
		local secondPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, -1)

		if #pasteLines == 1 then
			mainWindow.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1] .. secondPart
			setCursorPositionAndClearSelection(cursor.position.symbol + unicode.len(pasteLines[1]), cursor.position.line)
		else
			mainWindow.codeView.lines[cursor.position.line] = firstPart .. pasteLines[1]
			for pasteLine = #pasteLines - 1, 2, -1 do
				table.insert(mainWindow.codeView.lines, cursor.position.line + 1, pasteLines[pasteLine])
			end
			table.insert(mainWindow.codeView.lines, cursor.position.line + #pasteLines - 1, pasteLines[#pasteLines] .. secondPart)
			setCursorPositionAndClearSelection(unicode.len(pasteLines[#pasteLines]) + 1, cursor.position.line + #pasteLines - 1)
		end
	end
end

local function pasteRegularChar(unicodeByte, char)
	if not keyboard.isControl(unicodeByte) then
		deleteSelectedData()
		paste({char})
	end
end

local function pasteAutoBrackets(unicodeByte)
	local char = unicode.char(unicodeByte)
	local currentSymbol = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, cursor.position.symbol)

	-- Если у нас вообще врублен режим автоскобок, то чекаем их
	if config.enableAutoBrackets then
		-- Ситуация, когда курсор находится на закрывающей скобке, и нехуй ее еще раз вставлять
		if possibleBrackets.closers[char] and currentSymbol == char then
			deleteSelectedData()
			setCursorPosition(cursor.position.symbol + 1, cursor.position.line)
		-- Если нажата открывающая скобка
		elseif possibleBrackets.openers[char] then
			-- А вот тут мы берем в скобочки уже выделенный текст
			if mainWindow.codeView.selections[1] then
				local firstPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], 1, mainWindow.codeView.selections[1].from.symbol - 1)
				local secondPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line], mainWindow.codeView.selections[1].from.symbol, -1)
				mainWindow.codeView.lines[mainWindow.codeView.selections[1].from.line] = firstPart .. char .. secondPart
				mainWindow.codeView.selections[1].from.symbol = mainWindow.codeView.selections[1].from.symbol + 1

				if mainWindow.codeView.selections[1].to.line == mainWindow.codeView.selections[1].from.line then
					mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].to.symbol + 1
				end

				firstPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], 1, mainWindow.codeView.selections[1].to.symbol)
				secondPart = unicode.sub(mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line], mainWindow.codeView.selections[1].to.symbol + 1, -1)
				mainWindow.codeView.lines[mainWindow.codeView.selections[1].to.line] = firstPart .. possibleBrackets.openers[char] .. secondPart
			-- А тут мы делаем двойную автоскобку, если можем
			elseif possibleBrackets.openers[char] and not currentSymbol:match("[%a%d%_]") then
				paste({char .. possibleBrackets.openers[char]})
				setCursorPosition(cursor.position.symbol - 1, cursor.position.line)
				cursor.blinkState = false
			-- Ну, и если нет ни выделений, ни можем ебануть автоскобочку по регулярке
			else
				pasteRegularChar(unicodeByte, char)
			end
		-- Если мы вообще на скобку не нажимали
		else
			pasteRegularChar(unicodeByte, char)
		end
	-- Если оффнуты афтоскобки
	else
		pasteRegularChar(unicodeByte, char)
	end
end

local function backspaceAutoBrackets()	
	local previousSymbol = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol - 1, cursor.position.symbol - 1)
	local currentSymbol = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, cursor.position.symbol)
	if config.enableAutoBrackets and possibleBrackets.openers[previousSymbol] and possibleBrackets.openers[previousSymbol] == currentSymbol then
		deleteSpecifiedData(cursor.position.symbol, cursor.position.line, cursor.position.symbol, cursor.position.line)
	end
end

local function delete()
	if mainWindow.codeView.selections[1] then
		deleteSelectedData()
	else
		if cursor.position.symbol < unicode.len(mainWindow.codeView.lines[cursor.position.line]) + 1 then
			deleteSpecifiedData(cursor.position.symbol, cursor.position.line, cursor.position.symbol, cursor.position.line)
		else
			if cursor.position.line > 1 then
				deleteSpecifiedData(unicode.len(mainWindow.codeView.lines[cursor.position.line]) + 1, cursor.position.line, 0, cursor.position.line + 1)
			end
		end
	end
end

local function backspace()
	if mainWindow.codeView.selections[1] then
		deleteSelectedData()
	else
		if cursor.position.symbol > 1 then
			backspaceAutoBrackets()
			deleteSpecifiedData(cursor.position.symbol - 1, cursor.position.line, cursor.position.symbol - 1, cursor.position.line)
		else
			if cursor.position.line > 1 then
				deleteSpecifiedData(unicode.len(mainWindow.codeView.lines[cursor.position.line - 1]) + 1, cursor.position.line - 1, 0, cursor.position.line)
			end
		end
	end
end

local function enter()
	local firstPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], 1, cursor.position.symbol - 1)
	local secondPart = unicode.sub(mainWindow.codeView.lines[cursor.position.line], cursor.position.symbol, -1)
	mainWindow.codeView.lines[cursor.position.line] = firstPart
	table.insert(mainWindow.codeView.lines, cursor.position.line + 1, secondPart)
	setCursorPositionAndClearSelection(1, cursor.position.line + 1)
end

local function selectAll()
	mainWindow.codeView.selections[1] = {
		from = {
			symbol = 1, line = 1
		},
		to = {
			symbol = unicode.len(mainWindow.codeView.lines[#mainWindow.codeView.lines]), line = #mainWindow.codeView.lines
		}
	}
end

local function isLineCommented(line)
	return mainWindow.codeView.lines[line]:match("%-%-[^%-]")
end

local function commentLine(line)
	mainWindow.codeView.lines[line] = "-- " .. mainWindow.codeView.lines[line]
end

local function uncommentLine(line)
	mainWindow.codeView.lines[line], countOfReplaces = mainWindow.codeView.lines[line]:gsub("%-%-%s", "", 1)
	return countOfReplaces
end

local function toggleComment()
	if mainWindow.codeView.selections[1] then
		local allLinesAreCommented = true
		
		for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
			if not isLineCommented(line) then
				allLinesAreCommented = false
			end
		end
		
		for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
			if allLinesAreCommented then
				uncommentLine(line)
			else
				commentLine(line)
			end
		end

		local modifyer = 3
		if allLinesAreCommented then
			modifyer = -3
		end
		setCursorPosition(cursor.position.symbol + modifyer, cursor.position.line)
		mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].from.symbol + modifyer, mainWindow.codeView.selections[1].to.symbol + modifyer
	else
		if isLineCommented(cursor.position.line) then
			if uncommentLine(cursor.position.line) > 0 then
				setCursorPositionAndClearSelection(cursor.position.symbol - 3, cursor.position.line)
			end
		else
			commentLine(cursor.position.line)
			setCursorPositionAndClearSelection(cursor.position.symbol + 3, cursor.position.line)
		end
	end
end

local function indentLine(line)
	mainWindow.codeView.lines[line] = string.rep(" ", mainWindow.codeView.indentationWidth) .. mainWindow.codeView.lines[line]
end

local function unindentLine(line)
	mainWindow.codeView.lines[line], countOfReplaces = string.gsub(mainWindow.codeView.lines[line], "^" .. string.rep("%s", mainWindow.codeView.indentationWidth), "")
	return countOfReplaces
end

local function indentOrUnindent(isIndent)
	if mainWindow.codeView.selections[1] then
		local countOfReplacesInFirstLine, countOfReplacesInLastLine
		
		for line = mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line do
			if isIndent then
				indentLine(line)
			else
				local countOfReplaces = unindentLine(line)
				if line == mainWindow.codeView.selections[1].from.line then
					countOfReplacesInFirstLine = countOfReplaces
				elseif line == mainWindow.codeView.selections[1].to.line then
					countOfReplacesInLastLine = countOfReplaces
				end
			end
		end		

		if isIndent then
			setCursorPosition(cursor.position.symbol + mainWindow.codeView.indentationWidth, cursor.position.line)
			mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].from.symbol + mainWindow.codeView.indentationWidth, mainWindow.codeView.selections[1].to.symbol + mainWindow.codeView.indentationWidth
		else
			if countOfReplacesInFirstLine > 0 then
				mainWindow.codeView.selections[1].from.symbol = mainWindow.codeView.selections[1].from.symbol - mainWindow.codeView.indentationWidth
				if cursor.position.line == mainWindow.codeView.selections[1].from.line then
					setCursorPosition(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
				end
			end

			if countOfReplacesInLastLine > 0 then
				mainWindow.codeView.selections[1].to.symbol = mainWindow.codeView.selections[1].to.symbol - mainWindow.codeView.indentationWidth
				if cursor.position.line == mainWindow.codeView.selections[1].to.line then
					setCursorPosition(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
				end
			end
		end
	else
		if isIndent then
			indentLine(cursor.position.line)
			setCursorPositionAndClearSelection(cursor.position.symbol + mainWindow.codeView.indentationWidth, cursor.position.line)
		else
			if unindentLine(cursor.position.line) > 0 then
				setCursorPositionAndClearSelection(cursor.position.symbol - mainWindow.codeView.indentationWidth, cursor.position.line)
			end
		end
	end
end

local function updateRAMProgressBar()
	if not mainWindow.topToolBar.isHidden then
		local totalMemory = computer.totalMemory()
		mainWindow.RAMUsageProgressBar.value = math.ceil((totalMemory - computer.freeMemory()) / totalMemory * 100)
	end
end

local function find()
	if not mainWindow.bottomToolBar.isHidden and mainWindow.bottomToolBar.inputTextBox.text ~= "" then
		findStartFrom = findStartFrom + 1
	
		for line = findStartFrom, #mainWindow.codeView.lines do
			local whereToFind, whatToFind = mainWindow.codeView.lines[line], mainWindow.bottomToolBar.inputTextBox.text
			if not mainWindow.bottomToolBar.caseSensitiveButton.pressed then
				whereToFind, whatToFind = unicode.lower(whereToFind), unicode.lower(whatToFind)
			end

			local success, starting, ending = pcall(string.unicodeFind, whereToFind, whatToFind)
			if success then
				if starting then
					mainWindow.codeView.selections[1] = {
						from = {symbol = starting, line = line},
						to = {symbol = ending, line = line},
						color = 0xCC9200
					}
					findStartFrom = line
					gotoLine(line)
					return
				end
			else
				GUI.error("Wrong searching regex", {title = {color = 0xFFDB40, text = "Warning"}})
			end
		end

		findStartFrom = 0
	end
end

local function findFromFirstDisplayedLine()
	findStartFrom = mainWindow.codeView.fromLine
	find()
end

local function toggleBottomToolBar()
	mainWindow.bottomToolBar.isHidden = not mainWindow.bottomToolBar.isHidden
	mainWindow.toggleBottomToolBarButton.pressed = not mainWindow.bottomToolBar.isHidden
	calculateSizes()
		
	if not mainWindow.bottomToolBar.isHidden then
		mainWindow:draw()
		mainWindow.bottomToolBar.inputTextBox:input()
		findFromFirstDisplayedLine()
	end
end

local function toggleTopToolBar()
	mainWindow.topToolBar.isHidden = not mainWindow.topToolBar.isHidden
	mainWindow.toggleTopToolBarButton.pressed = not mainWindow.topToolBar.isHidden
	calculateSizes()
end

local function toggleLeftToolBar()
	mainWindow.leftTreeView.isHidden = not mainWindow.leftTreeView.isHidden
	mainWindow.toggleLeftToolBarButton.pressed = not mainWindow.leftTreeView.isHidden
	calculateSizes()
end

local function createWindow()
	mainWindow = windows.fullScreen()

	mainWindow.codeView = mainWindow:addCodeView(1, 1, 1, 1, {""}, 1, 1, 1, {}, {}, config.highlightLuaSyntax, 2)
	mainWindow.codeView.scrollBars.vertical.onTouch = function()
		mainWindow.codeView.fromLine = mainWindow.codeView.scrollBars.vertical.value
	end
	mainWindow.codeView.scrollBars.horizontal.onTouch = function()
		mainWindow.codeView.fromSymbol = mainWindow.codeView.scrollBars.horizontal.value
	end
	mainWindow.topMenu = mainWindow:addMenu(1, 1, 1, colors.topMenu.backgroundColor, colors.topMenu.textColor, colors.topMenu.backgroundPressedColor, colors.topMenu.textPressedColor)
	
	local item1 = mainWindow.topMenu:addItem("MineCode", 0x0)
	item1.onTouch = function()
		local menu = GUI.contextMenu(item1.x, item1.y + 1)
		menu:addItem(localization.about).onTouch = function()
			mainWindow.settingsContainer.isHidden = false
			local y = math.floor(mainWindow.settingsContainer.height / 2 - #about / 2)
			mainWindow.settingsContainer:addTextBox(1, y, mainWindow.settingsContainer.width, #about, nil, 0xEEEEEE, about, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
		end
		menu:addItem(localization.quit, false, "^W").onTouch = function()
			mainWindow:close()
		end
		menu:show()
	end

	local item2 = mainWindow.topMenu:addItem(localization.file)
	item2.onTouch = function()
		local menu = GUI.contextMenu(item2.x, item2.y + 1)
		menu:addItem(localization.new, false, "^N").onTouch = function()
			newFile()
		end
		menu:addItem(localization.open, false, "^O").onTouch = function()
			open()
		end
		menu:addItem(localization.getFromWeb, false, "^U").onTouch = function()
			downloadFromWeb()
		end
		menu:addSeparator()
		menu:addItem(localization.save, not mainWindow.leftTreeView.currentFile, "^S").onTouch = function()
			save()
		end
		menu:addItem(localization.saveAs, false, "^⇧S").onTouch = function()
			saveAs()
		end
		menu:show()
	end

	local item3 = mainWindow.topMenu:addItem(localization.edit)
	item3.onTouch = function()
		local menu = GUI.contextMenu(item3.x, item3.y + 1)
		menu:addItem(localization.cut, not mainWindow.codeView.selections[1], "^X").onTouch = function()
			cut()
		end
		menu:addItem(localization.copy, not mainWindow.codeView.selections[1], "^C").onTouch = function()
			copy()
		end
		menu:addItem(localization.paste, not clipboard, "^V").onTouch = function()
			paste(clipboard)
		end
		menu:addSeparator()
		menu:addItem(localization.comment, false, "^/").onTouch = function()
			toggleComment()
		end
		menu:addItem(localization.indent, false, "Tab").onTouch = function()
			indentOrUnindent(true)
		end
		menu:addItem(localization.unindent, false, "⇧Tab").onTouch = function()
			indentOrUnindent(false)
		end
		menu:addItem(localization.deleteLine, false, "^Del").onTouch = function()
			deleteLine(cursor.position.line)
		end
		menu:addSeparator()
		menu:addItem(localization.selectWord).onTouch = function()
			selectWord()
		end
		menu:addItem(localization.selectAll, false, "^A").onTouch = function()
			selectAll()
		end
		menu:show()
	end

	local item4 = mainWindow.topMenu:addItem(localization.view)
	item4.onTouch = function()
		local menu = GUI.contextMenu(item4.x, item4.y + 1)
		menu:addItem(localization.colorScheme).onTouch = function()
			mainWindow.settingsContainer.isHidden = false
			
			local colorSelectorsCount, colorSelectorCountX = 0, 4; for key in pairs(config.syntaxColorScheme) do colorSelectorsCount = colorSelectorsCount + 1 end
			local colorSelectorCountY = math.ceil(colorSelectorsCount / colorSelectorCountX)
			local colorSelectorWidth, colorSelectorHeight, colorSelectorSpaceX, colorSelectorSpaceY = math.floor(mainWindow.settingsContainer.width / colorSelectorCountX * 0.8), 3, 2, 1
			
			local startX, y = math.floor(mainWindow.settingsContainer.width / 2 - (colorSelectorCountX * (colorSelectorWidth + colorSelectorSpaceX) - colorSelectorSpaceX) / 2), math.floor(mainWindow.settingsContainer.height / 2 - (colorSelectorCountY * (colorSelectorHeight + colorSelectorSpaceY) - colorSelectorSpaceY + 3) / 2)
			mainWindow.settingsContainer:addLabel(1, y, mainWindow.settingsContainer.width, 1, 0xFFFFFF, localization.colorScheme):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
			local x, counter = startX, 1

			for key in pairs(config.syntaxColorScheme) do
				local colorSelector = mainWindow.settingsContainer:addColorSelector(x, y, colorSelectorWidth, colorSelectorHeight, config.syntaxColorScheme[key], key)
				colorSelector.onTouch = function()
					config.syntaxColorScheme[key] = colorSelector.color
					syntax.colorScheme = config.syntaxColorScheme
					saveConfig()
				end

				x, counter = x + colorSelectorWidth + colorSelectorSpaceX, counter + 1
				if counter > colorSelectorCountX then
					x, y, counter = startX, y + colorSelectorHeight + colorSelectorSpaceY, 1
				end
			end
		end
		menu:addItem(localization.cursorProperties).onTouch = function()
			mainWindow.settingsContainer.isHidden = false

			local elementWidth = math.floor(mainWindow.width * 0.3)
			local x, y = math.floor(mainWindow.width / 2 - elementWidth / 2), math.floor(mainWindow.height / 2) - 7
			mainWindow.settingsContainer:addLabel(1, y, mainWindow.settingsContainer.width, 1, 0xFFFFFF, localization.cursorProperties):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 3
			local inputTextBox = mainWindow.settingsContainer:addInputTextBox(x, y, elementWidth, 3, 0xCCCCCC, 0x777777, 0xCCCCCC, 0x2D2D2D, config.cursorSymbol, localization.cursorSymbol); y = y + 5
			inputTextBox.validator = function(text)
				if unicode.len(text) == 1 then return true end
			end
			inputTextBox.onInputFinished = function()
				config.cursorSymbol = inputTextBox.text; saveConfig()
			end
			local colorSelector = mainWindow.settingsContainer:addColorSelector(x, y, elementWidth, 3, config.cursorColor, localization.cursorColor); y = y + 5
			colorSelector.onTouch = function()
				config.cursorColor = colorSelector.color; saveConfig()
			end
			local horizontalSlider = mainWindow.settingsContainer:addHorizontalSlider(x, y, elementWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 1, 1000, config.cursorBlinkDelay * 1000, false, localization.cursorBlinkDelay .. ": ", " ms")
			horizontalSlider.onValueChanged = function()
				config.cursorBlinkDelay = horizontalSlider.value / 1000; saveConfig()
			end
		end

		if mainWindow.topToolBar.isHidden then
			menu:addItem(localization.toggleTopToolBar).onTouch = function()
				toggleTopToolBar()
			end
		end
		if config.enableAutoBrackets then
			menu:addItem(localization.disableAutoBrackets, false, "^]").onTouch = function()
				config.enableAutoBrackets = false
				saveConfig()
			end
		else
			menu:addItem(localization.enableAutoBrackets, false, "^]").onTouch = function()
				config.enableAutoBrackets = true
				saveConfig()
			end
		end

		menu:addSeparator()
		menu:addItem(localization.scalePlus, false, "^+").onTouch = function()
			scalePlus()
		end
		menu:addItem(localization.scaleMinus, false, "^-").onTouch = function()
			scaleMinus()
		end
		menu:show()
	end

	local item5 = mainWindow.topMenu:addItem(localization.gotoCyka)
	item5.onTouch = function()
		local menu = GUI.contextMenu(item5.x, item5.y + 1)
		menu:addItem(localization.pageUp, false, "PgUp").onTouch = function()
			pageUp()
		end
		menu:addItem(localization.pageDown, false, "PgDn").onTouch = function()
			pageDown()
		end
		menu:addItem(localization.gotoStart, false, "Home").onTouch = function()
			setCursorPositionToHome()
		end
		menu:addItem(localization.gotoEnd, false, "End").onTouch = function()
			setCursorPositionToEnd()
		end
		menu:show()
	end

	mainWindow.topToolBar = mainWindow:addContainer(1, 2, 1, 3)
	mainWindow.topToolBar.backgroundPanel = mainWindow.topToolBar:addPanel(1, 1, 1, 3, colors.topToolBar)
	mainWindow.titleTextBox = mainWindow.topToolBar:addTextBox(1, 1, 1, 3, 0x0, 0x0, {}, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)
	local titleTextBoxOldDraw = mainWindow.titleTextBox.draw
	mainWindow.titleTextBox.draw = function(titleTextBox)
		titleTextBoxOldDraw(titleTextBox)
		local sidesColor = mainWindow.errorMessage.isHidden and colors.title.default.sides or colors.title.onError.sides
		buffer.square(titleTextBox.x, titleTextBox.y, 1, titleTextBox.height, sidesColor, titleTextBox.colors.text, " ")
		buffer.square(titleTextBox.x + titleTextBox.width - 1, titleTextBox.y, 1, titleTextBox.height, sidesColor, titleTextBox.colors.text, " ")
	end

	mainWindow.RAMUsageProgressBar = mainWindow.topToolBar:addProgressBar(1, 2, 1, 0x777777, 0xBBBBBB, 0xAAAAAA, 50, true, true, "RAM: ", "%")

	--☯◌☺
	mainWindow.addBreakpointButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0x878787, 0xEEEEEE, 0xCCCCCC, 0x444444, "x")
	mainWindow.addBreakpointButton.onTouch = function()
		addBreakpoint()
	end

	mainWindow.toggleSyntaxHighlightingButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "◌")
	mainWindow.toggleSyntaxHighlightingButton.switchMode, mainWindow.toggleSyntaxHighlightingButton.pressed = true, true
	mainWindow.toggleSyntaxHighlightingButton.onTouch = function()
		mainWindow.codeView.highlightLuaSyntax = not mainWindow.codeView.highlightLuaSyntax
		config.highlightLuaSyntax = mainWindow.codeView.highlightLuaSyntax
		saveConfig()
	end

	mainWindow.runButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0x4B4B4B, 0xEEEEEE, 0xCCCCCC, 0x444444, "▷")
	mainWindow.runButton.onTouch = function()
		run()
	end

	mainWindow.toggleLeftToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x4B4B4B, 0xEEEEEE, "⇦")
	mainWindow.toggleLeftToolBarButton.switchMode, mainWindow.toggleLeftToolBarButton.pressed = true, true
	mainWindow.toggleLeftToolBarButton.onTouch = function()
		mainWindow.leftTreeView.isHidden = not mainWindow.toggleLeftToolBarButton.pressed
		calculateSizes()
	end

	mainWindow.toggleBottomToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x696969, 0xEEEEEE, "⇩")
	mainWindow.toggleBottomToolBarButton.switchMode, mainWindow.toggleBottomToolBarButton.pressed = true, false
	mainWindow.toggleBottomToolBarButton.onTouch = function()
		mainWindow.bottomToolBar.isHidden = not mainWindow.toggleBottomToolBarButton.pressed
		calculateSizes()
	end

	mainWindow.toggleTopToolBarButton = mainWindow.topToolBar:addAdaptiveButton(1, 1, 3, 1, 0xCCCCCC, 0x444444, 0x878787, 0xEEEEEE, "⇧")
	mainWindow.toggleTopToolBarButton.switchMode, mainWindow.toggleTopToolBarButton.pressed = true, true
	mainWindow.toggleTopToolBarButton.onTouch = function()
		mainWindow.topToolBar.isHidden = not mainWindow.toggleTopToolBarButton.pressed
		calculateSizes()
	end

	mainWindow.bottomToolBar = mainWindow:addContainer(1, 1, 1, 1)
	mainWindow.bottomToolBar.caseSensitiveButton = mainWindow.bottomToolBar:addAdaptiveButton(1, 1, 2, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, "Aa")
	mainWindow.bottomToolBar.caseSensitiveButton.switchMode = true
	mainWindow.bottomToolBar.onTouch = function()
		find()
	end
	mainWindow.bottomToolBar.inputTextBox = mainWindow.bottomToolBar:addInputTextBox(7, 1, 10, 3, 0xCCCCCC, 0x999999, 0xCCCCCC, 0x2D2D2D, "", localization.findSomeShit)
	mainWindow.bottomToolBar.inputTextBox.onInputFinished = function()
		findFromFirstDisplayedLine()
	end
	mainWindow.bottomToolBar.findButton = mainWindow.bottomToolBar:addAdaptiveButton(1, 1, 3, 1, 0x3C3C3C, 0xEEEEEE, 0xBBBBBB, 0x2D2D2D, localization.find)
	mainWindow.bottomToolBar.findButton.onTouch = function()
		find()
	end
	mainWindow.bottomToolBar.isHidden = true

	mainWindow.leftTreeView = mainWindow:addTreeView(1, 1, 1, 1, colors.leftTreeView.background, 0x3C3C3C, 0x3C3C3C, 0xEEEEEE, 0x888888, 0x444444, 0x00DBFF, "/")
	mainWindow.leftTreeView.onFileSelected = function(path)
		loadFile(path)
	end

	mainWindow.errorMessage = mainWindow:addContainer(1, 1, 1, 1)
	mainWindow.errorMessage.backgroundPanel = mainWindow.errorMessage:addPanel(1, 1, 1, 1, 0xFFFFFF, 30)
	mainWindow.errorMessage.errorTextBox = mainWindow.errorMessage:addTextBox(3, 2, 1, 1, nil, 0x4B4B4B, {}, 1)
	hideErrorMessage()

	mainWindow.settingsContainer = mainWindow:addContainer(1, 1, 1, 1)
	mainWindow.settingsContainer.backgroundPanel = mainWindow.settingsContainer:addPanel(1, 1, mainWindow.settingsContainer.width, mainWindow.settingsContainer.height, 0x0, 30)
	mainWindow.settingsContainer.backgroundPanel.onTouch = hideSettingsContainer
	mainWindow.settingsContainer.isHidden = true

	mainWindow.onAnyEvent = function(eventData)		
		if eventData[1] == "touch" and isClickedOnCodeArea(eventData[3], eventData[4]) then
			cursor.blinkState = true
			
			if eventData[5] == 1 then
				local menu = GUI.contextMenu(eventData[3], eventData[4])
				menu:addItem(localization.cut, not mainWindow.codeView.selections[1], "^X").onTouch = function()
					cut()
				end
				menu:addItem(localization.copy, not mainWindow.codeView.selections[1], "^C").onTouch = function()
					copy()
				end
				menu:addItem(localization.paste, not clipboard, "^V").onTouch = function()
					paste(clipboard)
				end
				menu:addSeparator()
				menu:addItem(localization.selectWord).onTouch = function()
					selectWord()
				end
				menu:addItem(localization.selectAll, false, "^A").onTouch = function()
					selectAll()
				end
				menu:show()
			else
				setCursorPositionAndClearSelection(convertScreenCoordinatesToCursorPosition(eventData[3], eventData[4]))

				local newUptime = computer.uptime()
				if newUptime - lastClickUptime <= config.doubleClickDelay then selectWord() end
				lastClickUptime = newUptime
			end
		elseif eventData[1] == "drag" and isClickedOnCodeArea(eventData[3], eventData[4]) then
			cursor.blinkState = true
			
			if eventData[5] ~= 1 then
				mainWindow.codeView.selections[1] = mainWindow.codeView.selections[1] or {from = {}, to = {}}
				mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].from.line = cursor.position.symbol, cursor.position.line
				mainWindow.codeView.selections[1].to.symbol, mainWindow.codeView.selections[1].to.line = fixCursorPosition(convertScreenCoordinatesToCursorPosition(eventData[3], eventData[4]))
				
				if mainWindow.codeView.selections[1].from.line > mainWindow.codeView.selections[1].to.line then
					mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line = swap(mainWindow.codeView.selections[1].from.line, mainWindow.codeView.selections[1].to.line)
					mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = swap(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol)
				elseif mainWindow.codeView.selections[1].from.line == mainWindow.codeView.selections[1].to.line then
					if mainWindow.codeView.selections[1].from.symbol > mainWindow.codeView.selections[1].to.symbol then
						mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol = swap(mainWindow.codeView.selections[1].from.symbol, mainWindow.codeView.selections[1].to.symbol)
					end
				end
			end
		elseif eventData[1] == "key_down" then
			cursor.blinkState = true

			-- Ctrl or CMD
			if keyboard.isKeyDown(29) or keyboard.isKeyDown(219) then
				-- Slash
				if eventData[4] == 53 then
					toggleComment()
				-- ]
				elseif eventData[4] == 27 then
					config.enableAutoBrackets = not config.enableAutoBrackets
					saveConfig()
				-- A
				elseif eventData[4] == 30 then
					selectAll()
				-- C
				elseif eventData[4] == 46 then
					copy()
				-- V
				elseif eventData[4] == 47 then
					paste(clipboard)
				-- X
				elseif eventData[4] == 45 then
					cut()
				-- W
				elseif eventData[4] == 17 then
					mainWindow:close()
				-- N
				elseif eventData[4] == 49 then
					newFile()
				-- O
				elseif eventData[4] == 24 then
					open()
				-- U
				elseif eventData[4] == 22 then
					downloadFromWeb()
				-- S
				elseif eventData[4] == 31 then
					-- Shift
					if mainWindow.leftTreeView.currentFile and not keyboard.isKeyDown(42) then
						save()
					else
						saveAs()
					end
				-- F
				elseif eventData[4] == 33 then
					toggleBottomToolBar()
				-- G
				elseif eventData[4] == 34 then
					find()
				-- Backspace
				elseif eventData[4] == 14 then
					deleteLine(cursor.position.line)
				-- Delete
				elseif eventData[4] == 211 then
					deleteLine(cursor.position.line)
				-- +
				elseif eventData[4] == 13 then
					scalePlus()
				-- -
				elseif eventData[4] == 12 then
					scaleMinus()
				end
			-- Arrows up, down, left, right
			elseif eventData[4] == 200 then
				moveCursor(0, -1)
			elseif eventData[4] == 208 then
				moveCursor(0, 1)
			elseif eventData[4] == 203 then
				moveCursor(-1, 0)
			elseif eventData[4] == 205 then
				moveCursor(1, 0)
			-- Backspace
			elseif eventData[4] == 14 then
				backspace()
			-- Tab
			elseif eventData[4] == 15 then
				if keyboard.isKeyDown(42) then
					indentOrUnindent(false)
				else
					indentOrUnindent(true)
				end
			-- Enter
			elseif eventData[4] == 28 then
				enter()
			-- F5
			elseif eventData[4] == 63 then
				run()
			-- Home
			elseif eventData[4] == 199 then
				setCursorPositionToHome()
			-- End
			elseif eventData[4] == 207 then
				setCursorPositionToEnd()
			-- Page Up
			elseif eventData[4] == 201 then
				pageUp()
			-- Page Down
			elseif eventData[4] == 209 then
				pageDown()
			-- Delete
			elseif eventData[4] == 211 then
				delete()
			else
				pasteAutoBrackets(eventData[3])
			end
		elseif eventData[1] == "clipboard" then
			paste(splitStringIntoLines(eventData[3]))
		elseif eventData[1] == "scroll" then
			if isClickedOnCodeArea(eventData[3], eventData[4]) then
				scroll(eventData[5], config.scrollSpeed)
			end
		elseif eventData[1] == "component_added" or eventData[1] == "component_removed" then
			if eventData[3] == "screen" then
				os.sleep(0.5)
				changeScale(config.screenScale)
			end
		elseif not eventData[1] then
			cursor.blinkState = not cursor.blinkState
		end

		updateTitle()
		updateRAMProgressBar()
		mainWindow:draw()
		if cursor.blinkState then
			local x, y = mainWindow.codeView.codeAreaPosition + cursor.position.symbol - mainWindow.codeView.fromSymbol + 1, mainWindow.codeView.y + cursor.position.line - mainWindow.codeView.fromLine
			if 
				x >= mainWindow.codeView.codeAreaPosition + 1 and
				x <= mainWindow.codeView.codeAreaPosition + mainWindow.codeView.codeAreaWidth - 2 and
				y >= mainWindow.codeView.y and
				y <= mainWindow.codeView.y + mainWindow.codeView.height - 2
			then
				buffer.text(x, y, config.cursorColor, config.cursorSymbol)
			end
		end
		buffer.draw()
	end
end

---------------------------------------------------- RUSH B! ----------------------------------------------------

loadConfig()
createWindow()
changeScale(config.screenScale)
updateTitle()
updateRAMProgressBar()
mainWindow:draw()

if args[1] == "open" and fs.exists(args[2] or "") then
	loadFile(args[2])
else
	newFile()
end

mainWindow:draw()
buffer.draw()
mainWindow:handleEvents(config.cursorBlinkDelay)


