-- LevelOS Utils

if not unpack then
    _G.unpack = table.unpack
end

local write = term.write

if _G.lUtils == nil then
	_G.lUtils = {}
end


if _G.lOS == nil then
	_G.lOS = {}
end

function lUtils.RGBToGrayscale( r, g, b )
	local gr = (r + g + b) / 3
	return gr,gr,gr
end

local function getLines(str)
    local lines = {}
    local w = 0
    for potato in str:gmatch("[^\n]+") do
        table.insert(lines,potato)
        if #potato > w then
            w = #potato
        end
    end
    return lines,w
end


function lUtils.renderImg(spr,x,y,format)
    local format
    if not format then
        if type(spr) == "string" then
            format = "nfp"
        elseif type(spr) == "table" then
            format = "lImg"
        end
    end
    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)))
            else
                term.setCursorPos(x,y+(l-1))
            end]]
            local line
            if y then
                line = y+(l-1)
            end
            term.setCursorPos(x or math.ceil(w/2)-math.floor(sW/2), line or (math.ceil(h/2)-math.floor(sH/2)+(l-1)))
            local bl = {}
            bl[1] = spr[l][1]
            bl[2] = string.gsub(spr[l][2],"T",lUtils.toBlit(term.getBackgroundColor()))
            bl[3] = string.gsub(spr[l][3],"T",lUtils.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
            local line
            if y then
                line = y+(l-1)
            end
            term.setCursorPos(x or math.ceil(w/2)-math.floor(sW/2), line or math.ceil(h/2)-math.floor(sH/2)+(l-1))
            term.blit(string.rep(" ",#lines[l]),lines[l],lines[l])
        end
    end
end


function lUtils.getDrawingCharacter(...)
    local e=0
    for t=1,5 do
        if arg[t] then
            e=e+2^(t-1)
        end
    end
    return {char = string.char(arg[6] and 159-e or 128+e), inverted = not not arg[6]}
end

lUtils.asset = {}
function lUtils.asset.load(filename)
	if filename and type(filename) == "string" and fs.exists(filename) then
        return textutils.unserialize(lUtils.fread(filename))
    else
        return false
    end
end


function lUtils.asset.save(asset,filename)
    if type(asset) == "table" and not fs.isDir(filename) then
        return lUtils.fwrite(filename,textutils.serialize(asset))
    else
        return false
    end
end


function lUtils.HSVToRGB( hue, saturation, value )
	-- Returns the RGB equivalent of the given HSV-defined color
	-- (adapted from some code found around the web)

	-- If it's achromatic, just return the value
	if saturation == 0 then
		return value,value,value
	end

	-- Get the hue sector
	local hue_sector = math.floor( hue / 60 )
	local hue_sector_offset = ( hue / 60 ) - hue_sector

	local p = value * ( 1 - saturation )
	local q = value * ( 1 - saturation * hue_sector_offset )
	local t = value * ( 1 - saturation * ( 1 - hue_sector_offset ) )

	if hue_sector == 0 then
		return value, t, p
	elseif hue_sector == 1 then
		return q, value, p
	elseif hue_sector == 2 then
		return p, value, t
	elseif hue_sector == 3 then
		return p, q, value
	elseif hue_sector == 4 then
		return t, p, value
	elseif hue_sector == 5 then
		return value, p, q
	end
end


function lUtils.RGBToHSV( red, green, blue )

	local hue, saturation, value

	local min_value = math.min( red, green, blue )
	local max_value = math.max( red, green, blue )

	value = max_value

	local value_delta = max_value - min_value

	-- If the color is not black
	if max_value ~= 0 then
		saturation = value_delta / max_value

	-- If the color is purely black
	else
		saturation = 0
		hue = -1
		return hue, saturation, value
	end

	if red == max_value then
		hue = ( green - blue ) / value_delta
	elseif green == max_value then
		hue = 2 + ( blue - red ) / value_delta
	else
		hue = 4 + ( red - green ) / value_delta
	end

	hue = hue * 60
	if hue < 0 then
		hue = hue + 360
	end

	return hue, saturation, value
end


lUtils.graphics = {}
local g = lUtils.graphics
function g.button(txt,x1,y1,x2,y2,func,border,fg1,bg1,fg2,bg2,bcolor)
    if not y2 then
        return nil
    end
    if not bg1 then
        bg1 = colors.black
    end
    if not fg1 then
        fg1 = colors.white
    end
    if not bcolor then
        bcolor = colors.gray
    end
    if not fg2 then
        fg2 = fg1
    end
    if not bg2 then
        bg2 = colors.gray
    end
    if not border then
        border = true
    end
    local btn = {txt=txt,x1=x1,y1=y1,x2=x2,y2=y2,func=func,border=border,selected=false,colors={bg1=bg1,fg1=fg1,bg2=bg2,fg2=fg2,border=bcolor}}
    function btn.render(...)
        local e = {...}
        local selected = false
        --term.setTextColor(colors.fg1)
        --term.setBackgroundColor(colors.bg1)
        local abc = {}
        if e[1] == "mouse_click" or e[1] == "mouse_up" then
            if e[3] >= btn.x1 and e[4] >= btn.y1 and e[3] <= btn.x2 and e[4] <= btn.y2 then
                if e[1] == "mouse_click" then
                    btn.selected = true
                elseif e[1] == "mouse_up" then
                    if not btn.func then
                        abc[1] = true
                    else
                        abc = {btn.func()}
                    end
                end
            end
        end
        if e[1] == "mouse_up" then
            btn.selected = false
        end
        if btn.selected then
            term.setBackgroundColor(btn.colors.bg2)
        else
            term.setBackgroundColor(btn.colors.bg1)
        end
        term.setTextColor(btn.colors.border)
        if btn.border == true then
            lUtils.border(btn.x1,btn.y1,btn.x2,btn.y2)
        else
            lOS.boxClear(btn.x1,btn.y1,btn.x2,btn.y2)
        end
        if btn.selected then
            term.setTextColor(btn.colors.fg2)
        else
            term.setTextColor(btn.colors.fg1)
        end
        lUtils.textbox(btn.txt,btn.x1+1,btn.y1+1,btn.x2-1,btn.y2-1)
        return unpack(abc)
    end
    return btn
end


    function lOS.boxClear(tx1,ty1,tx2,ty2)
    	local x1,y1,x2,y2 = tx1,ty1,tx2,ty2
   	if x1 > x2 then
		x2,x1 = x1,x2
	end
	if y1 > y2 then
		y2,y1 = y1,y2
	end
        clearline = ""
        for t=x1,x2 do
            clearline = clearline.." "
        end
        for l=y1,y2 do
            term.setCursorPos(x1,l)
            term.write(clearline)
        end
    end


local function run2( _tEnv, _sPath, ... )
    local tArgs = table.pack( ... )
    local tEnv = _tEnv
    setmetatable( tEnv, { __index = _G } )
    local fnFile,err
    if fs.exists("window") then
        fnFile, err = loadfile( _sPath, tEnv )
    else
        fnFile, err = loadfile( _sPath, nil, tEnv )
    end
    if fnFile then
        local returned = {pcall( function()
            return table.unpack({fnFile( table.unpack( tArgs, 1, tArgs.n ) )})
        end )}
		local ok, err = returned[1],returned[2]
		table.remove(returned,1)
        if not ok then
			table.remove(returned,1)
            if err and err ~= "" then
                --printError( err )
            end
            return false,err,table.unpack(returned)
        end
        return true,table.unpack(returned)
    end
    if err and err ~= "" then
        --printError( err )
    end
    return false,err
end



local function createShellEnv( sDir , tWindow , sPath)
    local tEnv = {}
    tEnv[ "shell" ] = lUtils.instantiate(shell)
    if sPath then
        tEnv.shell.getRunningProgram = function() return sPath end
    end
    tEnv[ "multishell" ] = multishell

    local package = {}
    package.loaded = {
        _G = _G,
        bit32 = bit32,
        coroutine = coroutine,
        math = math,
        package = package,
        string = string,
        table = table,
    }
    package.path = "?;?.lua;?/init.lua;/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua"
    if turtle then
        package.path = package.path..";/rom/modules/turtle/?;/rom/modules/turtle/?.lua;/rom/modules/turtle/?/init.lua"
    elseif command then
        package.path = package.path..";/rom/modules/command/?;/rom/modules/command/?.lua;/rom/modules/command/?/init.lua"
    end
    package.config = "/\n;\n?\n!\n-"
    package.preload = {}
    package.loaders = {
        function( name )
            if package.preload[name] then
                return package.preload[name]
            else
                return nil, "no field package.preload['" .. name .. "']"
            end
        end,
        function( name )
            local fname = string.gsub(name, "%.", "/")
            local sError = ""
            for pattern in string.gmatch(package.path, "[^;]+") do
                local sPath = string.gsub(pattern, "%?", fname)
                if sPath:sub(1,1) ~= "/" then
                    sPath = fs.combine(sDir, sPath)
                end
                if fs.exists(sPath) and not fs.isDir(sPath) then
                    local fnFile, sError = loadfile( sPath, tEnv )
                    if fnFile then
                        return fnFile, sPath
                    else
                        return nil, sError
                    end
                else
                    if #sError > 0 then
                        sError = sError .. "\n"
                    end
                    sError = sError .. "no file '" .. sPath .. "'"
                end
            end
            return nil, sError
        end
    }

    local sentinel = {}
    local function require( name )
        if type( name ) ~= "string" then
            error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 2 )
        end
        if package.loaded[name] == sentinel then
            error("Loop detected requiring '" .. name .. "'", 0)
        end
        if package.loaded[name] then
            return package.loaded[name]
        end

        local sError = "Error loading module '" .. name .. "':"
        for n,searcher in ipairs(package.loaders) do
            local loader, err = searcher(name)
            if loader then
                package.loaded[name] = sentinel
                local result = loader( err )
                if result ~= nil then
                    package.loaded[name] = result
                    return result
                else
                    package.loaded[name] = true
                    return true
                end
            else
                sError = sError .. "\n" .. err
            end
        end
        error(sError, 2)
    end

    tEnv["package"] = package
    tEnv["require"] = require

	tEnv["LevelOS"] = {self={window=tWindow}}

	local lAPI = tEnv["LevelOS"]
	--local s = tEnv["shell"]
    --local ms = tEnv["multishell"]
    --TEMP DISABLED
	local s = {}
	local ms = {}

    
    
	if not lOS.oldterm then
		lOS.oldterm = oldterm
		if not oldterm then
			lOS.oldterm = term.native() -- DANGEROUS
		end
	end
	local w,h = lOS.oldterm.getSize()
	if tWindow ~= nil and lOS.wins ~= nil then
		local win = tWindow
		local function setWin( width, height, mode )
			if win.win ~= nil and width ~= nil and height ~= nil then
				local x,y = win.win.getPosition()
				win.win.reposition(x,y,width,height)
                if mode then
                    win.winMode = mode
                end
			elseif win.win == nil and mode ~= "none" then
				win.win = window.create(lOS.oldterm,math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
				lOS.wins[#lOS.wins+1] = win
                if mode then
                    win.winMode = mode
                end
			end
			if mode == "none" and win.win then
				for t=1,#lOS.wins do
					if lOS.wins[t] ~= nil and lOS.wins[t] == win then
						table.remove(lOS.wins,t)
						break
					end
				end
				term.redirect(oldterm)
				win.win = nil
			end
		end
		local function pullEvent()
			win.events = "all"
			local e = {os.pullEventRaw()}
			win.events = "default"
			return table.unpack(e)
		end
		local function setTitle(title)
			if type(title) == "string" and title ~= "" then
				win.title = title
				return true
			else
				return false
			end
		end
		local function maximize()
			if win.win ~= nil then
				local w,h = lOS.wAll.getSize()
				win.win.reposition(1,2,w,h-3)
				return true
			end
			return false
		end
		lAPI.pullEvent = pullEvent
		lAPI.setWin = setWin
		lAPI.setTitle = setTitle
		lAPI.maximize = maximize
		tWindow.env = tEnv
		local srun = tEnv.shell.run
        
        function ms.launch(env, path, ...)
            local args = {...}
            local function func(win)
                local oEnv = createShellEnv(fs.getDir(path),win)
                env["LevelOS"] = oEnv["LevelOS"]
                return run2(env, path, table.unpack(args))
            end
            lOS.newWin(func,path)
        end

        function ms.getCount()
            return #lOS.wins
        end

        function ms.setTitle(n, title)
            lOS.wins[n].title = title
        end

        function ms.setFocus(n)
            if n >= 1 and n <= #lOS.wins then
                local w = lOS.wins[n]
                table.remove(lOS.wins,n)
                table.insert(lOS.wins,w)
                return true
            else
                return false
            end
        end

        function ms.getTitle(n)
            if n >= 1 and n <= #lOS.wins then
                return lOS.wins[n].title
            end
        end

        function s.execute(command, ...)

            local sPath = s.resolveProgram(command)
            if sPath ~= nil then
                local sTitle = lUtils.getFileName(sPath)
                sTitle = (sTitle:gsub("^%l", string.upper))
                lAPI.setTitle(sTitle)

                local sDir = fs.getDir(sPath)
                local env = createShellEnv(sDir,lAPI.self.window)
                env.arg = { [0] = command, ... }
                local result = run2(env, sPath, ...)

                return result
            else
                printError("No such program")
                return false
            end
        end
        
		--function tEnv.shell.run(...)
			--if ({...})[2] and fs.exists(({...})[2]) then
				--setTitle(lUtils.getFileName(({...})[1]).." - "..lUtils.getFileName(({...})[2]))
			--else
				--setTitle(lUtils.getFileName(({...})[1]))
			--end
			--srun(...)
		--end

		-- do the above once we port everything to lOS.run
		
	end

    return tEnv
end


lOS.createShellEnv = createShellEnv




lUtils.shapescape = {}


local align = {}
function align.left(slide,offset)
	local w,h = term.getSize()
	return offset
end
function align.right(slide,offset)
	local w,h = term.getSize()
	return w-offset
end
function align.top(slide,offset)
	local w,h = term.getSize()
	return offset
end
function align.bottom(slide,offset)
	local w,h = term.getSize()
	return h-offset
end
function align.center(slide,offset,vert)
	local w,h = term.getSize()
	if vert then
		return math.ceil(h/2)-offset
	else
		return math.ceil(w/2)-offset
	end
end


local generic = {}

function generic.align(obj)
	--if obj.ox1 == nil or obj.oy1 == nil then
		--obj.ox1,obj.oy1,obj.ox2,obj.oy2 = obj.x1,obj.y1,obj.x2,obj.y2
	--end
	local w,h = term.getSize()
	if obj.snap.Left == "Snap right" then
		if not obj.ox1 then
			obj.ox1 = w-obj.x1
		end
		obj.x1 = align.right(slide,obj.ox1)
	elseif obj.snap.Left == "Snap center" then
		if not obj.ox1 then
			obj.ox1 = math.ceil(w/2)-obj.x1
		end
		obj.x1 = align.center(slide,obj.ox1)
	else
		obj.ox1 = nil
	end
	if obj.snap.Right == "Snap right" then
		if not obj.ox2 then
			obj.ox2 = w-obj.x2
		end
		obj.x2 = align.right(slide,obj.ox2)
	elseif obj.snap.Right == "Snap center" then
		if not obj.ox2 then
			obj.ox2 = math.ceil(w/2)-obj.x2
		end
		obj.x2 = align.center(slide,obj.ox2)
	else
		obj.ox2 = nil
	end
	if obj.snap.Top == "Snap bottom" then
		if not obj.oy1 then
			obj.oy1 = h-obj.y1
		end
		obj.y1 = align.bottom(slide,obj.oy1)
	elseif obj.snap.Top == "Snap center" then
		if not obj.oy1 then
			obj.oy1 = math.ceil(h/2)-obj.y1
		end
		obj.y1 = align.center(slide,obj.oy1,true)
	else
		obj.oy1 = nil
	end
	if obj.snap.Bottom == "Snap bottom" then
		if not obj.oy2 then
			obj.oy2 = h-obj.y2
		end
		obj.y2 = align.bottom(slide,obj.oy2)
	elseif obj.snap.Bottom == "Snap center" then
		if not obj.oy2 then
			obj.oy2 = math.ceil(h/2)-obj.y2
		end
		obj.y2 = align.center(slide,obj.oy2,true)
	else
		obj.oy2 = nil
	end
end


function lUtils.shapescape.addScript(tShp,id,ev,assets,LevelOS,slides)
	if not tShp.event then
		tShp.event = {}
	end
	
	-- run script with environment
	local function getEnv(tShape)
		local tempWin
		if LevelOS then
			tempWin = LevelOS.self.window
		end
		local tEnv = createShellEnv("",tempWin)
		setmetatable(tEnv,{__index=_G})
		tEnv.self = tShape
		return tEnv
	end
	local function getSlide()
		return slides[slides.cSlide]
	end
	local function getSlides()
		return slides
	end
	local function setSlide(n)
		if slides[n] then
			slides.cSlide = n
			return true
		else
			return false
		end
	end
	local function exit()
		slides.stop = true
	end
	local getEvent = function() return end
	local tEnv = getEnv(tShp)
	tEnv.shapescape = {getEvent=getEvent,getSlide=getSlide,getSlides=getSlides,setSlide=setSlide,exit=exit}
	tShp.tEnv = tEnv
	local sFunc = load(assets[id].content,assets[id].name,"bt",tEnv)
    if ev == "Coroutine" and tShp.type == "window" then
        tShp.event[ev] = {function(tShape,e) tShape.tEnv.shapescape.getEvent = function() return unpack(e) end local ok,err = pcall(sFunc) if not ok then print(err) end end,id}
    else
	   tShp.event[ev] = {function(tShape,e) tShape.tEnv.shapescape.getEvent = function() return unpack(e) end sFunc() end,id}
    end
end

function lUtils.shapescape.renderSlide(slide,static)
	local oterm = term.current()
	term.redirect(slide.win)
	term.setBackgroundColor(colors.white)
	term.clear()
    local cursor
	for o=1,#slide.objs do
		--[[if slide.objs[o].snap then
			generic.align(slide.objs[o])
		end]]
		if slide.objs[o].type == "rect" or slide.objs[o].type == "text" or slide.objs[o].type == "window" then
			local s = slide.objs[o]
			if not slide.objs[o].render then
				local self = slide.objs[o]
				if slide.objs[o].type == "window" then
                    if not static then
    					function self.render()
    						if self.snap then
    							generic.align(self)
    						end
    						if self.color ~= 0 then
    							if not self.window then
    								self.window = window.create(term.current(),self.x1,self.y1,(self.x2-self.x1)+1,(self.y2-self.y1)+1,false)
    							end
    							local x,y = self.window.getPosition()
    							local w,h = self.window.getSize()
    							if x ~= self.x1 or y ~= self.y1 or w ~= (self.x2-self.x1)+1 or h ~= (self.y2-self.y1)+1 then
    								self.window.reposition(self.x1,self.y1,(self.x2-self.x1)+1,(self.y2-self.y1)+1)
    							end
    							for l=1,(self.y2-self.y1)+1 do
    								term.setCursorPos(self.x1,self.y1+(l-1))
    								term.blit(self.window.getLine(l))
    							end
    						end
    					end
                    else
                        local lines
                        local function genLines()
                            lines = {}
                            --[[local fg = lUtils.toBlit(self.color or colors.white)
                            local bg = lUtils.toBlit(self.color or colors.white)]]
                            local fg,bg
                            if self.color ~= colors.black then
                                fg = lUtils.toBlit(self.color)
                                bg = lUtils.toBlit(self.border.color ~= nil and self.border.color or colors.black)
                                if bg == nil then
                                    bg = lUtils.toBlit(colors.black)
                                end
                            else
                                fg = lUtils.toBlit(self.color)
                                bg = lUtils.toBlit(self.border.color ~= nil and self.border.color or colors.white)
                                if bg == nil then
                                    bg = lUtils.toBlit(colors.white)
                                end
                            end
                            for y=self.y1,self.y2 do
                                lines[#lines+1] = {"","",""}
                                for x=self.x1,self.x2 do
                                    lines[#lines][1] = lines[#lines][1]..string.char(math.random(129,159))
                                    if math.random(1,2) == 2 then
                                        lines[#lines][2] = lines[#lines][2]..bg
                                        lines[#lines][3] = lines[#lines][3]..fg
                                    else
                                        lines[#lines][2] = lines[#lines][2]..fg
                                        lines[#lines][3] = lines[#lines][3]..bg
                                    end
                                end
                            end
                        end
                        genLines()
                        local x1,y1,x2,y2 = self.x1,self.y1,self.x2,self.y2
                        local c1,c2 = self.color,self.border.color
                        function self.render()
                            if self.snap then
                                generic.align(self)
                            end
                            if x1 ~= self.x1 or y1 ~= self.y1 or x2 ~= self.x2 or y2 ~= self.y2 or c1 ~= self.color or c2 ~= self.border.color then
                                x1,y1,x2,y2 = self.x1,self.y1,self.x2,self.y2
                                c1,c2 = self.color,self.border.color
                                genLines()
                            end
                            for l=1,#lines do
                                term.setCursorPos(self.x1,self.y1+(l-1))
                                term.blit(unpack(lines[l]))
                            end
                        end
                    end
				else
					function self.render()
						if self.snap then
							generic.align(self)
						end
						if self.color ~= 0 then
							term.setBackgroundColor(self.color)
							--term.setCursorPos(self.x1,self.y1)
                            for y=self.y1,self.y2 do
                                term.setCursorPos(self.x1,y)
                                term.write(string.rep(" ",self.x2-(self.x1-1)))
                            end
							--lOS.boxClear(self.x1,self.y1,self.x2,self.y2)
						end
						if self.border and self.border.color ~= 0 then
							term.setTextColor(self.border.color)
							lUtils.border(self.x1,self.y1,self.x2,self.y2,"transparent")
						end
                        if self.image then
                            lUtils.renderImg(self.image,self.x1,self.y1)
                        end
						if self.txt then
							if self.color == 0 then
								term.setBackgroundColor(colors.white)
							else
								term.setBackgroundColor(self.color)
							end
							term.setTextColor(self.txtcolor)
							if self.border and self.border.color ~= 0 and self.y2 >= self.y1+2 then
								lUtils.textbox(self.txt,self.x1+1,self.y1+1,self.x2-1,self.y2-1,true)
							else
								if self.txt == "" and not self.input then
									lUtils.textbox("...",self.x1,self.y1,self.x2,self.y2,true)
								else
									lUtils.textbox(self.txt,self.x1,self.y1,self.x2,self.y2,true)
								end
							end
						end
					end
				end
			end
			s.render()

			if not slide.objs[o].update then
				local self = slide.objs[o]
				function self.update(...)
					local e = {...}
					if e[1] == "mouse_click" or e[1] == "mouse_up" then
						if e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then
							if self.event[e[1]] and self.event[e[1]][1] then
								self.event[e[1]][1](self,e)
							end
						end
						if e[1] == "mouse_up" and self.selected then
							self.selected = false
						end
					end
					if self.event.update then
						-- dt when i can
						self.event.update[1](self,e)
					end
					if not self.coroutine and self.event.Coroutine[2] >= 0 then
						self.coroutine = coroutine.create(function() self.event.Coroutine[1](self,{}) end)
					end
					if self.coroutine then
						local oterm = term.current()
						if self.window then
							term.redirect(self.window)
							if string.find(e[1],"mouse") then
								e[3] = e[3]-(self.x1-1)
								e[4] = e[4]-(self.y1-1)
							end
						end
						coroutine.resume(self.coroutine,unpack(e))
						-- if blink was enabled set cursor back to this window after everything blablabla
						term.redirect(oterm)

					end
				end
			end
		elseif slide.objs[o].type == "triangle" then

		end
		if not slide.objs[o].remove then
			local self = slide.objs[o]
			local sl = slide
			slide.objs[o].remove = function()
				for s=1,#sl.objs do
					if sl.objs[s] == self then
						sl.objs[s] = nil
						return true
					end
				end
			end
		end
	end
	term.redirect(oterm)
end


function lUtils.shapescape.run(slides)
	local oterm = term.current()
	if not slides.cSlide then
		slides.cSlide = 1
	end
	term.setBackgroundColor(colors.white)
	local sWin = window.create(term.current(),1,1,term.getSize())
	--sWin.setVisible(false)
	sWin.setBackgroundColor(colors.white)
	sWin.setTextColor(colors.black)
	sWin.clear()
	slides[slides.cSlide].win = sWin
	lUtils.shapescape.renderSlide(slides[slides.cSlide])
	while not slides.stop do
        local cursor
		local s = slides[slides.cSlide]
		local e = {os.pullEvent()}
		local cSlide = slides.cSlide
		sWin.reposition(1,1,term.getSize())
		term.redirect(sWin)
        term.setCursorBlink(false)
		lUtils.shapescape.renderSlide(s)
		for t=1,#s.objs do
			local o = s.objs[t]
			if o and o.update then
				o.update(unpack(e))
                if o.window and o.window.getCursorBlink() == true then
                    cursor = {pos={o.window.getCursorPos()},color=o.window.getTextColor()}
                    cursor.pos[1] = o.x1+(cursor.pos[1]-1)
                    cursor.pos[2] = o.y1+(cursor.pos[2]-1)
                end
			end
		end
		if cSlide ~= slides.cSlide then
			sWin.setBackgroundColor(colors.white)
			sWin.setTextColor(colors.black)
			sWin.clear()
			slides[slides.cSlide].win = sWin
			lUtils.shapescape.renderSlide(slides[slides.cSlide])
		end
        if cursor then
            term.setCursorPos(unpack(cursor.pos))
            term.setTextColor(cursor.color)
            term.setCursorBlink(true)
        end
        -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa it doesnt WORK
		term.redirect(oterm)
-- invis rendering during update broke eberythinf so now its notv invis
		--for t=1,({sWin.getSize()})[2] do
			--term.setCursorPos(1,t)
			--term.blit(sWin.getLine(t))
		--end
	end
end
		


function lUtils.randStr(keyLength,num,symb)
	local upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	local lowerCase = "abcdefghijklmnopqrstuvwxyz"
	local numbers = "0123456789"
	local symbols = "!@#$%&()*+-,./\:;<=>?^[]{}"
	
	local characterSet = upperCase .. lowerCase
	if num then
		characterSet = characterSet..numbers
	end
	if symb then
		characterSet = characterSet..symbols
	end
	
	local output = ""
	
	for i = 1, keyLength do
	    local rand = math.random(#characterSet)
	    output = output .. string.sub(characterSet, rand, rand)
	end
	return output
end


function lUtils.logout()
	lOS.userID = nil
	lOS.username = nil
	if fs.exists("LevelOS/data/account2.txt") then
		fs.delete("LevelOS/data/account2.txt")
	end
    lOS.notification("You are now logged out of Leveloper Services.")
end


function lUtils.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,thing
end


function lUtils.compare(a, b)
  for k,v in pairs(a) do 
    if (type(v) == "table" and type(b[k]) == "table" and not lUtils.compare(b[k], v)) or b[k] ~= v then return false end 
  end
  for k,v in pairs(b) do 
    if (type(v) == "table" and type(a[k]) == "table" and not lUtils.compare(a[k], v)) or a[k] ~= v then return false end 
  end
  return true
end


function lUtils.centerText(text)
	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)
end



function lUtils.outline(x1,y1,x2,y2)
	local c1 = term.getTextColor()
	local c2 = term.getBackgroundColor()
	term.setCursorPos(x1,y1)
	term.setBackgroundColor(c1)
	term.setTextColor(c2)
	local a
	for a=x1,x2 do
		term.write("\143")
	end
	for a=y1,y2 do
		term.setCursorPos(x1,y1-1+a)
		term.write("\149")
	end
	term.setBackgroundColor(c2)
	term.setTextColor(c1)
	for a=y1,y2 do
		term.setCursorPos(x2,y1-1+a)
		term.write("\149")
	end
	term.setCursorPos(x1,y2)
	for a=x1,x2 do
		term.write("\131")
	end
end



function lUtils.border(x1,y1,x2,y2,mode)
	term.setCursorPos(x1,y1)
	local w,h = x2-(x1-1),y2-(y1-1)
	local bg,fg = term.getBackgroundColor(),term.getTextColor()
	local inved = false
	local function inv()
		if not inved then
			term.setBackgroundColor(fg)
			term.setTextColor(bg)
			inved = true
		else
			term.setBackgroundColor(bg)
			term.setTextColor(fg)
			inved = false
		end
	end
	local function setBG()
		if mode and mode == "transparent" then
			term.setBackgroundColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
		end
	end
	local function setFG()
		if mode and mode == "transparent" then
			term.setTextColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
		end
	end
	setBG()
	term.write("\151")
	if mode and mode == "transparent" then
		for x=1,w-2 do
			setBG()
			term.write("\131")
		end
	else
		term.write(string.rep("\131",w-2))
	end
	inv()
	setFG()
	term.write("\148")
	for y=y1+1,y2-1 do
		term.setCursorPos(x2,y)
		setFG()
		term.write("\149")
		inv()
		term.setCursorPos(x1,y)
		setBG()
		term.write("\149")
		if mode and mode == "fill" then
			term.write(string.rep(" ",w-2))
		end
		inv()
	end
	term.setCursorPos(x1,y2)
	setFG()
	term.write("\138")
	if mode and mode == "transparent" then
		for x=1,w-2 do
			setFG()
			term.write("\143")
		end
	else
		term.write(string.rep("\143",w-2))
	end
	setFG()
	term.write("\133")
	inv()
end


function lUtils.spairs(t, order)
    -- collect the keys
    local keys = {}
    for k in pairs(t) do keys[#keys+1] = k end

    -- if order function given, sort by it by passing the table and keys a, b,
    -- otherwise just sort the keys 
    if order then
        table.sort(keys, function(a,b) return order(t, a, b) end)
    else
        table.sort(keys)
    end

    -- return the iterator function
    local i = 0
    return function()
        i = i + 1
        if keys[i] then
            return keys[i], t[keys[i]]
        end
    end
end


function lUtils.splitStr(str,pat)
   local t = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = "(.-)" .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= "" then
         table.insert(t,cap)
      end
      last_end = e+1
      s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
      cap = str:sub(last_end)
      table.insert(t, cap)
   end
   return t
end



function lUtils.explorer(path,mode)
	if path == nil or path == "" then
		path = "/"
	end
	local title = "Explorer"
	if mode == "SelFile" then
		title = "Select File"
	elseif mode == "SelFolder" then
		title = "Select Folder"
	end
	local w,h = term.getSize()
	a = {lUtils.openWin("Select File","LevelOS/explorer.lua "..path.." "..mode,math.ceil(w/2)-24,math.ceil(h/2)-8,48,16,true,false)}
	if a[1] == false then return false end
	table.remove(a,1)
	if type(a[1]) == "table" then
		a = a[1]
	end
	return table.unpack(a)
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



function lUtils.toColor(theblit)
	return to_colors[theblit] or nil
end



function lUtils.toBlit(thecolor)
	return to_blit[thecolor] or nil
end



function lUtils.instantiate(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == "table" then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[lUtils.instantiate(orig_key)] = lUtils.instantiate(orig_value)
        end
        setmetatable(copy, lUtils.instantiate(getmetatable(orig)))
    else
        copy = orig
    end
    return copy
end



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





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



function lOS.eventdelay()
	local oldtime = os.time()
	local timer = os.startTimer(0)
	while true do
		event,id = os.pullEvent("timer")
		if id == timer then
			local newtime = os.time()
			return (newtime-oldtime)*50000
		end
	end
end



function lUtils.getFileType(filename)
	if string.find(filename,"%.%a+$") == nil then
		return ""
	else
		return string.sub(filename,string.find(filename,"%.%a+$"))
	end
end



function lUtils.getFileName(filename,ext)
	local f = filename
	if string.find(filename," ") then
		f = string.sub(filename,1,({string.find(filename," ")})[1]-1)
	end
	f = fs.getName(f)
	if not ext then
		f = string.sub(f,1,string.len(f)-string.len(lUtils.getFileType(f)))
	end
	return string.gsub(f,"_"," ")
end



function lUtils.getFilePath(filename)
	local f = filename
	if string.find(filename," ") then
		f = string.sub(filename,1,({string.find(filename," ")})[1]-1)
	end
	return f
end

local strength

function lOS.checkinternet()
	strenth = 0
	local oldtime = os.time()
	a = http.get("https://level.eu5.net/ping.php")
	if a == nil or a == false then
		strength = 0
	else
		newtime = os.time()
		if newtime-oldtime < 0.005 then
			strength = 3
		elseif newtime-oldtime < 0.01 then
			strength = 2
		elseif newtime-oldtime >= 0.01 then
			strength = 1
		end
		return strength,newtime-oldtime
	end
	return 0,0
end

function lOS.getInternet()
    return strength or 0
end

function lOS.run( _sCommand, ... )
    local sPath = shell.resolveProgram( _sCommand )
    if sPath ~= nil then
		local tWindow = {}
		local tArgs = {...}
		--term.write(textutils.serialize(tArgs))
		if type(tArgs[1]) == "table" then
			tWindow = tArgs[1]
			table.remove(tArgs,1)
		else
			tWindow = nil
		end
		-- set window title
        local sDir = fs.getDir( sPath )
        local result = {run2( createShellEnv( sDir, tWindow, sPath ), sPath, table.unpack(tArgs) )}
        return table.unpack(result)
    else
        return false,"No such program"
    end
end

function lOS.newWin(func,rPath)
    -- fuck me
end

function lOS.execute(path)
	local tPath = ""
	rPath = string.sub(path,string.find(path,"%S+"))
	local b,e = string.find(path,"%S+")
	tPath = string.sub(path,e+2,string.len(path))
	local thingy = {}
	for i in string.gmatch(tPath,"%S+") do
		if i ~= nil and i ~= "" then
			thingy[#thingy+1] = i
		end
	end
	local w,h = oldterm.getSize()
	--if tPath ~= "" then
		local tempthing = #lOS.oWins+1
		lOS.oWins[tempthing] = {window.create(oldterm,1,2,w,h-3),nil,fullscreen=false,minimized=false,filepath=rPath,icon={string.sub(fs.getName(rPath),1,3),string.sub(fs.getName(rPath),4,6)},ran=false}
		local function tempfunc()
			local a = {lOS.run(rPath,lOS.oWins[tempthing],table.unpack(thingy))}
			if a[1] == false then
				local b = {lUtils.popup(fs.getName(rPath),fs.getName(rPath).." has stopped working.",25,9,{"OK","View Error"})}
				if b[3] == "View Error" then
					lUtils.popup(fs.getName(rPath),a[2],25,11,{"OK"})
				end
			end
		end
		lOS.oWins[tempthing][2] = coroutine.create(tempfunc)
		--lOS.oWins[#lOS.oWins+1] = {window.create(oldterm,1,2,w,h-3),coroutine.create(loadstring('local a = {lOS.run("'..rPath..'",lOS.oWins['..tostring(#lOS.oWins+1)..'],"'..tPath..'")} if a[1] == false then local b = {lUtils.popup(fs.getName("'..rPath..'"),fs.getName("'..rPath..'").." has stopped working.",25,9,{"OK","View Error"})} if b[3] == "View Error" then lUtils.popup(fs.getName("'..rPath..'"),a[2],25,11,{"OK"}) end end')),fullscreen=false,minimized=false,filepath=rPath,icon={string.sub(fs.getName(rPath),1,3),string.sub(fs.getName(rPath),4,6)},ran=false}
	--else
		--lOS.oWins[#lOS.oWins+1] = {window.create(oldterm,1,2,w,h-3),coroutine.create(loadstring('local a = {lOS.run("'..rPath..'",lOS.oWins['..tostring(#lOS.oWins+1)..'])} if a[1] == false then local b = {lUtils.popup(fs.getName("'..rPath..'"),fs.getName("'..rPath..'").." has stopped working.",25,9,{"OK","View Error"})} if b[3] == "View Error" then lUtils.popup(fs.getName("'..rPath..'"),a[2],25,11,{"OK"}) end end')),fullscreen=false,minimized=false,filepath=rPath,icon={string.sub(fs.getName(rPath),1,3),string.sub(fs.getName(rPath),4,6)},ran=false}
	--end
	_G.cWin = #lOS.oWins
end



function lOS.searchfor(arg,path)
	local files = fs.list(path)
	local folders = {}
	local result = {}
	local p = ""
	if path ~= "" then
		p = path.."/"
	else
		p = ""
	end
	for t=1,#files do
		if fs.isDir(p..files[t]) then
			folders[#folders+1] = p..files[t]
		elseif string.find(string.lower(files[t]),string.lower(arg)) then
			result[#result+1] = p..files[t]
		end
	end
	for t=1,#folders do
		local a = lOS.searchfor(arg,folders[t])
		for b=1,#a do
			result[#result+1] = a[b]
		end
	end
	return result
end



function lOS.search(keyword,x,y,w,h,searchfile,animation)
	local lines = {}
	local slp = function() os.sleep(0) end
	for t=1,h do
		lines[t] = {"","",""}
	end
	lines[h] = {"\138","0","8"}
	lines[h-1] = {"\149","8","0"}
	lines[h-2] = {"\151","8","0"}
	lines[1] = {"\151","8","7"}
	lines[2] = {"\149","8","7"}
	for t=1,w-2 do
		if t < math.floor(w/2) then
			lines[1][1] = lines[1][1].."\131"
			lines[1][2] = lines[1][2].."8"
			lines[1][3] = lines[1][3].."7"
			lines[2][1] = lines[2][1].." "
			lines[2][2] = lines[2][2].."0"
			lines[2][3] = lines[2][3].."7"
		else
			lines[1][1] = lines[1][1].."\131"
			lines[1][2] = lines[1][2].."8"
			lines[1][3] = lines[1][3].."0"
			lines[2][1] = lines[2][1].." "
			lines[2][2] = lines[2][2].."0"
			lines[2][3] = lines[2][3].."0"
		end
		lines[h][1] = lines[h][1].."\143"
		lines[h][2] = lines[h][2].."0"
		lines[h][3] = lines[h][3].."8"
		lines[h-1][1] = lines[h-1][1].." "
		lines[h-1][2] = lines[h-1][2].."f"
		lines[h-1][3] = lines[h-1][3].."0"
		lines[h-2][1] = lines[h-2][1].."\131"
		lines[h-2][2] = lines[h-2][2].."8"
		lines[h-2][3] = lines[h-2][3].."0"
	end
	lines[1][1] = lines[1][1].."\148"
	lines[1][2] = lines[1][2].."0"
	lines[1][3] = lines[1][3].."8"
	lines[2][1] = lines[2][1].."\149"
	lines[2][2] = lines[2][2].."0"
	lines[2][3] = lines[2][3].."8"
	lines[h][1] = lines[h][1].."\133"
	lines[h][2] = lines[h][2].."0"
	lines[h][3] = lines[h][3].."8"
	lines[h-1][1] = lines[h-1][1].."\149"
	lines[h-1][2] = lines[h-1][2].."0"
	lines[h-1][3] = lines[h-1][3].."8"
	lines[h-2][1] = lines[h-2][1].."\148"
	lines[h-2][2] = lines[h-2][2].."0"
	lines[h-2][3] = lines[h-2][3].."8"
	term.setCursorPos(x,y+(h-1))
	term.blit(table.unpack(lines[h]))
	term.setCursorPos(x,y+(h-1)-1)
	term.blit(table.unpack(lines[h-2]))
	slp()
	term.setCursorPos(x,y+(h-1)-1)
	term.blit(table.unpack(lines[h-1]))
	term.setCursorPos(x,y+(h-1)-2)
	term.blit(table.unpack(lines[h-2]))
	slp()
	for t=4,h,3 do
		term.setCursorPos(x,y+(h-1)-(t-1))
		term.blit(table.unpack(lines[1]))
		for a=1,t-4 do
			term.setCursorPos(x,y+(h-1)-(t-1)+(a))
			term.blit(table.unpack(lines[2]))
		end
		slp()
	end
	term.setCursorPos(x,y)
	term.blit(table.unpack(lines[1]))
	for a=1,h-4 do
		term.setCursorPos(x,y+a)
		term.blit(table.unpack(lines[2]))
	end
	search = lUtils.makeEditBox("Search",w-3,1)
	search.lines = {""}
	local function searchy()
		while true do
			lUtils.drawEditBox(search,x+2,y+(h-2),0,0,string.len(search.lines[1])+1,1,true,false)
		end
	end
	local scrl = -1
    local sel = 1
	local btns = {}
    local function rendersearch()
        for a=1,h-4 do
            term.setCursorPos(x,y+a)
            term.blit(table.unpack(lines[2]))
        end
        local ox,oy = term.getCursorPos()
        local txtcolor = term.getTextColor()
        term.setCursorPos(x+(w/2),y+1)
        term.setBackgroundColor(colors.white)
        term.setTextColor(colors.black)
--      write("lastkey = "..lastkey.." and os.time() = "..os.time())
        -- selected box
        term.setBackgroundColor(colors.white)
        lOS.boxClear(x+math.ceil(w/2),y+1,x+(w-2),y+(h-4))
        if result[sel] ~= nil then
            local tPath = ""
            if string.gsub(result[sel],fs.getName(result[sel]),"") ~= "" then
                tPath = string.gsub(result[sel],fs.getName(result[sel]),"")
            else
                tPath = "root"
            end
            term.setCursorPos(x+(w/2)+1,y+2)
            local ay = y+2
            local ax = x+(math.floor(w/2))+6
            local aw = (math.ceil(w/2-1)-6)
            term.setTextColor(colors.lightGray)
            write("Name")
            term.setTextColor(colors.black)
            for t=1,math.ceil(string.len(fs.getName(result[sel]))/aw) do
                term.setCursorPos(ax,ay)
                write(string.sub(fs.getName(result[sel]),1+((t-1)*aw),t*aw))
                ay = ay+1
            end
            term.setCursorPos(x+(w/2)+1,ay)
            term.setTextColor(colors.lightGray)
            write("Path")
            term.setTextColor(colors.black)
            for t=1,math.ceil(string.len(tPath)/aw) do
                term.setCursorPos(ax,ay)
                write(string.sub(tPath,1+((t-1)*aw),t*aw))
                ay = ay+1
            end
	if fs.exists(result[sel].."updater") then
		term.setTextColor(colors.gray)
		term.setCursorPos(x+(w/2)+1,ay)
		term.write("(Auto updates)")
		ay = ay+1
	end
	ay = ay+1
    local aline = ""
    for t=ax-5,x+(w-2) do
        aline = aline.." "
    end
	function filetype(filename)
		if string.find(filename,"%.%a+$") == nil then
			return ""
		else
			return string.sub(filename,string.find(filename,"%.%a+$"))
		end
	end
    if ay+1 <= y+(h-4) then
		if filetype(fs.getName(result[sel])) == ".lua" then
			btns = {}
	        btns[1] = {" Execute",ax-5,ay}
	        btns[2] = {" Edit",ax-5,ay+2}
			btns[3] = {" Create Shortcut",ax-5,ay+4}
		elseif filetype(fs.getName(result[sel])) == ".txt" then
			btns = {}
			btns[1] = {" Edit",ax-5,ay}
			btns[2] = {" Create Shortcut",ax-5,ay+2}
		else
			btns = {}
	        btns[1] = {" Execute",ax-5,ay}
	        btns[2] = {" Edit",ax-5,ay+2}
			btns[3] = {" Create Shortcut",ax-5,ay+4}
		end
    end
    for t=1,#btns do
        if t==1 then
            term.setCursorPos(ax-5,btns[t][3]-1)
            term.setBackgroundColor(colors.lightGray)
            term.setTextColor(colors.white)
            term.write(string.gsub(aline,"%s","\143"))
        end
        term.setCursorPos(btns[t][2],btns[t][3])
        term.setTextColor(colors.black)
        term.write(btns[t][1]..string.sub(aline,string.len(btns[t][1])+1,string.len(aline)))
        if t < #btns then
            term.setCursorPos(ax-5,btns[t][3]+1)
            term.setTextColor(colors.white)
            term.write(string.gsub(aline,"%s","\140"))
        else
            term.setCursorPos(ax-5,btns[t][3]+1)
            term.setTextColor(colors.lightGray)
            term.setBackgroundColor(colors.white)
            term.write(string.gsub(aline,"%s","\131"))
        end
    end
        end
        for t=1,#result do
			if result[t] ~= nil then
	            if sel == t then
	                term.setBackgroundColor(colors.lightGray)
	            else
	                term.setBackgroundColor(colors.gray)
	            end
				if fs.exists(result[t].."updater") ~= nil then
					for i=1,#result do
						if result[i] ~= nil and result[i] == result[t].."updater" then
							table.remove(result,i)
						end
					end
				end
	            if y+((t-1)*2)-scrl > y and y+((t-1)*2)-scrl <= y+(h-4) then
	                term.setCursorPos(x,y+((t-1)*2)-scrl)
	                term.setTextColor(colors.lightGray)
	                term.write("\149")
	                for i=1,(w/2)-2 do
	                    write(" ")
	                end
	                term.setCursorPos(x+1,y+((t-1)*2)-scrl)
	                term.setTextColor(colors.white)
	                if string.len(fs.getName(result[t])) > (w/2)-2 then
	                    term.write(string.sub(fs.getName(result[t]),1,(w/2)-5).."...")
	                else
	                    term.write(fs.getName(result[t]))
	                end
	            end
	            if 1+y+((t-1)*2)-scrl > y and 1+y+((t-1)*2)-scrl <= y+(h-4) then
	                term.setCursorPos(x,1+y+((t-1)*2)-scrl)
	                term.setTextColor(colors.lightGray)
	                term.write("\149")
	                for i=1,(w/2)-2 do
	                    write(" ")
	                end
	                term.setCursorPos(x+1,1+y+((t-1)*2)-scrl)
	                if sel == t then
	                    term.setTextColor(colors.gray)
	                end
	                if string.gsub(result[t],fs.getName(result[t]),"") ~= "" then
	                    if string.len(string.gsub(result[t],fs.getName(result[t]),"")) > (w/2)-2 then
	                        term.write(string.sub(string.gsub(result[t],fs.getName(result[t]),""),1,(w/2)-5).."...")
	                    else
	                        term.write(string.gsub(result[t],fs.getName(result[t]),""))
	                    end
	                else
	                    term.write("root")
	                end
	            end
	        end
		end
        term.setCursorPos(ox,oy)
        term.setTextColor(txtcolor)
    end
	lastkey = os.time()
	lastsearch = ""
	result = {}
	term.setBackgroundColor(colors.white)
--	lOS.boxClear(x+math.ceil(w/2),y+1,x+(w-2),y+(h-2))
	local function regevents()
		local atimer = os.startTimer(0.5)
		while true do
			local a = {os.pullEvent()}
			if a[1] == "mouse_click" then
				if a[3] < x or a[3] > x+(w-1) or a[4] < y then -- maybe check underneath search box too but i dont think thats necessary for now
					return false,""
				else
					for t=1,#btns do
						if a[4] == btns[t][3] and a[3] >= btns[t][2] and result[sel] ~= nil then
							if string.gsub(btns[t][1],"%s","") == "Edit" and fs.exists(result[sel].."updater") == true then
								result[sel] = result[sel].."updater"
							end
							return true,result[sel],string.gsub(btns[t][1],"%s","")
						end
					end
				end
			elseif a[1] == "key" then
				lastkey = os.time()
				if a[2] == keys.enter and search.lines[1] ~= "" and result[sel] ~= nil then
					return true,result[sel],"Execute"
				elseif a[2] == keys.down then
					if result[sel+1] ~= nil then
						sel = sel+1
						while y+((sel-1)*2)-scrl <= y do
							scrl = scrl-1
						end
						while 1+y+((sel-1)*2)-scrl > y+(h-4) do
							scrl = scrl+1
						end
						rendersearch()
					end
				elseif a[2] == keys.up then
					if sel > 1 then
						sel = sel-1
						while y+((sel-1)*2)-scrl <= y do
							scrl = scrl-1
						end
						while 1+y+((sel-1)*2)-scrl > y+(h-4) do
							scrl = scrl+1
						end
						rendersearch()
					end
				end
			elseif a[1] == "timer" and a[2] == atimer then
				atimer = os.startTimer(0.5)
				if os.time() > lastkey+0.01 and search.lines[1] ~= lastsearch then
					lastkey = os.time()
					lastsearch = search.lines[1]
					result = lOS.searchfor(search.lines[1],"")
					sel = 1
					rendersearch()
				end
			elseif a[1] == "mouse_scroll" and a[3] >= x and a[3] <= x+((w/2)-1) and a[4] >= y and a[4] <= y+(h-4) then
				if scrl+a[2] >= -1 then
					scrl = scrl+a[2]
					rendersearch()
				end
			end
		end
	end
	i = {}
	parallel.waitForAny(searchy,function() i={regevents()} end)
	term.setCursorBlink(false)
	return table.unpack(i)
end



function lUtils.littlewin(oriwin,w,h)
	local oW,oH = oriwin.getSize()
	local oldwin = {lines={}}
	for y=1,oH do
		oldwin.lines[y] = {oriwin.getLine(y)}
	end
	local newwin = {lines={}}
	local ystep = oH/h
	local xstep = oW/w
	for y=1,h do
		local xstart = math.floor(xstep/2)
		if xstart < 1 then
			xstart = 1
		end
		local ystart = math.floor(ystep/2)
		if ystart < 1 then
			ystart = 1
		end
		local curLine = math.floor(ystart+ystep*(y-1)+0.5)
		if curLine > oH or curLine <= 0 then
			curLine = 1
		end
		local templine = {oriwin.getLine(curLine)}
		newwin.lines[y] = {"","",""}
		for x=1,w do
			newwin.lines[y][1] = newwin.lines[y][1]..string.sub(templine[1],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
			newwin.lines[y][2] = newwin.lines[y][2]..string.sub(templine[2],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
			newwin.lines[y][3] = newwin.lines[y][3]..string.sub(templine[3],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
		end
	end
	function newwin.render(x,y)
		for l=1,#newwin.lines do
			term.setCursorPos(x,y+(l-1))
			term.blit(table.unpack(newwin.lines[l]))
		end
	end
	return newwin
end



function lUtils.fread(filepath)
	local fread = fs.open(filepath,"r")
	local thing = fread.readAll()
	fread.close()
	return thing
end

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



wPattern = "%S+"
function lUtils.makeEditBox(filepath,width,height,sTable)
    if filepath == nil then
        return false
    end
    if width == nil then
        width = getWidth()
    end
    if height == nil then
        height = getHeight()
    end
    if sTable == nil then
        --sTable = {background={colors.white},text={colors.black},keywords={colors.yellow},notes={colors.green},strings={colors.red},menu={colors.yellow,colors.lightGray}}
        sTable = {background={colors.white},text={colors.black},cursor={colors.red}}
    end
    lines = {""}
    return {width=width,height=height,sTable=sTable,lines=lines,filepath=filepath,changed=false}
end



function lUtils.drawEditBox(box,expos,eypos,spx,spy,cpx,cpy,active,enterkey,rChar,changesAllowed)
    if changesAllowed == nil then
        changesAllowed = true
    end
    -- term.setCursorPos(expos,eypos)
    -- print(tostring(changesAllowed))
    -- os.sleep(1)
    if enterkey == nil then
        enterkey = true
    end
	if spx == nil then
		spx = 0
	end
	if spy == nil then
		spy = 0
	end
	if cpx == nil then
		cpx = 1
	end
	if cpy == nil then
		cpy = 1
	end
	if expos == nil then
		expos = box.x
	else
		box.x = expos
	end
	if eypos == nil then
		eypos = box.y
	else
		box.y = eypos
	end
    -- As these abbrevs are pretty unclear, I will now specify them respectively.
    -- editorboxtable,editorxposition,editoryposition,scrollpositionx,scrollpositiony,usercursorpositionx,usercurserpositiony
    local keywords = {["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true}
    if box.width == nil or box.height == nil or box.sTable == nil or box.lines == nil or box.filepath == nil then
        return false
    end
    if active == nil then
        active = true
    end
    if active == false then
        if box.sTable.background == nil then
            term.setBackgroundColor(colors.black)
        else
            term.setBackgroundColor(box.sTable.background[1])
        end
        lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
        ypos = eypos
        for l=1+spy,box.height+spy do
            if box.lines[l] ~= nil then
                if rChar ~= nil then
                    term.setTextColor(colors.lightGray)
                    term.setCursorPos(expos,ypos)
                    for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
                        write(rChar)
                    end
                else
                    term.setTextColor(colors.lightGray)
                    term.setCursorPos(expos,ypos)
                    write(string.sub(box.lines[l],1+spx,box.width+spx))
                end
            end
            ypos = ypos+1
        end
        return box,spx,spy,cpx,cpy,false,{}
    end
    while true do
    	expos,eypos = box.x,box.y
        if box.sTable.background == nil then
            term.setBackgroundColor(colors.black)
        else
            term.setBackgroundColor(box.sTable.background[1])
        end
        lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
        ypos = eypos
        for l=1+spy,box.height+spy do
            instring = false
            innote = false
            if box.lines[l] ~= nil then
                if box.sTable.text == nil then
                    term.setTextColor(colors.white)
                else
                    term.setTextColor(box.sTable.text[1])
                end
                term.setCursorPos(expos,ypos)
                if rChar ~= nil then
                    for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
                        write(rChar)
                    end
                else
                    wordcount = 1
                    if string.find(string.sub(box.lines[l],1+spx,box.width+spx),"%s+") ~= nil then
                        if string.find(string.sub(box.lines[l],1+spx,box.width+spx),"%s+") == 1 then
                            write(string.match(string.sub(box.lines[l],1+spx,box.width+spx),"%s+"))
                        end
                    end
                    for word in string.gmatch(string.sub(box.lines[l],1+spx,box.width+spx),wPattern) do
                        somearg = string.find(word,"--",nil,true)
                        if tonumber(somearg) ~= nil and instring == false then
                            innote = true
                        end
                        somearg = nil
                        if innote == true and box.sTable.notes ~= nil then
                            term.setTextColor(box.sTable.notes[1])
                        elseif keywords[word] ~= nil and box.sTable.keywords ~= nil then
                            term.setTextColor(box.sTable.keywords[1])
                        elseif tonumber(word) ~= nil and box.sTable.numbers ~= nil then
                            term.setTextColor(box.sTable.numbers[1])
                        else
                            term.setTextColor(box.sTable.text[1])
                        end
                        if wordcount == 1 then
                            write(word)
                        else
                            write(" "..word)
                        end
                        wordcount = wordcount+1
                    end
                end
            end
            ypos = ypos+1
        end
        if cpy > spy and cpy <= spy+(box.height) and cpx > spx and cpx <= spx+(box.width) then
            if box.sTable.cursor ~= nil then
                term.setTextColor(box.sTable.cursor[1])
            else
                term.setTextColor(colors.black)
            end
            term.setCursorPos(cpx-spx-1+expos,cpy-spy-1+eypos)
            term.setCursorBlink(true)
        end
        while cpx > string.len(box.lines[cpy])+1 do
            cpx = cpx-1
        end
        while cpx > spx+(box.width) do
            spx = spx+1
        end
        while cpx <= spx do
            spx = spx-1
        end
        event,button,x,y = os.pullEvent()
        term.setCursorBlink(false)
        if (event == "mouse_click" and (x < expos or x > expos+box.width-1 or y < eypos or y > eypos+box.height-1)) or (event == "key" and button == keys.enter and enterkey == false) then
            if box.sTable.background == nil then
                term.setBackgroundColor(colors.black)
            else
                term.setBackgroundColor(box.sTable.background[1])
            end
            lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
            ypos = eypos
            for l=1+spy,box.height+spy do
                if box.lines[l] ~= nil then
                    if rChar ~= nil then
                        for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
                            write(rChar)
                        end
                    else
                        term.setTextColor(colors.lightGray)
                        term.setCursorPos(expos,ypos)
                        write(string.sub(box.lines[l],1+spx,box.width+spx))
                    end
                end
                ypos = ypos+1
            end
            return box,spx,spy,cpx,cpy,false,{event,button,x,y}
        elseif event == "mouse_scroll" and enterkey == true then
            if button == -1 and spy > 0 then
                spy = spy-1
            elseif button == 1 then
                spy = spy+1
            end
        elseif event == "key" then
            if button == keys.right and cpx < string.len(box.lines[cpy])+1 then
                cpx = cpx+1
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
            elseif button == keys.left and cpx > 1 then
                cpx = cpx-1
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
            elseif button == keys.down and cpy < #box.lines then
                cpy = cpy+1
                while cpy > spy + (box.height) do
                    spy = spy+1
                end
                while cpy <= spy do
                    spy = spy-1
                end
            elseif button == keys.up and cpy > 1 then
                cpy = cpy-1
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
            elseif button == keys.home then
                cpx = 1
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
            elseif button == keys["end"] then
                cpx = string.len(box.lines[cpy])+1
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
            elseif button == keys.tab and changesAllowed == true then
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
                if box.changed == false then
                    box.changed = true
                end
                box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1).."  "..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
                cpx = cpx+2
            elseif button == keys.backspace and changesAllowed == true then
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height do
                    spy = spy+1
                end
                if box.changed == false then
                    box.changed = true
                end
                if cpx == 1 then
                    if cpy > 1 then
                        cpx = string.len(box.lines[cpy-1])+1
                        box.lines[cpy-1] = box.lines[cpy-1]..box.lines[cpy]
                        table.remove(box.lines,cpy)
                        cpy = cpy-1
                    end
                else
                    box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-2)..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
                    cpx = cpx-1
                end
            elseif button == keys.enter and enterkey ~= false and changesAllowed == true then
                while cpy <= spy do
                    spy = spy-1
                end
                while cpy > spy+box.height-1 do
                    spy = spy+1
                end
                if box.changed == false then
                    box.changed = true
                end
                table.insert(box.lines,cpy+1,string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy])))
                box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)
                cpy = cpy+1
                cpx = 1
            end
        elseif event == "char" and changesAllowed == true then
            while cpy <= spy do
                spy = spy-1
            end
            while cpy > spy+box.height do
                spy = spy+1
            end
            if box.changed == false then
                box.changed = true
            end
            box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)..button..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
            cpx = cpx+1
	elseif event == "paste" and changesAllowed == true then
	    while cpy <= spy do
                spy = spy-1
            end
            while cpy > spy+box.height do
                spy = spy+1
            end
            if box.changed == false then
                box.changed = true
            end
            box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)..button..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
            cpx = cpx+string.len(button)
        elseif event == "mouse_click" and y <= eypos+box.height-1 then
            cpx = x - expos + spx+1
            cpy = y - eypos + spy+1
            if cpy > #box.lines then
                cpy = #box.lines
            end
        end
    end
end


function lUtils.textbox(txt,x1,y1,x2,y2,trans)
	local bbox = lUtils.makeBox(txt,x1,y1,x2,y2,{background={term.getBackgroundColor()},text={term.getTextColor()},name={colors.cyan},code={colors.red,colors.lightGray},notification={colors.green},title={colors.orange,align="center"}})
	_G.bbox = bbox
	lUtils.printBox(bbox,0,true,trans)
end


function lUtils.makeBox(boxTxt,x1,y1,x2,y2,sTable,buttons)
	-- boxTxt = {{{"Introduction",type="title"}},{{"Welcome to ",type="text"},{"LuaCraft",type="name"},{"!",type="text"}}}
	paragraphs = {}
	if boxTxt == nil then
		return false
	end
	if x1 == nil then
		x1 = 1
	end
	if y1 == nil then
		y1 = 1
	end
	if x2 == nil then
		x2 = getWidth()
	end
	if y2 == nil then
		y2 = getHeight()
	end
	if sTable == nil then
		sTable = {background={colors.white},text={colors.black},name={colors.cyan},code={colors.red,colors.lightGray},notification={colors.green},title={colors.orange,align="center"}}
	end
	if _G.type(boxTxt) == "string" then
		while true do
			par1,par2 = string.find(boxTxt,"\n")
			if par2 ~= nil then
				paragraphs[#paragraphs+1] = {{string.sub(boxTxt,1,par1-1),type="text"}}
				if string.len(boxTxt) > par2 then
					boxTxt = string.sub(boxTxt,par2+1,string.len(boxTxt))
				else
					boxTxt = ""
					break
				end
			else
				paragraphs[#paragraphs+1] = {{boxTxt,type="text"}}
				break
			end
		end
	elseif _G.type(boxTxt) == "table" then
		paragraphs = boxTxt
	end
	lines = {{{},tt=""}}
	cline = 1
	for p=1,#paragraphs do
		for t=1,#paragraphs[p] do
			-- paragraphs[1] = {{"Introduction",type="title"}}
			-- paragraphs[1][1] = {"Introduction",type="title"}
			lines[cline][1][#lines[cline][1]+1] = {"",type=paragraphs[p][t].type}
			for word in string.gmatch(paragraphs[p][t][1],"%S+") do
--				write(word)
				if string.len(lines[cline].tt) == 0 then
					if string.len(lines[cline].tt..word) > x2-(x1-1) then
						-- This means a single word takes up MORE than an ENTIRE line. I'll have to write some proper code for that later.
					end
					-- First word in the line, so obviously no space before the word.
					-- wow old me is so stupid I completely forgot about indentation
					-- HAHAHA dumbass old me messed the fuck up
					local indent = ""
					if string.sub(paragraphs[p][t][1],1,1) == " " then
						indent = string.gmatch(paragraphs[p][t][1],"%s+")()
					end
					lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1]..indent..word
					lines[cline].tt = lines[cline].tt..indent..word
				else
					if string.len(lines[cline].tt.." "..word) > x2-(x1-1) then
						cline = cline+1
						lines[cline] = {{},tt=""}
						lines[cline][1][#lines[cline][1]+1] = {"",type=paragraphs[p][t].type}
						lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1]..word
						lines[cline].tt = lines[cline].tt..word
					else
						lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1].." "..word
						lines[cline].tt = lines[cline].tt.." "..word
					end
				end
			end
			print("")
		end
		cline = cline+1
		lines[cline] = {{},tt=""}
	end
	if buttons == nil then
		buttons = {}
	end
	return {x1=x1,y1=y1,x2=x2,y2=y2,sTable=sTable,lines=lines,buttons=buttons}
--  for p=1,#paragraphs do
--  	for t=1,#paragraphs[p] do
--  		if paragraphs[p][t].type ~= nil then
--  			if sTable[paragraphs[p][t].type] ~= nil then
--  				term.setTextColor(sTable[paragraphs[p][t].type][1])
--  				if sTable[paragraphs[p][t].type][2] ~= nil then
--  					term.setBackgroundColor(sTable[paragraphs[p][t].type][2])
--  				end
--  			end
--  		end
--  	end
--  end
end

function lUtils.transWrite(txt)
	for t=1,string.len(txt) do
		term.setBackgroundColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
		term.write(string.sub(txt,t,t))
	end
end

function lUtils.printBox(box,scrollpos,simple,trans)
	someabsolutelyrandomthing = true
	while someabsolutelyrandomthing == true do
		term.setTextColor(colors.black)
		if box.x1 == nil or box.y1 == nil or box.x2 == nil or box.y2 == nil or box.sTable == nil or box.lines == nil then
			return false,"Invalid Box."
		end
		if box.sTable.background ~= nil then
			term.setBackgroundColor(box.sTable.background[1])
		else
			term.setBackgroundColor(colors.white)
		end
		if not trans then
			lOS.boxClear(box.x1,box.y1,box.x2,box.y2)
		end
		ypos = box.y1
		for l=1+scrollpos,box.y2-(box.y1-1)+scrollpos do
			if box.lines[l] ~= nil then
				term.setCursorPos(box.x1,ypos)
				for p=1,#box.lines[l][1] do
					if box.sTable[box.lines[l][1][p].type] ~= nil then
						term.setTextColor(box.sTable[box.lines[l][1][p].type][1])
						if box.sTable[box.lines[l][1][p].type][2] ~= nil then
							term.setBackgroundColor(box.sTable[box.lines[l][1][p].type][2])
						end
						if box.sTable[box.lines[l][1][p].type].align ~= nil then
							if box.sTable[box.lines[l][1][p].type].align == "center" then
								cposx,cposy=term.getCursorPos()
								term.setCursorPos(box.x1+math.floor((box.x2-(box.x1-1))/2)-math.floor(string.len(box.lines[l][1][p][1])/2),cposy)
							end
						end
						lUtils.transWrite(box.lines[l][1][p][1])
					end
				end
			end
			ypos = ypos+1
		end
		if box.buttons ~= nil then
			for b=1,#box.buttons do
				if box.buttons[b].type == "button" or box.buttons[b].type == "editor" then
					term.setBackgroundColor(box.buttons[b].bg)
					term.setTextColor(box.buttons[b].txt)
					for t=1,box.buttons[b].y2-(box.buttons[b].y1-1) do
						blankline = ""
						for bl=1,box.buttons[b].x2-(box.buttons[b].x1-1) do
							blankline = blankline.." "
						end
						if box.buttons[b].y1+t-1 >= 1+scrollpos and box.buttons[b].y1+t-1 <= box.y2-(box.y1-1)+scrollpos then
							term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
							term.write(blankline)
							term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
							if box.buttons[b].text[t] ~= nil then
								term.write(box.buttons[b].text[t])
							end
						end
					end
				end
				if box.buttons[b].type == "editor" then
					if box.buttons[b].y1 >= 1+scrollpos and box.buttons[b].y2 <= box.y2-(box.y1-1)+scrollpos then
						if drawEditBox ~= nil and box.buttons[b].quit == false then
							term.setCursorPos(1,1)
							term.write(box.buttons[b].spx..","..box.buttons[b].spy.." | "..box.buttons[b].cpx..","..box.buttons[b].cpy)
							drawEditBox(box.buttons[b].box,box.buttons[b].x1+box.x1-1,(box.buttons[b].y1-1)+box.y1-scrollpos,box.buttons[b].spx,box.buttons[b].spy,box.buttons[b].cpx,box.buttons[b].cpy,false)
						end
					end
				end
			end
		end
		if simple == true then
			someabsolutelyrandomthing = false
		else
		event,button,x,y = os.pullEvent()
		if event == "mouse_click" and (x < box.x1 or x > box.x2 or y < box.y1 or y > box.y2) then
			term.setBackgroundColor(colors.white)
			ypos = box.y1
			for l=1+scrollpos,box.y2-(box.y1-1)+scrollpos do
				if box.lines[l] ~= nil then
					term.setCursorPos(box.x1,ypos)
					for p=1,#box.lines[l][1] do
						if box.sTable[box.lines[l][1][p].type] ~= nil then
							term.setTextColor(colors.lightGray)
							if box.sTable[box.lines[l][1][p].type][2] ~= nil then
								term.setBackgroundColor(box.sTable[box.lines[l][1][p].type][2])
							end
							if box.sTable[box.lines[l][1][p].type].align ~= nil then
								if box.sTable[box.lines[l][1][p].type].align == "center" then
									cposx,cposy=term.getCursorPos()
									term.setCursorPos(box.x1+math.floor((box.x2-(box.x1-1))/2)-math.floor(string.len(box.lines[l][1][p][1])/2),cposy)
								end
							end
							term.write(box.lines[l][1][p][1])
						end
					end
				end
				ypos = ypos+1
			end
			if box.buttons ~= nil then
				for b=1,#box.buttons do
					term.setBackgroundColor(box.buttons[b].bg)
					term.setTextColor(colors.lightGray)
					for t=1,box.buttons[b].y2-(box.buttons[b].y1-1) do
						blankline = ""
						for bl=1,box.buttons[b].x2-(box.buttons[b].x1-1) do
							blankline = blankline.." "
						end
						if box.buttons[b].y1+t-1 >= 1+scrollpos and box.buttons[b].y1+t-1 <= box.y2-(box.y1-1)+scrollpos then
							term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
							term.write(blankline)
							term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
							if box.buttons[b].text[t] ~= nil then
								term.write(box.buttons[b].text[t])
							end
						end
					end
				end
			end
			return scrollpos
		elseif event == "mouse_click" then
			for b=1,#box.buttons do
				-- box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos
				if x >= box.buttons[b].x1+box.x1-1 and x <= box.buttons[b].x2+box.x1-1 and y >= (box.buttons[b].y1-1)+box.y1-scrollpos and y <= (box.buttons[b].y2-1)+box.y1-scrollpos then
					newbox,newbutton,newscrollpos = box.buttons[b].func(box,box.buttons[b],scrollpos)
					if newbox ~= nil then
						box = newbox
					end
					if newbutton ~= nil then
						box.buttons[b] = newbutton
					end
					if newscrollpos ~= nil then
						scrollpos = newscrollpos
					end
				end
			end
		elseif event == "mouse_scroll" then
			if button == -1 and scrollpos > 0 then
				scrollpos = scrollpos-1
			elseif button == 1 then
				scrollpos = scrollpos+1
			end
		end
		end
	end
end

function lUtils.getPixel(win,x,y)
    local w,h = win.getSize()
    if x < 1 or x > w or y < 1 or y > h then
	return "0","0","0"
    else
        theline = {win.getLine(y)}
        return string.sub(theline[1],x,x),string.sub(theline[2],x,x),string.sub(theline[3],x,x)
   end
end

function lUtils.openWin(title,filepath,x,y,width,height,canresize,canmaximize)
	if canmaximize == nil then
		canmaximize = true
	end
	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 OGterm = term.current()
	local OGwin = {lines={}}
	local OGtxt = term.getTextColor()
	local OGbg = term.getBackgroundColor()
	local w,h = term.getSize()
	for t=1,h do
		OGwin.lines[t] = {OGterm.getLine(t)}
	end
	function getPixel(win,x,y)
		theline = {win.getLine(y)}
		return string.sub(theline[1],x,x),string.sub(theline[2],x,x),string.sub(theline[3],x,x)
	end
	function OGwin.render()
		for l=1,#OGwin.lines do
			term.setCursorPos(1,l)
			term.blit(table.unpack(OGwin.lines[l]))
		end
	end
	local progWin = window.create(term.current(),x+1,y+1,width-2,height-2)
	dragging = false
	dragX = false
	dragY = false
	dragSide = 1
	function redrawbox()
		term.setBackgroundColor(colors.gray)
		term.setCursorPos(x,y)
		for t=1,width-6 do
			term.write(" ")
		end
		term.setTextColor(colors.white)
		if canmaximize == false then
			term.write("    × ")
		else
			term.write(" +  × ")
		end
		term.setCursorPos(x+1,y)
		term.write(title.." ("..os.time()..")")
		term.setTextColor(colors.gray)
		for i=1,height-2 do
			term.setCursorPos(x,y+i)
			term.blit(string.char(149),to_blit[colors.gray],({getPixel(progWin,1,i)})[3])
			term.blit(progWin.getLine(i))
			term.blit(string.char(149),({getPixel(progWin,width-2,i)})[3],to_blit[colors.gray])
		end
		local bottomline = {string.char(138),({getPixel(progWin,1,height-2)})[3],to_blit[colors.gray]}
		for i=1,width-2 do
			bottomline[1] = bottomline[1]..string.char(143)
			bottomline[2] = bottomline[2]..({getPixel(progWin,i,height-2)})[3]
			bottomline[3] = bottomline[3]..to_blit[colors.gray]
		end
		bottomline[1] = bottomline[1]..string.char(133)
		bottomline[2] = bottomline[2]..({getPixel(progWin,width-2,height-2)})[3]
		bottomline[3] = bottomline[3]..to_blit[colors.gray]
		term.setCursorPos(x,y+(height-1))
		term.blit(table.unpack(bottomline))
	end
	local endresult = {}
	function regevents()
		local tPath = filepath
		local rPath = string.sub(filepath,string.find(filepath,"%S+"))
		local b,e = string.find(filepath,"%S+")
		local tPath = string.sub(filepath,e+2,string.len(filepath))
		local progCor
		local thingy = {}
		for i in string.gmatch(tPath,"%S+") do
			if i ~= nil and i ~= "" then
				thingy[#thingy+1] = i
			end
		end
		progCor = coroutine.create(function() endresult = {lOS.run(rPath,table.unpack(thingy))} end)
		local stop = false
		e = {}
		while stop == false do
			if coroutine.status(progCor) == "dead" then return end
			term.redirect(progWin)
			progWin.redraw()
			progWin.restoreCursor()
			if e[1] == "mouse_click" or e[1] == "mouse_drag" or e[1] == "mouse_up" or e[1] == "mouse_scroll" then
				e[3] = e[3]-x
				e[4] = e[4]-y
				if e[3] >= 1 and e[3] <= width-2 and e[4] >= 1 and e[4] <= height-2 then
					coroutine.resume(progCor,table.unpack(e))
				end
			else
				coroutine.resume(progCor,table.unpack(e))
			end
			term.redirect(OGterm)
			redrawbox()
			term.setTextColor(progWin.getTextColor())
			term.setCursorPos(({progWin.getPosition()})[1]+({progWin.getCursorPos()})[1]-1,({progWin.getPosition()})[2]+({progWin.getCursorPos()})[2]-1)
			term.setCursorBlink(progWin.getCursorBlink())
			e = {os.pullEvent()}
			if e[1] == "mouse_drag" then
				if dragX == true then
					if dragSide == 1 then
						width = OGw+(DRx-e[3])
						x = OGx-(DRx-e[3])
					else
						width = OGw-(DRx-e[3])
					end
				end
				if dragY == true then
					height = OGh-(DRy-e[4])
				end
				progWin.reposition(x+1,y+1,width-2,height-2)
				progWin.setVisible(false)
				term.redirect(progWin)
				progWin.redraw()
				progWin.restoreCursor()
				coroutine.resume(progCor)
				term.redirect(OGterm)
				progWin.setVisible(true)
				OGwin.render()
				redrawbox()
			end
			if e[1] == "mouse_drag" and dragging == true then
				x = OGx-(DRx-e[3])
				y = OGy-(DRy-e[4])
				progWin.reposition(x+1,y+1)
				OGwin.render()
				redrawbox()
			elseif e[1] == "mouse_click" or e[1] == "mouse_up" then
				if e[1] == "mouse_up" then
					dragging = false
					dragX = false
					dragY = false
					OGx,OGy = x,y
					OGw,OGh = width,height
					DRx,DRy = nil,nil
				end
				if e[4] == y and e[3] >= x+(width-3) and e[3] <= x+(width-1) then
					if e[1] == "mouse_click" then
						term.setTextColor(colors.white)
						term.setBackgroundColor(colors.red)
						term.setCursorPos(x+(width-3),y)
						term.write(" × ")
					elseif e[1] == "mouse_up" then
						stop = true
						return false
					end
				elseif e[4] == y and e[3] >= x+(width-6) and e[3] <= x+(width-4) and canmaximize ~= false then
					if e[1] == "mouse_click" then
						term.setTextColor(colors.white)
						term.setBackgroundColor(colors.lightGray)
						term.setCursorPos(x+(width-6),y)
						term.write(" + ")
					elseif e[1] == "mouse_up" then
						lOS.oWins[#lOS.oWins+1] = {window.create(oldterm,1,2,({oldterm.getSize()})[1],({oldterm.getSize()})[2]-3),progCor,fullscreen=false,minimized=false,filepath="'..rPath..'",icon={string.sub(fs.getName(rPath),1,3),string.sub(fs.getName(rPath),4,6)},ran=false}
						os.startTimer(0.1)
						return true
					end
				elseif e[4] == y and e[1] == "mouse_click" then
					dragging = true
					DRx,DRy = e[3],e[4]
					OGx,OGy = x,y
				end
				if e[4] > y and e[4] <= y+(height-1) and (e[3] == x or e[3] == x+(width-1)) and e[1] == "mouse_click" then
					DRx = e[3]
					OGw = width
					OGx = x
					dragX = true
					if e[3] == x then
						dragSide = 1
					else
						dragSide = 2
					end
				end
				if e[4] == y+(height-1) and e[3] >= x and e[3] <= x+(width-1) and e[1] == "mouse_click" then
					dragY = true
					DRy = e[4]
					OGh = height
				end
			end
		end
	end
	regevents()
	OGwin.render()
	term.setBackgroundColor(OGbg)
	term.setTextColor(OGtxt)
	return table.unpack(endresult)
end



function lUtils.popup(title,msg,width,height,buttons,redrawscreen)
	local OGterm = term.current()
	local OGwin = {lines={}}
	local w,h = term.getSize()
	for t=1,h do
		OGwin.lines[t] = {OGterm.getLine(t)}
	end
	function OGwin.render()
		for l=1,#OGwin.lines do
			term.setCursorPos(1,l)
			term.blit(table.unpack(OGwin.lines[l]))
		end
	end
	dragging = false
	if width == nil or height == nil then
		width = 21
		height = 7
	end
	if height < 6 then height = 6 end
	popupline = ""
	for pl=1,width do
		popupline = popupline.." "
	end
	function redrawbox()
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
	term.setBackgroundColor(colors.gray)
	term.setTextColor(colors.white)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
	write(" "..title)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
	write(" × ")
	-- The line below is unreadable now but it just makes the text box for the popup message and then prints it
	term.setBackgroundColor(colors.white)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1)
	term.write(string.sub(popupline,2,string.len(popupline)-1))
	term.setTextColor(colors.black)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+1)
	lUtils.printBox(lUtils.makeBox(msg,(math.ceil(w/2)-math.floor(width/2))+1,math.ceil(h/2)-math.floor(height/2)+2,(math.ceil(w/2)-math.floor(width/2)+width-1)-1,math.ceil(h/2)-math.floor(height/2)+height-1-3,{background={colors.white},text={colors.blue}}),0,true)
	for t=1,height-4 do
		term.setBackgroundColor(colors.white)
		term.setTextColor(colors.lightGray)
		term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+t)
		write(string.char(149))
		term.setBackgroundColor(colors.lightGray)
		term.setTextColor(colors.white)
		term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-1,math.ceil(h/2)-math.floor(height/2)+t)
		write(string.char(149))
	end
	term.setBackgroundColor(colors.lightGray)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-2)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	term.setTextColor(colors.black)
	for b=1,#buttons do
		term.setBackgroundColor(colors.lightGray)
		write(" ")
		term.setBackgroundColor(colors.white)
		write(" "..buttons[b].." ")
	end
	end
	redrawbox()
	while true do
		event,button,x,y = os.pullEvent()
		if event == "mouse_drag" and dragging == true then
			w = OGw-(OGx-x)*2
			h = OGh-(OGy-y)*2
			OGwin.render()
			redrawbox()
		elseif event == "mouse_click" or event == "mouse_up" then
			if event == "mouse_up" and dragging == true then
				dragging = false
			end
			if y == math.ceil(h/2)-math.floor(height/2)+height-2 then
				curX = math.ceil(w/2)-math.floor(width/2)
				for b=1,#buttons do
					if x >= curX+1 and x <= curX+string.len(" "..buttons[b].." ") then
						if event == "mouse_up" or event == "monitor_touch" then
							if redrawscreen ~= nil and redrawscreen == true then
								OGwin.render()
							end
							return true,b,buttons[b]
						elseif event == "mouse_click" then
							term.setCursorPos(curX+1,math.ceil(h/2)-math.floor(height/2)+height-2)
							term.setBackgroundColor(colors.lightBlue)
							write(" "..buttons[b].." ")
						end
					end
					curX = curX+string.len(" "..buttons[b].." ")+1
				end
			elseif y == math.ceil(h/2)-math.floor(height/2) and x >= math.ceil(w/2)-math.floor(width/2)+width-3 and x <= math.ceil(w/2)-math.floor(width/2)+width-1 then
				if event == "mouse_click" then
					term.setTextColor(colors.white)
					term.setBackgroundColor(colors.red)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
				elseif event == "mouse_up" then
					if redrawscreen ~= nil and redrawscreen == true then
						OGwin.render()
					end
					return false,0,""
				end
			elseif y == math.ceil(h/2)-math.floor(height/2) then
				dragging = true
				OGx,OGy = x,y
				OGw = w
				OGh = h
			else
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
				term.setBackgroundColor(colors.gray)
				term.setTextColor(colors.white)
				write(popupline)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
				write(" "..title)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
				write(" × ")
				-- The line below is unreadable now but it just makes the text box for the popup message and then prints it
				term.setBackgroundColor(colors.white)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1)
				term.write(string.sub(popupline,2,string.len(popupline)-1))
				term.setTextColor(colors.black)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+1)
				lUtils.printBox(lUtils.makeBox(msg,(math.ceil(w/2)-math.floor(width/2))+1,math.ceil(h/2)-math.floor(height/2)+2,(math.ceil(w/2)-math.floor(width/2)+width-1)-1,math.ceil(h/2)-math.floor(height/2)+height-1-3,{background={colors.white},text={colors.blue}}),0,true)
				for t=1,height-4 do
					term.setBackgroundColor(colors.white)
					term.setTextColor(colors.lightGray)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+t)
					write(string.char(149))
					term.setBackgroundColor(colors.lightGray)
					term.setTextColor(colors.white)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-1,math.ceil(h/2)-math.floor(height/2)+t)
					write(string.char(149))
				end
				term.setBackgroundColor(colors.lightGray)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-2)
				write(popupline)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
				write(popupline)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1)
				write(popupline)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
				write(popupline)
				term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
				term.setTextColor(colors.black)
				for b=1,#buttons do
					term.setBackgroundColor(colors.lightGray)
					write(" ")
					term.setBackgroundColor(colors.white)
					write(" "..buttons[b].." ")
				end
			end
			if (x < math.ceil(w/2)-math.floor(width/2) or x > math.ceil(w/2)-math.floor(width/2)+width-1 or y < math.ceil(h/2)-math.floor(height/2) or y > math.ceil(h/2)-math.floor(height/2)+height-1) and event == "mouse_click" then
				for t=1,5 do
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					term.setBackgroundColor(colors.white)
					term.setTextColor(colors.black)
					write(popupline)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					write(" "..title)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
					os.sleep(0.1)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					term.setBackgroundColor(colors.gray)
					term.setTextColor(colors.white)
					write(popupline)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					write(" "..title)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
					os.sleep(0.1)
				end
			end
		end
	end
end

function lUtils.inputbox(title,msg,width,height,buttons)
	local OGterm = term.current()
	local OGwin = {lines={}}
	local w,h = term.getSize()
	for t=1,h do
		OGwin.lines[t] = {OGterm.getLine(t)}
	end
	function OGwin.render()
		for l=1,#OGwin.lines do
			term.setCursorPos(1,l)
			term.blit(table.unpack(OGwin.lines[l]))
		end
	end
	dragging = false
	local input = lUtils.makeEditBox("input",width-4,1)
	if width == nil or height == nil then
		width = 21
		height = 7
	end
	if height < 6 then height = 6 end
	popupline = ""
	for pl=1,width do
		popupline = popupline.." "
	end
	local function redrawbox()
	ocursorx,ocursory = term.getCursorPos()
	otext = term.getTextColor()
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
	term.setBackgroundColor(colors.gray)
	term.setTextColor(colors.white)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
	write(" "..title)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
	write(" × ")
	-- The line below is unreadable now but it just makes the text box for the popup message and then prints it
	term.setBackgroundColor(colors.white)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1)
	term.write(string.sub(popupline,2,string.len(popupline)-1))
	term.setTextColor(colors.black)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+1)
	lUtils.printBox(lUtils.makeBox(msg,(math.ceil(w/2)-math.floor(width/2))+1,math.ceil(h/2)-math.floor(height/2)+2,(math.ceil(w/2)-math.floor(width/2)+width-1)-1,math.ceil(h/2)-math.floor(height/2)+height-1-3-3,{background={colors.white},text={colors.blue}}),0,true)
	for t=1,height-4 do
		term.setBackgroundColor(colors.white)
		term.setTextColor(colors.lightGray)
		term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+t)
		write(string.char(149))
		term.setBackgroundColor(colors.lightGray)
		term.setTextColor(colors.white)
		term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-1,math.ceil(h/2)-math.floor(height/2)+t)
		write(string.char(149))
	end
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3-2)
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.white)
	write("\159")
	for t=1,width-4 do
		write("\143")
	end
	term.setBackgroundColor(colors.white)
	term.setTextColor(colors.lightGray)
	write("\144")
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3-1)
	term.setBackgroundColor(colors.lightGray)
	term.setTextColor(colors.white)
	write("\149")
	term.setTextColor(colors.lightGray)
	term.setBackgroundColor(colors.white)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1+width-3,math.ceil(h/2)-math.floor(height/2)+height-1-3-1)
	write("\149")
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3)
	write("\130")
	for t=1,width-4 do
		write("\131")
	end
	write("\129")
	term.setBackgroundColor(colors.lightGray)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-2)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	write(popupline)
	term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
	term.setTextColor(colors.black)
	for b=1,#buttons do
		term.setBackgroundColor(colors.lightGray)
		write(" ")
		term.setBackgroundColor(colors.white)
		write(" "..buttons[b].." ")
	end
	term.setTextColor(otext)
	term.setCursorPos(ocursorx,ocursory)
	end
	redrawbox()
	local function regevents()
	while true do
		event,button,x,y = os.pullEvent()
		if event == "mouse_drag" and dragging == true then
			w = OGw-(OGx-x)*2
			h = OGh-(OGy-y)*2
			OGwin.render()
			redrawbox()
		elseif event == "mouse_click" or event == "mouse_up" then
			if event == "mouse_up" and dragging == true then
				dragging = false
			end
			if y == math.ceil(h/2)-math.floor(height/2)+height-2 then
				curX = math.ceil(w/2)-math.floor(width/2)
				for b=1,#buttons do
					if x >= curX+1 and x <= curX+string.len(" "..buttons[b].." ") then
						if event == "mouse_up" or event == "monitor_touch" then
							return true,b,buttons[b]
						elseif event == "mouse_click" then
							ocursorx,ocursory = term.getCursorPos()
							otext = term.getTextColor()
							term.setCursorPos(curX+1,math.ceil(h/2)-math.floor(height/2)+height-2)
							term.setBackgroundColor(colors.lightBlue)
							term.setTextColor(colors.black)
							write(" "..buttons[b].." ")
							term.setCursorPos(ocursorx,ocursory)
							term.setTextColor(otext)
						end
					end
					curX = curX+string.len(" "..buttons[b].." ")+1
				end
			elseif y == math.ceil(h/2)-math.floor(height/2) and x >= math.ceil(w/2)-math.floor(width/2)+width-3 and x <= math.ceil(w/2)-math.floor(width/2)+width-1 then
				if event == "mouse_click" then
					term.setTextColor(colors.white)
					term.setBackgroundColor(colors.red)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
				elseif event == "mouse_up" then
					return false,0,""
				end
			elseif y == math.ceil(h/2)-math.floor(height/2) then
				dragging = true
				OGx,OGy = x,y
				OGw = w
				OGh = h
			else
				redrawbox()
			end
			if (x < math.ceil(w/2)-math.floor(width/2) or x > math.ceil(w/2)-math.floor(width/2)+width-1 or y < math.ceil(h/2)-math.floor(height/2) or y > math.ceil(h/2)-math.floor(height/2)+height-1) and event == "mouse_click" then
				for t=1,5 do
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					term.setBackgroundColor(colors.white)
					term.setTextColor(colors.black)
					write(popupline)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					write(" "..title)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
					os.sleep(0.1)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					term.setBackgroundColor(colors.gray)
					term.setTextColor(colors.white)
					write(popupline)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
					write(" "..title)
					term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
					write(" × ")
					os.sleep(0.1)
				end
			end
		end
	end
	end
	function readbox()
		c = {"mouse_click",1,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1}
		while true do
			if c[1] == "mouse_click" and c[3] >= math.ceil(w/2)-math.floor(width/2)+2 and c[3] <= math.ceil(w/2)-math.floor(width/2)+2+(input.width-1) and c[4] == math.ceil(h/2)-math.floor(height/2)+height-1-3-1 then
	            if input.lines[1] ~= nil then
    	            lUtils.drawEditBox(input,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1,0,0,string.len(input.lines[1])+1,1,true,false)
	            else
    	            lUtils.drawEditBox(input,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1,0,0,1,1,true,false)
	            end
			end
			c = {os.pullEvent("mouse_click")}
		end
	end
	local returnthis = {}
	parallel.waitForAny(readbox,function() returnthis = {regevents()} end)
	term.setCursorBlink(false)
	return input.lines[1],table.unpack(returnthis)
end

function lUtils.createButton(x,y,w,h,lines,func,colrs)
	local btn = {x1=x,y1=y,x2=x+(w-1),y2=y+(h-1),func=func,colors=colrs,selected=false}
	if not btn.colors then
		btn.colors = {}
	end
	local c = btn.colors
	if not c.txt then
		c.txt = colors.white
	end
	if not c.fg then
		c.fg = c.bg or colors.lightGray
	end
	if not c.bg then
		c.bg = colors.gray
	end
	if not c.clicked then
		c.clicked = {}
	end
	local cc = c.clicked
	if not cc.fg then
		cc.fg = cc.bg or colors.lightGray
	end
	if not cc.bg then
		cc.bg = c.bg2 or colors.lightGray
	end
	if not cc.txt then
		cc.txt = c.txt2 or colors.white
	end
	function btn.render()
		local ofg,obg = term.getTextColor(),term.getBackgroundColor()
		if btn.selected then
			term.setBackgroundColor(btn.colors.clicked.bg)
			term.setTextColor(btn.colors.clicked.fg)
		else
			term.setBackgroundColor(btn.colors.bg)
			term.setTextColor(btn.colors.fg)
		end
		lUtils.border(btn.x1,btn.y1,btn.x2,btn.y2,"fill")
		if type(lines) == "string" then
			if btn.selected then
				term.setTextColor(btn.colors.clicked.txt)
			else
				term.setTextColor(btn.colors.txt)
			end
			if btn.y1 == btn.y2 then
				term.setCursorPos(btn.x1,btn.y1)
			else
				term.setCursorPos(btn.x1+1,btn.y1+1)
			end
			term.write(lines)
		end
		term.setTextColor(ofg)
		term.setBackgroundColor(obg)
	end
	function btn.update(...)
		local e = {...}
		if (e[1] == "mouse_click" or e[1] == "mouse_up") and e[2] == 1 then
			if e[3] >= btn.x1 and e[4] >= btn.y1 and e[3] <= btn.x2 and e[4] <= btn.y2 then
				if e[1] == "mouse_click" then
					btn.selected = true
					btn.render()
				elseif e[1] == "mouse_up" and btn.selected then
					btn.selected = false
					btn.render()
					return btn.func()
				end
			end
		end
		if e[1] == "mouse_up" then
			btn.selected = false
		end
	end
	return btn
end
function lUtils.createCanvas(x,y,w,h)
	cvs = {}
	if not (x or y) then
		cvs.x1,cvs.y1 = 1,1
	else
		cvs.x1,cvs.y1 = x,y
	end
	if not (w or h) then
		cvs.x2,cvs.y2 = term.getSize()
	else
		cvs.x2,cvs.y2 = x+(w-1),y+(h-1)
	end
	cvs.objs = {}
	function cvs.clear()
		cvs.objs = {}
	end
	function cvs.update(...)
		for o=1,#cvs.objs do
			cvs.objs[o].update(...)
		end
	end
	function cvs.render()
		for o=1,#cvs.objs do
			cvs.objs[o].render()
		end
	end
	function cvs.createButton(...)
		local b = lUtils.createButton(...)
		if b then
			cvs.objs[#cvs.objs+1] = b
			return b
		else
			return false
		end
	end
	return cvs
end
function lUtils.clickmenu(x,y,w,options,redrawscreen,disabled,preferredColors)
	local mColors = {}
	if preferredColors then
		mColors = preferredColors
	else
		mColors = {txt = colors.white, fg = colors.lightGray, bg = colors.gray}
	end
	local w = w
	local OGterm = term.current()
	local OGwin = {lines={}}
	OGwin.w,OGwin.h = term.getSize()
	for t=1,OGwin.h do
		OGwin.lines[t] = {OGterm.getLine(t)}
	end
	function OGwin.render()
		for l=1,#OGwin.lines do
			term.setCursorPos(1,l)
			term.blit(table.unpack(OGwin.lines[l]))
		end
	end
	local clearline = ""
	for t=1,w-2 do
		clearline = clearline.." "
	end
	width,height = term.getSize()
	h = 0
	for t=1,#options do
		if type(options[t]) == "table" then
			h = h+#options[t]
		else
			h = h+1
		end
		if t < #options then
			h = h+1 -- for the divider
		end
	end
	h = h+2
	if x+(w-1) > width then
		x = width-(w-1)
	end
	if y+(h-1) > height then
		y = y-(h-1)
	end
	xstart = x
	ystart = y
	local function redrawmenu()
		term.setBackgroundColor(colors.gray)
		term.setTextColor(colors.lightGray)
		lOS.boxClear(x,y,x+(w-1),y+(h-1))
		term.setCursorPos(x,y)
		term.setBackgroundColor(colors.gray)
		term.setTextColor(colors.lightGray)
		write("\151")
		for a=1,w-2 do
			write("\131")
		end
		for a=1,h-2 do
			term.setCursorPos(x,y+a)
			write("\149")
		end
		term.setBackgroundColor(colors.lightGray)
		term.setTextColor(colors.gray)
		term.setCursorPos(x+(w-1),y)
		write("\148")
		term.setCursorPos(x,y+(h-1))
		write("\138")
		for a=1,w-2 do
			write("\143")
		end
		write("\133")
		for a=1,h-2 do
			term.setCursorPos(x+(w-1),y+a)
			write("\149")
		end
		term.setBackgroundColor(colors.gray)
		term.setTextColor(colors.white)
		y = y+1
		for t=1,#options do
			term.setTextColor(colors.white)
			if type(options[t]) == "table" then
				for a=1,#options[t] do
					term.setCursorPos(x+1,y)
					if disabled ~= nil and disabled[options[t][a]] ~= nil and disabled[options[t][a]] == true then
						term.setTextColor(colors.lightGray)
					else
						term.setTextColor(colors.white)
					end
					write(options[t][a])
					y = y+1
				end
			else
				term.setCursorPos(x+1,y)
				if disabled ~= nil and disabled[options[t]] ~= nil and disabled[options[t]] == true then
					term.setTextColor(colors.lightGray)
				end
				write(options[t])
				y = y+1
			end
			if t < #options then
				term.setCursorPos(x+1,y)
				term.setTextColor(colors.lightGray)
				for a=1,w-2 do
					write("\140")
				end
				y = y+1
			end
		end
	end
	redrawmenu()
	while true do
		local e = {os.pullEvent()}
		if e[1] == "mouse_click" or e[1] == "mouse_up" then
			if e[3] > xstart and e[3] < xstart+(w-1) and e[4] > ystart and e[4] < ystart+(h-1) then
				y = ystart+1
				for t=1,#options do
					if type(options[t]) == "table" then
						for a=1,#options[t] do
							if e[4] == y and not (disabled ~= nil and disabled[options[t][a]] ~= nil and disabled[options[t][a]] == true) then
								if e[1] == "mouse_up" then
									if redrawscreen == true then
										OGwin.render()
									end
									return true,t,options[t][a]
								else
									term.setCursorPos(x+1,y)
									term.setBackgroundColor(colors.lightGray)
									term.setTextColor(colors.white)
									write(clearline)
									term.setCursorPos(x+1,y)
									write(options[t][a])
								end
							end
							y = y+1
						end
					else
						if e[4] == y and not (disabled ~= nil and disabled[options[t]] ~= nil and disabled[options[t]] == true) then
							if e[1] == "mouse_up" then
								return true,t,options[t]
							else
								term.setCursorPos(x+1,y)
								term.setBackgroundColor(colors.lightGray)
								term.setTextColor(colors.white)
								write(clearline)
								term.setCursorPos(x+1,y)
								write(options[t])
							end
						end
						y = y+1
					end
					y = y+1
				end
			elseif e[1] == "mouse_click" then
				if redrawscreen == true then
					OGwin.render()
				end
				return false,0
			end
		end
		if e[1] == "mouse_up" then
			x = xstart
			y = ystart
			redrawmenu()
		end
	end
end