
-- package.loaded.GUI = nil
-- _G.GUI = nil

local advancedLua = require("advancedLua")
local buffer = require("doubleBuffering")
local MineOSCore = require("MineOSCore")
local image = require("image")
local GUI = require("GUI")
local fs = require("filesystem")
local component = require("component")
local unicode = require("unicode")
local event = require("event")
local ecs = require("ECSAPI")

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

local obj = {}
local sizes = {}
local colors = {
	main = 0xFFFFFF,
	topBar = 0xDDDDDD,
	topBarText = 0x555555,
	topBarElement = 0xCCCCCC,
	topBarElementText = 0x555555,
	statusBar = 0xDDDDDD,
	statusBarText = 0x888888,
	appName = 0x262626,
	version = 0x555555,
	description = 0x888888,
	downloadButton = 0xAAAAAA,
	downloadButtonText = 0xFFFFFF,
	downloading = 0x009240,
	downloadingText = 0xFFFFFF,
	downloaded = 0xCCCCCC,
	downloadedText = 0xFFFFFF,
}

local typeFilters = {
	"Application",
	"Library",
	"Wallpaper",
	"Script",
}

local localization = table.fromFile("MineOS/Applications/AppMarket.app/Resources/Localization/" .. _G.OSSettings.language .. ".lang")
local appMarketConfigPath = "MineOS/System/AppMarket/"
local pathToApplications = "MineOS/System/OS/Applications.txt"
local pathToNewApplications = appMarketConfigPath .. "NewApplications.txt"
local updateImage = image.load(MineOSCore.paths.icons .. "Update.pic")
local topBarElements = {localization.applications, localization.libraries, localization.wallpapers, localization.other, localization.updates}
local oldApplications, newApplications, currentApps, changes = {}, {}, {}, {}

local currentTopBarElement = 1
local from, limit, fromY = 1, 8

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

local function correctDouble(number)
	return string.format("%.2f", number)
end

local function status(text)
	text = unicode.sub(text, 1, sizes.width - 2)
	local y = sizes.y + sizes.height - 1
	buffer.square(sizes.x, y, sizes.width, 1, colors.statusBar, colors.statusBarText, " ")
	buffer.text(sizes.x + 1, y, colors.statusBarText, text)
	buffer.draw()
end

local function calculateSizes()
	sizes.width, sizes.height = math.floor(buffer.screen.width * 0.6), math.floor(buffer.screen.height * 0.7)
	sizes.x, sizes.y = math.floor(buffer.screen.width / 2 - sizes.width / 2), math.floor(buffer.screen.height / 2 - sizes.height / 2)
	sizes.topBarHeight = 3
	obj.main = GUI.object(sizes.x, sizes.y + sizes.topBarHeight, sizes.width, sizes.height - sizes.topBarHeight)
	sizes.downloadButtonWidth = 17
	sizes.descriptionTruncateSize = sizes.width - 6 - MineOSCore.iconWidth - sizes.downloadButtonWidth
	sizes.searchFieldWidth = math.floor(sizes.width * 0.3)
	obj.searchTextField = GUI.inputTextBox(math.floor(sizes.x + sizes.width / 2 - sizes.searchFieldWidth / 2), 1, sizes.searchFieldWidth, 1, 0xEEEEEE, 0x555555, 0xEEEEEE, 0x262626, "", localization.search, true)
end

local function drawTopBar()
	obj.topBarButtons = GUI.tabBar(sizes.x, sizes.y, sizes.width, sizes.topBarHeight, 2, colors.topBar, colors.topBarText, colors.topBarElement, colors.topBarElementText, table.unpack(topBarElements))
	obj.topBarButtons.selectedTab = currentTopBarElement
	obj.topBarButtons:draw()
	obj.windowActionButtons = GUI.windowActionButtons(sizes.x + 1, sizes.y):draw()
end

local function getIcon(url)
	local success, response = ecs.internetRequest(url)
	local path = appMarketConfigPath .. "TempIcon.pic"
	if success then
		local file = io.open(path, "w")
		file:write(response)
		file:close()
	else
		GUI.error(tostring(response), {title = {color = 0xFFDB40, text = localization.errorWhileLoadingIcon}})
	end
	return image.load(path)
end

local function getDescription(url)
	local success, response = ecs.internetRequest(url)
	if success then
		return response
	else
		GUI.error(tostring(response), {title = {color = 0xFFDB40, text = localization.errorWhileLoadingDescription}})
	end
end

local function getApplication(i)
	currentApps[i] = {}
	currentApps[i].name = fs.name(newApplications[i].name)

	if newApplications[i].icon then
		currentApps[i].icon = getIcon(newApplications.GitHubUserURL .. newApplications[i].icon)
	else
		if newApplications[i].type == "Application" then
			currentApps[i].icon = failureIcon
		elseif newApplications[i].type == "Wallpaper" then
			currentApps[i].icon = MineOSCore.icons.image
		elseif newApplications[i].type == "Library" then
			currentApps[i].icon = MineOSCore.icons.lua
		else
			currentApps[i].icon = MineOSCore.icons.script
		end
	end

	if newApplications[i].about then
		currentApps[i].description = getDescription(newApplications.GitHubUserURL .. newApplications[i].about .. _G.OSSettings.language .. ".txt")
		currentApps[i].description = string.wrap({currentApps[i].description}, sizes.descriptionTruncateSize )
	else
		currentApps[i].description = {localization.descriptionNotAvailable}
	end

	if newApplications[i].version then
		currentApps[i].version = localization.version .. correctDouble(newApplications[i].version)
	else
		currentApps[i].version = localization.versionNotAvailable
	end
end

local function checkAppExists(name, type)
	if type == "Application" then
		name = name .. ".app"
	end
	return fs.exists(name)
end

local function drawApplication(x, y, i, doNotDrawButton)
	buffer.image(x, y, currentApps[i].icon)
	buffer.text(x + 10, y, colors.appName, currentApps[i].name)
	buffer.text(x + 10, y + 1, colors.version, currentApps[i].version)
	local appExists = checkAppExists(newApplications[i].name, newApplications[i].type)
	local text = appExists and localization.update or localization.download
	
	if not doNotDrawButton then
		local xButton, yButton = sizes.x + sizes.width - sizes.downloadButtonWidth - 2, y + 1
		if currentApps[i].buttonObject then
			currentApps[i].buttonObject.x, currentApps[i].buttonObject.y = xButton, yButton
			currentApps[i].buttonObject:draw()
		else
			currentApps[i].buttonObject = GUI.button(xButton, yButton, sizes.downloadButtonWidth, 1, colors.downloadButton, colors.downloadButtonText, 0x555555, 0xFFFFFF, text):draw()
		end
	end

	for j = 1, #currentApps[i].description do
		buffer.text(x + 10, y + j + 1, colors.description, currentApps[i].description[j])
	end
	y = y + (#currentApps[i].description > 2 and #currentApps[i].description - 2 or 0)
	y = y + 5

	return x, y
end

local function drawPageSwitchButtons(y)
	local text = localization.applicationsFrom .. from .. localization.applicationsTo .. from + limit - 1
	local textLength = unicode.len(text)
	local buttonWidth = 5
	local width = buttonWidth * 2 + textLength + 2
	local x = math.floor(sizes.x + sizes.width / 2 - width / 2)
	obj.prevPageButton = GUI.button(x, y, buttonWidth, 1, colors.downloadButton, colors.downloadButtonText, 0x262626, 0xFFFFFF, "<"):draw()
	x = x + obj.prevPageButton.width + 1
	buffer.text(x, y, colors.version, text)
	x = x + textLength + 1
	obj.nextPageButton = GUI.button(x, y, buttonWidth, 1, colors.downloadButton, colors.downloadButtonText, 0x262626, 0xFFFFFF, ">"):draw()
end

local function clearMainZone()
	buffer.square(sizes.x, obj.main.y, sizes.width, obj.main.height, 0xFFFFFF)
end

local function drawMain(refreshData)
	clearMainZone()
	local x, y = sizes.x + 2, fromY

	buffer.setDrawLimit(sizes.x, obj.main.y, sizes.width, obj.main.height)

	obj.searchTextField.y, obj.searchTextField.isHidden = y, false
	obj.searchTextField:draw()
	y = y + 2

	local matchCount = 1
	for i = 1, #newApplications do
		if newApplications[i].type == typeFilters[currentTopBarElement] then
			if obj.searchTextField.text == "" or (string.find(unicode.lower(fs.name(newApplications[i].name)), unicode.lower(obj.searchTextField.text))) then
				if matchCount >= from and matchCount <= from + limit - 1 then
					if refreshData and not currentApps[i] then
						status(localization.downloadingInfoAboutApplication .. " \"" .. newApplications[i].name .. "\"")
						getApplication(i)
					end
					x, y = drawApplication(x, y, i)
				end
				matchCount = matchCount + 1
			end
		end
	end

	if matchCount > limit then
		drawPageSwitchButtons(y)
	end

	buffer.resetDrawLimit()
end

local function getNewApplications()
	ecs.getFileFromUrl(oldApplications.GitHubApplicationListURL, pathToNewApplications)
	newApplications = table.fromFile(pathToNewApplications)
end

local function getChanges()
	changes = {}
	for j = 1, #newApplications do
		local matchFound = false
		for i = 1, #oldApplications do	
			if oldApplications[i].name == newApplications[j].name then
				if oldApplications[i].version < newApplications[j].version then table.insert(changes, j) end
				matchFound = true
				break
			end
		end
		if not matchFound then table.insert(changes, j) end
	end
end

local function updates()
	clearMainZone()

	obj.searchTextField.isHidden = true

	if #changes > 0 then
		buffer.setDrawLimit(sizes.x, obj.main.y, sizes.width, obj.main.height)
		local x, y = sizes.x + 2, fromY
		obj.updateAllButton = GUI.button(math.floor(sizes.x + sizes.width / 2 - sizes.downloadButtonWidth / 2), y, 20, 1, colors.downloadButton, colors.downloadButtonText, 0x555555, 0xFFFFFF, "Обновить все"):draw()
		y = y + 2

		for i = from, (from + limit) do
			if not changes[i] then break end
			if not currentApps[changes[i]] then
				status(localization.downloadingInfoAboutApplication .. " \"" .. fs.name(newApplications[changes[i]].name) .. "\"")
				getApplication(changes[i])
			end
			x, y = drawApplication(x, y, changes[i], true)
		end

		if #changes > limit then
			drawPageSwitchButtons(y)
		end
		buffer.resetDrawLimit()
	else
		local text = localization.youHaveNewestApps
		buffer.text(math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2), math.floor(obj.main.y + obj.main.height / 2 - 1), colors.description, text)
	end
end

local function flush()
	fromY = obj.main.y + 1
	from = 1
	currentApps = {}
end

local function loadOldApplications()
	oldApplications = table.fromFile(pathToApplications)
end

local function saveOldApplications()
	table.toFile(pathToApplications, oldApplications)
end

local function drawAll(refreshIcons, force)
	drawTopBar()
	if currentTopBarElement == 5 then
		updates()
	else
		drawMain(refreshIcons)
	end
	buffer.draw(force)
end

local function updateImageWindow()
	clearMainZone()
	local x, y = math.floor(sizes.x + sizes.width / 2 - updateImage.width / 2), math.floor(obj.main.y + obj.main.height / 2 - updateImage.height / 2 - 2)
	buffer.image(x, y, updateImage)
	return y + updateImage.height
end

local function updateImageWindowWithText(text)
	local y = updateImageWindow() + 2
	local x = math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2)
	buffer.text(x, y, colors.description, text)
end

local function updateAll()
	local y = updateImageWindow()
	local barWidth = math.floor(sizes.width * 0.6)
	local xBar = math.floor(sizes.x + sizes.width / 2 - barWidth / 2)
	y = y + 2
	for i = 1, #changes do
		local text = localization.updating .. " " .. fs.name(newApplications[changes[i]].name)
		local xText = math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2)
		buffer.square(sizes.x, y + 1, sizes.width, 1, 0xFFFFFF)
		buffer.text(xText, y + 1, colors.description, text)
		GUI.progressBar(xBar, y, barWidth, 0x0092FF, 0xCCCCCC, 0x0, math.ceil(i / #changes * 100), true, false):draw()
		buffer.draw()
		ecs.getOSApplication(newApplications[changes[i]], true)
	end
	changes = {}
	oldApplications = newApplications
	saveOldApplications()
	fs.remove(pathToNewApplications)
	require("computer").shutdown(true)
end

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

-- buffer.start()
-- buffer.clear(0xFF8888)

local args = {...}
if args[1] == "updateCheck" then
	currentTopBarElement = 5
end

fs.makeDirectory(appMarketConfigPath)
calculateSizes()
flush()
loadOldApplications()
drawTopBar()
GUI.windowShadow(sizes.x, sizes.y, sizes.width, sizes.height, 50)
updateImageWindowWithText(localization.downloadingApplicationsList)
buffer.draw()
getNewApplications()
getChanges()
drawAll(true, false)

while true do
	local e = {event.pull()}
	if e[1] == "touch" then

		if obj.main:isClicked(e[3], e[4]) then
			if obj.searchTextField:isClicked(e[3], e[4]) then
				obj.searchTextField:input()
				flush()
				drawAll(true, false)
			end

			if currentTopBarElement < 5 then
				for appIndex, app in pairs(currentApps) do
					if app.buttonObject:isClicked(e[3], e[4]) then
						app.buttonObject:pressAndRelease(0.3)
						if app.buttonObject.text == localization.update or app.buttonObject.text == localization.download then
							app.buttonObject.text = localization.downloading
							app.buttonObject.disabled = true
							app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloading, colors.downloadingText
							app.buttonObject:draw()
							buffer.draw()
							ecs.getOSApplication(newApplications[appIndex], true)
							app.buttonObject.text = localization.downloaded
							app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloaded, colors.downloadedText
							app.buttonObject:draw()
							buffer.draw()
						end
						break
					end	
				end
			else
				if obj.updateAllButton and obj.updateAllButton:isClicked(e[3], e[4]) then
					obj.updateAllButton:pressAndRelease()
					updateAll()
					flush()
					drawAll()
				end
			end

			if obj.nextPageButton then
				if obj.nextPageButton:isClicked(e[3], e[4]) then
					obj.nextPageButton:pressAndRelease()
					fromY = obj.main.y + 1
					from = from + limit
					currentApps = {}
					drawAll(true, false)
				elseif obj.prevPageButton:isClicked(e[3], e[4]) then
					if from > limit then
						fromY = obj.main.y + 1
						from = from - limit
						currentApps = {}
						drawAll(true, false)
					end
				end
			end
		end


		if obj.windowActionButtons.close:isClicked(e[3], e[4]) then
			obj.windowActionButtons.close:pressAndRelease()
			return
		end

		for key, button in pairs(obj.topBarButtons.tabs.children) do
			if button:isClicked(e[3], e[4]) then
				currentTopBarElement = key
				flush()
				drawAll(true, false)
				break
			end
		end
	elseif e[1] == "scroll" then
		if e[5] == 1 then
			if (fromY < obj.main.y) then
				fromY = fromY + 2
				drawAll(false, false)
			end
		else
			fromY = fromY - 2
			drawAll(false, false)
		end
	end
end








