for k,v in pairs(colors) do
	if type(v) == "number" then
		term.setPaletteColor(v,term.native().getPaletteColor(v))
	end
end

if lOS and lOS.started then
    error("LevelOS is already running, silly!")
end
if jit then error("no speed thank you very much", 0) end
if not term.isColour() then return end
local w,h = term.getSize()
if h < 19 then return end
local newwin = false
term.clear()
term.setCursorPos(1,1)

local therealOGterm = term.current()
local function tokenise( ... )
    local sLine = table.concat( { ... }, " " )
    local tWords = {}
    local bQuoted = false
    for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do
        if bQuoted then
            table.insert( tWords, match )
        else
            for m in string.gmatch( match, "[^ \t]+" ) do
                table.insert( tWords, m )
            end
        end
        bQuoted = not bQuoted
    end
    return tWords
end

if fs.exists("AppData") == false then
    fs.makeDir("AppData")
end

if fs.exists("User") == false then
    fs.makeDir("User")
    fs.makeDir("User/Documents")
    fs.makeDir("User/Images")
    fs.makeDir("User/Scripts")
    fs.makeDir("User/Downloads")
end

if not fs.exists("bigfont") then
    shell.run("pastebin get 3LfWxRWh bigfont")
end

local to_colors, to_blit = {}, {}
for i = 1, 16 do
    to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i)
    to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1)
end
 
 
 
local function toColor(theblit)
    return to_colors[theblit] or nil
end
 
 
 
local function toBlit(thecolor)
    return to_blit[thecolor] or nil
end

local function render(spr,x,y)
    local format = "lImg"
    if format == "lImg" then
        local sW,sH = #spr[1][1],#spr
        local w,h = term.getSize()
        for l=1,#spr do
        	if not y then
            	term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),(math.ceil(h/2)-math.floor(sH/2)+(l-1))+x)
            else
            	term.setCursorPos(x,y+(l-1))
            end
            local bl = {}
            bl[1] = spr[l][1]
            bl[2] = string.gsub(spr[l][2],"T",toBlit(term.getBackgroundColor()))
            bl[3] = string.gsub(spr[l][3],"T",toBlit(term.getBackgroundColor()))
            term.blit(unpack(bl))
        end
    elseif format == "nfp" or format == "nfg" then
        local b,e = string.find(spr,"\n")
        local sW,sH
        local w,h = term.getSize()
        local lines,sW = getLines(spr)
        sH = #lines
        for l=1,sH do
            term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),math.ceil(h/2)-math.floor(sH/2)+(l-1))
            term.blit(string.rep(" ",#lines[l]),lines[l],lines[l])
        end
    end
end


local function fread(file)
    local f = fs.open(file,"r")
    local o = f.readAll()
    f.close()
    return o
end

local function fwrite(file,content)
    local f = fs.open(file,"w")
    f.write(content)
    f.close()
    return true
end

local loadingico = textutils.unserialize(fread("LevelOS/assets/loading.limg"))


os.loadAPI("bigfont")


local progress = 0

local function centerText(text) -- i tried to put indentation but pastebin is being stupid for some reason
    local x,y = term.getSize()
    local x2,y2 = term.getCursorPos()
    term.setCursorPos((math.ceil(x / 2) - math.floor(text:len() / 2)), y2)
    term.write(text)
    term.setCursorPos(x2,y2+1)
end

local doUpdate

local function bootscreen()
    term.setBackgroundColor(colors.black)
    term.setTextColor(colors.cyan)
    term.clear()
    local w,h = term.getSize()
    if h > 19 then
    	bigfont.writeOn(term.current(),2,"L",nil,h/2-8)
    else
    	bigfont.writeOn(term.current(),2,"L",nil,h/2-5)
    end
    local frame = 1
    while true do
        if doUpdate then
            term.setBackgroundColor(colors.blue)
            term.setTextColor(colors.white)
            term.clear()
            local w,h = term.getSize()
            term.setCursorPos(1,math.ceil(h/2))
            centerText("Getting ready for updates")
            centerText("Do not turn off your computer")
            local init = false
            while true do
                render(loadingico[frame],-5)
                if progress > 0 then
                	if not init then
                		term.setBackgroundColor(colors.blue)
                		term.setTextColor(colors.white)
                		term.clear()
                		render(loadingico[frame],-5)
                		init = true
                	end
                	term.setCursorPos(1,math.ceil(h/2))
                	centerText("Working on updates")
                	centerText(math.floor(progress + 0.5).."% complete")
                	centerText("Do not turn off your computer")
                end
                frame = frame+1
                if frame > #loadingico then
                    frame = 1
                end
                os.sleep(0.1)
            end
        end
        if h > 19 then
        	render(loadingico[frame],10)
        else
        	render(loadingico[frame],7)
        end
        frame = frame+1
        if frame > #loadingico then
            frame = 1
        end
        os.sleep(0.1)
    end
end

local hpost = http.post

local function getField(thing,fieldname)
    if string.find(thing,"<"..fieldname..">",1,true) ~= nil and string.find(thing,"</"..fieldname..">",1,true) ~= nil then
        begin = nil
        ending = nil
        trash,begin = string.find(thing,"<"..fieldname..">",1,true)
        ending,ending2 = string.find(thing,"</"..fieldname..">",begin+1,true)
        if begin ~= nil and ending ~= nil then
            return string.sub(thing,begin+1,ending-1),string.sub(thing,1,trash-1)..string.sub(thing,ending2+1,string.len(thing))
        end
    end
    return nil
end

local function download(pth)
    local f = hpost("https://level.eu5.net/sGet.php","path="..textutils.urlEncode(pth).."&code="..textutils.urlEncode("lSlb8kZq"),{Cookie=userID}).readAll()
    if f ~= "409" and f ~= "403" and f ~= "401" then
    	if pth == "startup.lua" and f ~= fread(shell.getRunningProgram()) then
        	fwrite(shell.getRunningProgram(),f)
        	os.sleep(1)
        	os.reboot()
        end
        if pth ~= "startup.lua" then
        	fwrite(pth,f)
        end
        return true
    else
        return false
    end
end

local function update()
    local opost = http.post
    local ping = http.get("https://level.eu5.net/ping.php")
    if not ping then
        local ping2 = http.get("http://level.eu5.net/ping.php")
        if not ping2 then
            -- no internet
            return
        end
        function http.post(...)
            local args = table.pack(...)
            local r = table.pack(opost(...))
            if not r[1] and string.find(args[1],"https://",nil,true) == 1 then
                args[1] = "http"..string.sub(args[1],6,#args[1])
                return opost(table.unpack(args))
            else
                return table.unpack(r)
            end
        end
        local oget = http.get
        function http.get(...)
            local args = table.pack(...)
            local r = table.pack(oget(...))
            if not r[1] and string.find(args[1],"https://",nil,true) == 1 then
                args[1] = "http"..string.sub(args[1],6,#args[1])
                return oget(table.unpack(args))
            else
                return table.unpack(r)
            end
        end
        hpost = http.post
    end


	local servertimestamp
	local clienttimestamp
	if fs.exists("LevelOS/data/version.txt") then
    	clienttimestamp = tonumber(fread("LevelOS/data/version.txt"))
    end
    if not clienttimestamp then
    	clienttimestamp = 0
    end
	local response,err = hpost("https://level.eu5.net/sGet.php","path="..textutils.urlEncode("").."&code="..textutils.urlEncode("lSlb8kZq"))
	if not response then
		-- could not connect
		--os.sleep(0.5)
		-- put "starting in offline mode"
		return
	end
	local f = response.readAll()
	servertimestamp = tonumber((getField(f,"version")))
	if servertimestamp > clienttimestamp then
		doUpdate = true
	end
    local tFiles = 0
    local pack = "Full"
    if fs.exists("LevelOS/data/settings.lconf") then
        local set = textutils.unserialize(fread("LevelOS/data/settings.lconf"))
        if set.package then
            pack = set.package
        end
    end

    local tree = {}
    local folders = {}
    local function searchFolder(folder)
        --print("Searching folder root/"..folder)
        local f = hpost("https://level.eu5.net/sGet.php","path="..textutils.urlEncode(folder).."&code="..textutils.urlEncode("lSlb8kZq")).readAll()
        --print(f)
        local f2 = f
        while true do
            local file = nil
            file,f = getField(f,"file")
            if not file then
                break
            else
                local name = getField(file,"name")
                local timestamp = getField(file,"timestamp")
                timestamp = tonumber(timestamp)
                if pack == "Full" or fs.exists(fs.combine(folder,name)) or folder == "LevelOS/assets" or fs.combine(folder,name) == "startup.lua" then
                	tree[fs.combine(folder,name)] = {timestamp=timestamp}
                	tFiles = tFiles+1
                end
                --print("Found "..fs.combine(folder,name))
            end
        end
        f = f2
        while true do
            local file = nil
            file,f = getField(f,"folder")
            if not file then
                break
            else
                local name = getField(file,"name")
                if not fs.exists(fs.combine(folder,name)) then
                    fs.makeDir(fs.combine(folder,name))
                end
                folders[fs.combine(folder,name)] = ""
                searchFolder(fs.combine(folder,name))
            end
        end
        return true
    end
    
    if doUpdate then
        if not searchFolder("") then return false end

        if tree["startup.lua"] then
        	download("startup.lua")
        end

        for k,v in pairs(tree) do
            if download(k,root,v.timestamp) == true then
                progress = progress+(100/tFiles)
            end
        end
        fwrite("LevelOS/data/version.txt",tostring(servertimestamp))
        os.sleep(1)
        os.reboot()
    end
    if not fs.exists("LevelOS/assets/wifi.limg") then
    	folders["LevelOS/assets"] = ""
    	if not searchFolder("LevelOS/assets") then return false end
    	for k,v in pairs(tree) do
            if download(k) == true then
            end
        end
    end
end


parallel.waitForAny(bootscreen,update)

local expect = require "cc.expect".expect

--[[- Wraps a block of text, so that each line fits within the given width.
This may be useful if you want to wrap text before displaying it to a
@{monitor} or @{printer} without using @{_G.print|print}.
@tparam string text The string to wrap.
@tparam[opt] number width The width to constrain to, defaults to the width of
the terminal.
@treturn { string... } The wrapped input string as a list of lines.
@usage Wrap a string and write it to the terminal.
    term.clear()
    local lines = require "cc.strings".wrap("This is a long piece of text", 10)
    for i = 1, #lines do
      term.setCursorPos(1, i)
      term.write(lines[i])
    end
]]
local function wrap(text, width)
    expect(1, text, "string")
    expect(2, width, "number", "nil")
    width = width or term.getSize()


    local lines, lines_n, current_line = {}, 0, ""
    local function push_line()
        lines_n = lines_n + 1
        lines[lines_n] = current_line
        current_line = ""
    end

    local pos, length = 1, #text
    local sub, match = string.sub, string.match
    while pos <= length do
        local head = sub(text, pos, pos)
        if head == " " or head == "\t" then
            local whitespace = match(text, "^[ \t]+", pos)
            current_line = current_line .. whitespace
            pos = pos + #whitespace
        elseif head == "\n" then
            push_line()
            pos = pos + 1
        else
            local word = match(text, "^[^ \t\n]+", pos)
            pos = pos + #word
            if #word > width then
                -- Print a multiline word
                while #word > 0 do
                    local space_remaining = width - #current_line - 1
                    if space_remaining <= 0 then
                        push_line()
                        space_remaining = width
                    end

                    current_line = current_line .. sub(word, 1, space_remaining)
                    word = sub(word, space_remaining + 1)
                end
            else
                -- Print a word normally
                if width - #current_line < #word then push_line() end
                current_line = current_line .. word
            end
        end
    end

    push_line()

    -- Trim whitespace longer than width.
    for k, line in pairs(lines) do
        line = line:sub(1, width)
        lines[k] = line
    end

    return lines
end

local function bPrint(txt)
	local x,y = term.getCursorPos()
	local w,h = term.getSize()

	local text = wrap(txt,w-(x-1))
	for t=1,#text do
		term.write(text[t])
		term.setCursorPos(x,y+t)
	end
end

local function wordwrap(str)
    local x,y = term.getCursorPos()
    local tW,tH = term.getSize()
    for w in str:gmatch("%S+") do
        local x1,y1 = term.getCursorPos()
        if x1+(#w*3) >= tW then
            bigfont.bigPrint(" ")
            local x2,y2 = term.getCursorPos()
            term.setCursorPos(x,y2)
        end
        bigfont.bigWrite(w.." ")
    end
end


if fs.exists("LevelOS/lStore.lua") then
	shell.setAlias("lStore","LevelOS/lStore.lua")

	local completion = require "cc.shell.completion"
	local function completelStorePut(shell, text, previous)
	    if previous[2] == "put" then
	        return fs.complete(text, "User/Cloud", true, false)
	    end
	end

	shell.setCompletionFunction("LevelOS/lStore.lua", completion.build(
	    { completion.choice, { "put ", "get ", "run " } },
	    completelStorePut
	))
end

local u

local u = {pcall(loadfile("LevelOS/system.lua",_ENV))}
_G.whatitreturn = u
term.redirect(therealOGterm)
os.loadAPI("bigfont")
term.setPaletteColor(colors.blue,0,120/255,215/255)
term.setPaletteColor(colors.white,1,1,1)
term.setCursorPos(4,4)
term.setBackgroundColor(colors.blue)
term.clear()
local w,h = term.getSize()
local crashwin
if w > 140 then
	crashwin = window.create(therealOGterm,12,8,w/2+10,h-12)
	bigfont.writeOn(therealOGterm,3,"L",w-40,math.ceil(h/2)-10)
else
	crashwin = window.create(therealOGterm,4,5,w-8,h-5)
end
term.redirect(crashwin)
term.setBackgroundColor(colors.blue)
term.clear()
if w > 150 and h > 50 and fs.exists("LevelOS/assets/QR_Code.limg") then
	local qrcode = textutils.unserialize(fread("LevelOS/assets/QR_Code.limg"))
	bigfont.hugePrint(":(")
	wordwrap("Your PC ran into a problem and needs to restart. Please press space to continue.")
	print("\n\n\n\n\n\n")
	local x,y = term.getCursorPos()
	render(qrcode[1],x,y)
	term.setCursorPos(x+22,y)
	bPrint("For more information about this issue and possible fixes, visit https://discord.gg/H2UyJXe")
	term.setCursorPos(x+22,y+(#qrcode[1]-3))
	bPrint("If you contact support, give them this info:")
	term.setCursorPos(x+22,y+(#qrcode[1]-1))
	bPrint(u[2])
else
	bigfont.bigPrint(":(")
	print("\nYour PC ran into a problem and needs to restart. Please press space to continue.")
	print("\nError:")
	print(u[2])
end
os.sleep(1)
while true do
	local e = {os.pullEvent("key")}
	if e[2] == keys.space then
		os.reboot()
	end
end

-- TEMP FOR DEBUGGING
term.setBackgroundColor(colors.black) term.clear() term.setCursorPos(1,1)