tArgs = {...}

if OneOS then
	--running under OneOS
	OneOS.ToolBarColour = colours.grey
	OneOS.ToolBarTextColour = colours.white
end

local _w, _h = term.getSize()

local round = function(num, idp)
	local mult = 10^(idp or 0)
	return math.floor(num * mult + 0.5) / mult
end

UIColours = {
	Toolbar = colours.grey,
	ToolbarText = colours.lightGrey,
	ToolbarSelected = colours.lightBlue,
	ControlText = colours.white,
	ToolbarItemTitle = colours.black,
	Background = colours.lightGrey,
	MenuBackground = colours.white,
	MenuText = colours.black,
	MenuSeparatorText = colours.grey,
	MenuDisabledText = colours.lightGrey,
	Shadow = colours.grey,
	TransparentBackgroundOne = colours.white,
	TransparentBackgroundTwo = colours.lightGrey,
	MenuBarActive = colours.white
}

Clipboard = {
	Content = nil,
	Type = nil,
	IsCut = false,

	Empty = function()
		Clipboard.Content = nil
		Clipboard.Type = nil
		Clipboard.IsCut = false
	end,

	isEmpty = function()
		return Clipboard.Content == nil
	end,

	Copy = function(content, _type)
		Clipboard.Content = content
		Clipboard.Type = _type or 'generic'
		Clipboard.IsCut = false
	end,

	Cut = function(content, _type)
		Clipboard.Content = content
		Clipboard.Type = _type or 'generic'
		Clipboard.IsCut = true
	end,

	Paste = function()
		local c, t = Clipboard.Content, Clipboard.Type
		if Clipboard.IsCut then
			Clipboard.Empty()
		end
		return c, t
	end
}

if OneOS and OneOS.Clipboard then
	Clipboard = OneOS.Clipboard
end

Drawing = {
	
	Screen = {
		Width = _w,
		Height = _h
	},

	DrawCharacters = function (x, y, characters, textColour,bgColour)
		Drawing.WriteStringToBuffer(x, y, characters, textColour, bgColour)
	end,
	
	DrawBlankArea = function (x, y, w, h, colour)
		Drawing.DrawArea (x, y, w, h, " ", 1, colour)
	end,

	DrawArea = function (x, y, w, h, character, textColour, bgColour)
		--width must be greater than 1, other wise we get a stack overflow
		if w < 0 then
			w = w * -1
		elseif w == 0 then
			w = 1
		end

		for ix = 1, w do
			local currX = x + ix - 1
			for iy = 1, h do
				local currY = y + iy - 1
				Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour)
			end
		end
	end,

	DrawImage = function(_x,_y,tImage, w, h)
		if tImage then
			for y = 1, h do
				if not tImage[y] then
					break
				end
				for x = 1, w do
					if not tImage[y][x] then
						break
					end
					local bgColour = tImage[y][x]
		            local textColour = tImage.textcol[y][x] or colours.white
		            local char = tImage.text[y][x]
		            Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)
				end
			end
		elseif w and h then
			Drawing.DrawBlankArea(x, y, w, h, colours.green)
		end
	end,
	--using .nft
	LoadImage = function(path)
		local image = {
			text = {},
			textcol = {}
		}
		local fs = fs
		if OneOS then
			fs = OneOS.FS
		end
		if fs.exists(path) then
			local _open = io.open
			if OneOS then
				_open = OneOS.IO.open
			end
	        local file = _open(path, "r")
	        local sLine = file:read()
	        local num = 1
	        while sLine do  
	                table.insert(image, num, {})
	                table.insert(image.text, num, {})
	                table.insert(image.textcol, num, {})
	                                            
	                --As we're no longer 1-1, we keep track of what index to write to
	                local writeIndex = 1
	                --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour
	                local bgNext, fgNext = false, false
	                --The current background and foreground colours
	                local currBG, currFG = nil,nil
	                for i=1,#sLine do
	                        local nextChar = string.sub(sLine, i, i)
	                        if nextChar:byte() == 30 then
                                bgNext = true
	                        elseif nextChar:byte() == 31 then
                                fgNext = true
	                        elseif bgNext then
                                currBG = Drawing.GetColour(nextChar)
                                bgNext = false
	                        elseif fgNext then
                                currFG = Drawing.GetColour(nextChar)
                                fgNext = false
	                        else
                                if nextChar ~= " " and currFG == nil then
                                       currFG = colours.white
                                end
                                image[num][writeIndex] = currBG
                                image.textcol[num][writeIndex] = currFG
                                image.text[num][writeIndex] = nextChar
                                writeIndex = writeIndex + 1
	                        end
	                end
	                num = num+1
	                sLine = file:read()
	        end
	        file:close()
		end
	 	return image
	end,

	DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)
		w = w or Drawing.Screen.Width
		h = h or Drawing.Screen.Height
		x = x or 0
		y = y or 0
		x = math.ceil((w - #characters) / 2) + x
		y = math.floor(h / 2) + y

		Drawing.DrawCharacters(x, y, characters, textColour, bgColour)
	end,

	GetColour = function(hex)
		if hex == ' ' then
			return colours.transparent
		end
	    local value = tonumber(hex, 16)
	    if not value then return nil end
	    value = math.pow(2,value)
	    return value
	end,

	Clear = function (_colour)
		_colour = _colour or colours.black
		Drawing.ClearBuffer()
		Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour)
	end,

	Buffer = {},
	BackBuffer = {},

	DrawBuffer = function()
		for y,row in pairs(Drawing.Buffer) do
			for x,pixel in pairs(row) do
				local shouldDraw = true
				local hasBackBuffer = true
				if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then
					hasBackBuffer = false
				end
				if hasBackBuffer and Drawing.BackBuffer[y][x][1] == Drawing.Buffer[y][x][1] and Drawing.BackBuffer[y][x][2] == Drawing.Buffer[y][x][2] and Drawing.BackBuffer[y][x][3] == Drawing.Buffer[y][x][3] then
					shouldDraw = false
				end
				if shouldDraw then
					term.setBackgroundColour(pixel[3])
					term.setTextColour(pixel[2])
					term.setCursorPos(x, y)
					term.write(pixel[1])
				end
			end
		end
		Drawing.BackBuffer = Drawing.Buffer
		Drawing.Buffer = {}
		term.setCursorPos(1,1)
	end,

	ClearBuffer = function()
		Drawing.Buffer = {}
	end,

	WriteStringToBuffer = function (x, y, characters, textColour,bgColour)
		for i = 1, #characters do
   			local character = characters:sub(i,i)
   			Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour)
		end
	end,

	WriteToBuffer = function(x, y, character, textColour,bgColour)
		x = round(x)
		y = round(y)
		if bgColour == colours.transparent then
			Drawing.Buffer[y] = Drawing.Buffer[y] or {}
			Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {"", colours.white, colours.black}
			Drawing.Buffer[y][x][1] = character
			Drawing.Buffer[y][x][2] = textColour
		else
			Drawing.Buffer[y] = Drawing.Buffer[y] or {}
			Drawing.Buffer[y][x] = {character, textColour, bgColour}
		end
	end,
}

Current = {
	Document = nil,
	TextInput = nil,
	CursorPos = {1,1},
	CursorColour = colours.black,
	Selection = {8, 36},
	Window = nil,
	Modified = false,
}

local isQuitting = false

function OrderSelection()
	if Current.Selection then
		if Current.Selection[1] <= Current.Selection[2] then
			return Current.Selection
		else
			return {Current.Selection[2], Current.Selection[1]}
		end
	end
end

function StripColours(str)
	return str:gsub('['..string.char(14)..'-'..string.char(29)..']','')
end

function FindColours(str)
	local _, count = str:gsub('['..string.char(14)..'-'..string.char(29)..']','')
	return count
end

ColourFromCharacter = function(character)
	local n = character:byte() - 14
	if n > 16 then
		return nil
	else
		return 2^n
	end
end

CharacterFromColour = function(colour)
	return string.char(math.floor(math.log(colour)/math.log(2))+14)
end

Events = {}

Button = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	BackgroundColour = colours.lightGrey,
	TextColour = colours.white,
	ActiveBackgroundColour = colours.lightGrey,
	Text = "",
	Parent = nil,
	_Click = nil,
	Toggle = nil,

	AbsolutePosition = function(self)
		return self.Parent:AbsolutePosition()
	end,

	Draw = function(self)
		local bg = self.BackgroundColour
		local tc = self.TextColour
		if type(bg) == 'function' then
			bg = bg()
		end

		if self.Toggle then
			tc = UIColours.MenuBarActive
			bg = self.ActiveBackgroundColour
		end

		local pos = GetAbsolutePosition(self)
		Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, bg)
		Drawing.DrawCharactersCenter(pos.X, pos.Y, self.Width, self.Height, self.Text, tc, bg)
	end,

	Initialise = function(self, x, y, width, height, backgroundColour, parent, click, text, textColour, toggle, activeBackgroundColour)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		height = height or 1
		new.Width = width or #text + 2
		new.Height = height
		new.Y = y
		new.X = x
		new.Text = text or ""
		new.BackgroundColour = backgroundColour or colours.lightGrey
		new.TextColour = textColour or colours.white
		new.ActiveBackgroundColour = activeBackgroundColour or colours.lightGrey
		new.Parent = parent
		new._Click = click
		new.Toggle = toggle
		return new
	end,

	Click = function(self, side, x, y)
		if self._Click then
			if self:_Click(side, x, y, not self.Toggle) ~= false and self.Toggle ~= nil then
				self.Toggle = not self.Toggle
				Draw()
			end
			return true
		else
			return false
		end
	end
}

TextBox = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	BackgroundColour = colours.lightGrey,
	TextColour = colours.black,
	Parent = nil,
	TextInput = nil,
	Placeholder = '',

	AbsolutePosition = function(self)
		return self.Parent:AbsolutePosition()
	end,

	Draw = function(self)		
		local pos = GetAbsolutePosition(self)
		Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, self.BackgroundColour)
		local text = self.TextInput.Value
		if #tostring(text) > (self.Width - 2) then
			text = text:sub(#text-(self.Width - 3))
			if Current.TextInput == self.TextInput then
				Current.CursorPos = {pos.X + 1 + self.Width-2, pos.Y}
			end
		else
			if Current.TextInput == self.TextInput then
				Current.CursorPos = {pos.X + 1 + self.TextInput.CursorPos, pos.Y}
			end
		end
		
		if #tostring(text) == 0 then
			Drawing.DrawCharacters(pos.X + 1, pos.Y, self.Placeholder, colours.lightGrey, self.BackgroundColour)
		else
			Drawing.DrawCharacters(pos.X + 1, pos.Y, text, self.TextColour, self.BackgroundColour)
		end

		term.setCursorBlink(true)
		
		Current.CursorColour = self.TextColour
	end,

	Initialise = function(self, x, y, width, height, parent, text, backgroundColour, textColour, done, numerical)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		height = height or 1
		new.Width = width or #text + 2
		new.Height = height
		new.Y = y
		new.X = x
		new.TextInput = TextInput:Initialise(text or '', function(key)
			if done then
				done(key)
			end
			Draw()
		end, numerical)
		new.BackgroundColour = backgroundColour or colours.lightGrey
		new.TextColour = textColour or colours.black
		new.Parent = parent
		return new
	end,

	Click = function(self, side, x, y)
		Current.Input = self.TextInput
		self:Draw()
	end
}

TextInput = {
	Value = "",
	Change = nil,
	CursorPos = nil,
	Numerical = false,
	IsDocument = nil,

	Initialise = function(self, value, change, numerical, isDocument)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Value = tostring(value)
		new.Change = change
		new.CursorPos = #tostring(value)
		new.Numerical = numerical
		new.IsDocument = isDocument or false
		return new
	end,

	Insert = function(self, str)
		if self.Numerical then
			str = tostring(tonumber(str))
		end

		local selection = OrderSelection()

		if self.IsDocument and selection then
			self.Value = string.sub(self.Value, 1, selection[1]-1) .. str .. string.sub( self.Value, selection[2]+2)
			self.CursorPos = selection[1]
			Current.Selection = nil
		else
			local _, newLineAdjust = string.gsub(self.Value:sub(1, self.CursorPos), '\n','')

			self.Value = string.sub(self.Value, 1, self.CursorPos + newLineAdjust) .. str .. string.sub( self.Value, self.CursorPos + 1  + newLineAdjust)
			self.CursorPos = self.CursorPos + 1
		end
		
		self.Change(key)
	end,

	Extract = function(self, remove)
		local selection = OrderSelection()
		if self.IsDocument and selection then
			local _, newLineAdjust = string.gsub(self.Value:sub(selection[1], selection[2]), '\n','')
			local str = string.sub(self.Value, selection[1], selection[2]+1+newLineAdjust)
			if remove then
				self.Value = string.sub(self.Value, 1, selection[1]-1) .. string.sub( self.Value, selection[2]+2+newLineAdjust)
				self.CursorPos = selection[1] - 1
				Current.Selection = nil
			end
			return str
		end
	end,

	Char = function(self, char)
		if char == 'nil' then
			return
		end
		self:Insert(char)
	end,

	Key = function(self, key)
		if key == keys.enter then
			if self.IsDocument then
				self.Value = string.sub(self.Value, 1, self.CursorPos ) .. '\n' .. string.sub( self.Value, self.CursorPos + 1 )
				self.CursorPos = self.CursorPos + 1
			end
			self.Change(key)		
		elseif key == keys.left then
			-- Left
			if self.CursorPos > 0 then
				local colShift = FindColours(string.sub( self.Value, self.CursorPos, self.CursorPos))
				self.CursorPos = self.CursorPos - 1 - colShift
				self.Change(key)
			end
			
		elseif key == keys.right then
			-- Right				
			if self.CursorPos < string.len(self.Value) then
				local colShift = FindColours(string.sub( self.Value, self.CursorPos+1, self.CursorPos+1))
				self.CursorPos = self.CursorPos + 1 + colShift
				self.Change(key)
			end
		
		elseif key == keys.backspace then
			-- Backspace
			if self.IsDocument and Current.Selection then
				self:Extract(true)
				self.Change(key)
			elseif self.CursorPos > 0 then
				local colShift = FindColours(string.sub( self.Value, self.CursorPos, self.CursorPos))
				local _, newLineAdjust = string.gsub(self.Value:sub(1, self.CursorPos), '\n','')

				self.Value = string.sub( self.Value, 1, self.CursorPos - 1 - colShift + newLineAdjust) .. string.sub( self.Value, self.CursorPos + 1 - colShift + newLineAdjust)
				self.CursorPos = self.CursorPos - 1 - colShift
				self.Change(key)
			end
		elseif key == keys.home then
			-- Home
			self.CursorPos = 0
			self.Change(key)
		elseif key == keys.delete then
			if self.IsDocument and Current.Selection then
				self:Extract(true)
				self.Change(key)
			elseif self.CursorPos < string.len(self.Value) then
				self.Value = string.sub( self.Value, 1, self.CursorPos ) .. string.sub( self.Value, self.CursorPos + 2 )				
				self.Change(key)
			end
		elseif key == keys["end"] then
			-- End
			self.CursorPos = string.len(self.Value)
			self.Change(key)
		elseif key == keys.up and self.IsDocument then
			-- Up
			if Current.Document.CursorPos then
				local page = Current.Document.Pages[Current.Document.CursorPos.Page]
				self.CursorPos = page:GetCursorPosFromPoint(Current.Document.CursorPos.Collum + page.MarginX, Current.Document.CursorPos.Line - page.MarginY - 1 + Current.Document.ScrollBar.Scroll, true)
				self.Change(key)
			end
		elseif key == keys.down and self.IsDocument then
			-- Down
			if Current.Document.CursorPos then
				local page = Current.Document.Pages[Current.Document.CursorPos.Page]
				self.CursorPos = page:GetCursorPosFromPoint(Current.Document.CursorPos.Collum + page.MarginX, Current.Document.CursorPos.Line - page.MarginY + 1 + Current.Document.ScrollBar.Scroll, true)
				self.Change(key)
			end
		end
	end
}

Menu = {
	X = 0,
	Y = 0,
	Width = 0,
	Height = 0,
	Owner = nil,
	Items = {},
	RemoveTop = false,

	Draw = function(self)
		Drawing.DrawBlankArea(self.X + 1, self.Y + 1, self.Width, self.Height, UIColours.Shadow)
		if not self.RemoveTop then
			Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground)
			for i, item in ipairs(self.Items) do
				if item.Separator then
					Drawing.DrawArea(self.X, self.Y + i, self.Width, 1, '-', colours.grey, UIColours.MenuBackground)
				else
					local textColour = item.Colour or UIColours.MenuText
					if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then
						textColour = UIColours.MenuDisabledText
					end
					Drawing.DrawCharacters(self.X + 1, self.Y + i, item.Title, textColour, UIColours.MenuBackground)
				end
			end
		else
			Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground)
			for i, item in ipairs(self.Items) do
				if item.Separator then
					Drawing.DrawArea(self.X, self.Y + i - 1, self.Width, 1, '-', colours.grey, UIColours.MenuBackground)
				else
					local textColour = item.Colour or UIColours.MenuText
					if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then
						textColour = UIColours.MenuDisabledText
					end
					Drawing.DrawCharacters(self.X + 1, self.Y + i - 1, item.Title, textColour, UIColours.MenuBackground)

					Drawing.DrawCharacters(self.X - 1 + self.Width-#item.KeyName, self.Y + i - 1, item.KeyName, textColour, UIColours.MenuBackground)
				end
			end
		end
	end,

	NameForKey = function(self, key)
		if key == keys.leftCtrl then
			return '^'
		elseif key == keys.tab then
			return 'Tab'
		elseif key == keys.delete then
			return 'Delete'
		elseif key == keys.n then
			return 'N'
		elseif key == keys.a then
			return 'A'
		elseif key == keys.s then
			return 'S'
		elseif key == keys.o then
			return 'O'
		elseif key == keys.z then
			return 'Z'
		elseif key == keys.y then
			return 'Y'
		elseif key == keys.c then
			return 'C'
		elseif key == keys.x then
			return 'X'
		elseif key == keys.v then
			return 'V'
		elseif key == keys.r then
			return 'R'
		elseif key == keys.l then
			return 'L'
		elseif key == keys.t then
			return 'T'
		elseif key == keys.h then
			return 'H'
		elseif key == keys.e then
			return 'E'
		elseif key == keys.p then
			return 'P'
		elseif key == keys.f then
			return 'F'
		elseif key == keys.m then
			return 'M'
		elseif key == keys.q then
			return 'Q'
		else
			return '?'		
		end
	end,

	Initialise = function(self, x, y, items, owner, removeTop)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		if not owner then
			return
		end

		local keyNames = {}

		for i, v in ipairs(items) do
			items[i].KeyName = ''
			if v.Keys then
				for _i, key in ipairs(v.Keys) do
					items[i].KeyName = items[i].KeyName .. self:NameForKey(key)
				end
			end
			if items[i].KeyName ~= '' then
				table.insert(keyNames, items[i].KeyName)
			end
		end
		local keysLength = LongestString(keyNames)
		if keysLength > 0 then
			keysLength = keysLength + 2
		end

		new.Width = LongestString(items, 'Title') + 2 + keysLength
		if new.Width < 10 then
			new.Width = 10
		end
		new.Height = #items + 2
		new.RemoveTop = removeTop or false
		if removeTop then
			new.Height = new.Height - 1
		end
		
		if y < 1 then
			y = 1
		end
		if x < 1 then
			x = 1
		end

		if y + new.Height > Drawing.Screen.Height + 1 then
			y = Drawing.Screen.Height - new.Height
		end
		if x + new.Width > Drawing.Screen.Width + 1 then
			x = Drawing.Screen.Width - new.Width
		end


		new.Y = y
		new.X = x
		new.Items = items
		new.Owner = owner
		return new
	end,

	New = function(self, x, y, items, owner, removeTop)
		if Current.Menu and Current.Menu.Owner == owner then
			Current.Menu = nil
			return
		end

		local new = self:Initialise(x, y, items, owner, removeTop)
		Current.Menu = new
		return new
	end,

	Click = function(self, side, x, y)
		local i = y-1
		if self.RemoveTop then
			i = y
		end
		if i >= 1 and y < self.Height then
			if not ((self.Items[i].Enabled and type(self.Items[i].Enabled) == 'function' and self.Items[i].Enabled() == false) or self.Items[i].Enabled == false) and self.Items[i].Click then
				self.Items[i]:Click()
				if Current.Menu.Owner and Current.Menu.Owner.Toggle then
					Current.Menu.Owner.Toggle = false
				end
				Current.Menu = nil
				self = nil
			end
			return true
		end
	end
}

MenuBar = {
	X = 1,
	Y = 1,
	Width = Drawing.Screen.Width,
	Height = 1,
	MenuBarItems = {},

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		--Drawing.DrawArea(self.X - 1, self.Y, 1, self.Height, "|", UIColours.ToolbarText, UIColours.Background)

		Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, colours.grey)
		for i, button in ipairs(self.MenuBarItems) do
			button:Draw()
		end
	end,

	Initialise = function(self, items)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.X = 1
		new.Y = 1
		new.MenuBarItems = items
		return new
	end,

	AddToolbarItem = function(self, item)
		table.insert(self.ToolbarItems, item)
		self:CalculateToolbarItemPositions()
	end,

	CalculateToolbarItemPositions = function(self)
		local currY = 1
		for i, toolbarItem in ipairs(self.ToolbarItems) do
			toolbarItem.Y = currY
			currY = currY + toolbarItem.Height
		end
	end,

	Click = function(self, side, x, y)
		for i, item in ipairs(self.MenuBarItems) do
			if item.X <= x and item.X + item.Width > x then
				if item:Click(item, side, x - item.X + 1, 1) then
					return true
				end
			end
		end
		return false
	end
}

TextFormatPlainText = 1
TextFormatInkText = 2

Document = {
	X = 1,
	Y = 1,
	PageSize = {Width = 25, Height = 21},
	TextInput = nil,
	Pages = {},
	Format = TextFormatPlainText,
	Title = '',
	Path = nil,
	ScrollBar = nil,
	Lines = {},
	CursorPos = nil,

	CalculateLineWrapping = function(self)
		local limit = self.PageSize.Width
		local text = self.TextInput.Value
        local lines = {''}
        local words = {}

        for word, space in text:gmatch('(%S+)(%s*)') do
        	for i = 1, math.ceil(#word/limit) do
        		local _space = ''
        		if i == math.ceil(#word/limit) then
        			_space = space
        		end
        		table.insert(words, {word:sub(1+limit*(i-1), limit*i), _space})
        	end
        end

        for i, ws in ipairs(words) do
        		local word = ws[1]
        		local space = ws[2]
                local temp = lines[#lines] .. word .. space:gsub('\n','')
                if #temp > limit then
                    table.insert(lines, '')
                end
                if space:find('\n') then
                    lines[#lines] = lines[#lines] .. word
                    
                    space = space:gsub('\n', function()
                            table.insert(lines, '')
                            return ''
                    end)
                else
                    lines[#lines] = lines[#lines] .. word .. space
                end
        end
        return lines
	end,

	CalculateCursorPos = function(self)
		local passedCharacters = 0
		Current.CursorPos = nil
		for p, page in ipairs(self.Pages) do
			page:Draw()
			if not Current.CursorPos then
				for i, line in ipairs(page.Lines) do
					local relCursor = self.TextInput.CursorPos - FindColours(self.TextInput.Value:sub(1,self.TextInput.CursorPos))
					if passedCharacters + #StripColours(line.Text:gsub('\n','')) >= relCursor then
						Current.CursorPos = {self.X + page.MarginX + (relCursor - passedCharacters), page.Y + 1 + i}
						self.CursorPos = {Page = p, Line = i, Collum = relCursor - passedCharacters - FindColours(self.TextInput.Value:sub(1,self.TextInput.CursorPos-1))}
						break
					end
					passedCharacters = passedCharacters + #StripColours(line.Text:gsub('\n',''))
				end
			end
		end
	end,

	Draw = function(self)
		self:CalculatePages()
		self:CalculateCursorPos()
		self.ScrollBar:Draw()
	end,

	CalculatePages = function(self)
		self.Pages = {}
		local lines = self:CalculateLineWrapping()
		self.Lines = lines
		local pageLines = {}
		local totalPageHeight = (3 + self.PageSize.Height + 2 * Page.MarginY)
		for i, line in ipairs(lines) do
			table.insert(pageLines, TextLine:Initialise(line))
			if i % self.PageSize.Height == 0 then
				table.insert(self.Pages, Page:Initialise(self, pageLines, 3 - self.ScrollBar.Scroll + totalPageHeight*(#self.Pages)))
				pageLines = {}
			end
		end
		if #pageLines ~= 0 then
			table.insert(self.Pages, Page:Initialise(self, pageLines, 3 - self.ScrollBar.Scroll + totalPageHeight*(#self.Pages)))
		end

		self.ScrollBar.MaxScroll = totalPageHeight*(#self.Pages) - Drawing.Screen.Height + 1
	end,

	ScrollToCursor = function(self)
		self:CalculateCursorPos()
		if Current.CursorPos and 
			(Current.CursorPos[2] > Drawing.Screen.Height 
			or Current.CursorPos[2] < 2) then
			self.ScrollBar:DoScroll(Current.CursorPos[2] - Drawing.Screen.Height)
		end
	end,

	SetSelectionColour = function(self, colour)
		local selection = OrderSelection()
		local text = self.TextInput:Extract(true)
		local colChar = CharacterFromColour(colour)
		local precedingColour = ''
		if FindColours(self.TextInput.Value:sub(self.TextInput.CursorPos+1, self.TextInput.CursorPos+1)) == 0 then
			for i = 1, self.TextInput.CursorPos do
				local c = self.TextInput.Value:sub(self.TextInput.CursorPos - i,self.TextInput.CursorPos - i)
				if FindColours(c) == 1 then
					precedingColour = c
					break
				end
			end
			if precedingColour == '' then
				precedingColour = CharacterFromColour(colours.black)
			end
		end

		self.TextInput:Insert(colChar..StripColours(text)..precedingColour)
		--text = text:gsub('['..string.char(14)..'-'..string.char(29)..']','')
	end,

	Initialise = function(self, text, title, path)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Title = title or 'New Document'
		new.Path = path
		new.X = (Drawing.Screen.Width - (new.PageSize.Width + 2*(Page.MarginX)))/2
		new.Y = 2
		new.TextInput = TextInput:Initialise(text, function()
			new:ScrollToCursor() 
			Current.Modified = true
			Draw()
		end, false, true)
		new.ScrollBar = ScrollBar:Initialise(Drawing.Screen.Width, new.Y, Drawing.Screen.Height-1, 0, nil, nil, nil, function()end)
		Current.TextInput = new.TextInput
		Current.ScrollBar = new.ScrollBar
		return new
	end
}

ScrollBar = {
	X = 1,
	Y = 1,
	Width = 1,
	Height = 1,
	BackgroundColour = colours.grey,
	BarColour = colours.lightBlue,
	Parent = nil,
	Change = nil,
	Scroll = 0,
	MaxScroll = 0,
	ClickPoint = nil,

	AbsolutePosition = function(self)
		return self.Parent:AbsolutePosition()
	end,

	Draw = function(self)
		local pos = GetAbsolutePosition(self)
	    local barHeight = self.Height - self.MaxScroll
	    if barHeight < 3 then
	      barHeight = 3
	    end
	    local percentage = (self.Scroll/self.MaxScroll)

	    Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, self.BackgroundColour)
	    Drawing.DrawBlankArea(pos.X, pos.Y + round(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)
	end,

	Initialise = function(self, x, y, height, maxScroll, backgroundColour, barColour, parent, change)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 1
		new.Height = height
		new.Y = y
		new.X = x
		new.BackgroundColour = backgroundColour or colours.grey
		new.BarColour = barColour or colours.lightBlue
		new.Parent = parent
		new.Change = change or function()end
		new.MaxScroll = maxScroll
		new.Scroll = 0
		return new
	end,

	DoScroll = function(self, amount)
		amount = round(amount)
		if self.Scroll < 0 or self.Scroll > self.MaxScroll then
			return false
		end
		self.Scroll = self.Scroll + amount
		if self.Scroll < 0 then
			self.Scroll = 0
		elseif self.Scroll > self.MaxScroll then
			self.Scroll = self.MaxScroll
		end
		self.Change()
		return true
	end,

	Click = function(self, side, x, y, drag)
		local percentage = (self.Scroll/self.MaxScroll)
		local barHeight = (self.Height - self.MaxScroll)
		if barHeight < 3 then
			barHeight = 3
		end
		local relScroll = (self.MaxScroll*(y + barHeight*percentage)/self.Height)
		if not drag then
			self.ClickPoint = self.Scroll - relScroll + 1
		end

		if self.Scroll-1 ~= relScroll then
			self:DoScroll(relScroll-self.Scroll-1 + self.ClickPoint)
		end
		return true
	end
}

AlignmentLeft = 1
AlignmentCentre = 2
AlignmentRight = 3

TextLine = {
	Text = "",
	Alignment = AlignmentLeft,

	Initialise = function(self, text, alignment)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Text = text
		new.Alignment = alignment or AlignmentLeft
		return new
	end
}

local clickPos = 1
Page = {
	X = 1,
	Y = 1,
	Width = 1,
	Height = 1,
	MarginX = 3,
	MarginY = 2,
	BackgroundColour = colours.white,
	TextColour = colours.white,
	ActiveBackgroundColour = colours.lightGrey,
	Lines = {},
	Parent = nil,

	AbsolutePosition = function(self)
		return self.Parent:AbsolutePosition()
	end,

	Draw = function(self)
		local pos = GetAbsolutePosition(self)

		if pos.Y > Drawing.Screen.Height or pos.Y + self.Height < 1 then
			return
		end

		Drawing.DrawBlankArea(pos.X+self.Width,pos.Y -1 + 1, 1, self.Height, UIColours.Shadow)
		Drawing.DrawBlankArea(pos.X+1, pos.Y -1 + self.Height, self.Width, 1, UIColours.Shadow)
		Drawing.DrawBlankArea(pos.X, pos.Y -1, self.Width, self.Height, self.BackgroundColour)

		local textColour = self.TextColour
		if not Current.Selection then
			for i, line in ipairs(self.Lines) do
				local _c = 1
				for c = 1, #line.Text do
					local col = ColourFromCharacter(line.Text:sub(c,c))
					if col then
						textColour = col
					else
						Drawing.WriteToBuffer(pos.X + self.MarginX - 1 + _c, pos.Y -2 + i + self.MarginY, line.Text:sub(c,c), textColour, self.BackgroundColour)
						_c = _c + 1
					end
				end
			end
		else
			local selection = OrderSelection()
			local char = 1
			local textColour = self.TextColour
			for i, line in ipairs(self.Lines) do
				local _c = 1
				for c = 1, #line.Text do
					local col = ColourFromCharacter(line.Text:sub(c,c))
					if col then
						textColour = col
					else
						local tc = textColour
						local colour = colours.white
						if char >= selection[1] and char <= selection[2] then
							colour = colours.lightBlue
							tc = colours.white
						end

						Drawing.WriteToBuffer(pos.X + self.MarginX - 1 + _c, pos.Y -2 + i + self.MarginY, line.Text:sub(c,c), tc, colour)
						_c = _c + 1
					end
					char = char + 1
				end
			end
		end
	end,

	Initialise = function(self, parent, lines, y)
		local new = {}    -- the new instanc
		setmetatable( new, {__index = self} )
		new.Height = parent.PageSize.Height + 2 * self.MarginY
		new.Width = parent.PageSize.Width + 2 * self.MarginX
		new.X = 1
		new.Y = y or 1
		new.Lines = lines or {}
		new.BackgroundColour = colours.white
		new.TextColour = colours.black
		new.Parent = parent
		new.ClickPos = 1
		return new
	end,

	GetCursorPosFromPoint = function(self, x, y, rel)
		local pos = GetAbsolutePosition(self)
		if rel then
			pos = {Y = 0, X = 0}
		end
		local row = y - pos.Y + self.MarginY - self.Parent.ScrollBar.Scroll
		local col = x - self.MarginX - pos.X + 1
		local cursorPos = 0
		if row <= 0 or col <= 0 then
			return 0
		end

		if row > #self.Lines then
			for i, v in ipairs(self.Lines) do
				cursorPos = cursorPos + #v.Text-- - FindColours(v.Text)
			end
			return cursorPos
		end

		--term.setCursorPos(1,3)
		local prevLineCount = 0
		for i, v in ipairs(self.Lines) do
			if i == row then
				if col > #v.Text then
					col = #v.Text-- + FindColours(v.Text)
				else
					col = col + FindColours(v.Text:sub(1, col))
				end
				--term.setCursorPos(1,2)
				--print(prevLineCount)
				cursorPos = cursorPos + col + 2 - i - prevLineCount
				break
			else
				prevLineCount = FindColours(v.Text)
				if prevLineCount ~= 0 then
					prevLineCount = prevLineCount
				end
				cursorPos = cursorPos + #v.Text + 2 - i + FindColours(v.Text)
			end
		end

		return cursorPos - 2
	end,

	Click = function(self, side, x, y, drag)
		local cursorPos = self:GetCursorPosFromPoint(x, y)
		self.Parent.TextInput.CursorPos = cursorPos
		if drag == nil then
			Current.Selection = nil
			clickPos = x
		else
			local relCursor = cursorPos-- - FindColours(self.Parent.TextInput.Value:sub(1,cursorPos)) + 1
			if not Current.Selection then
				local adder = 1
				if clickPos and clickPos < x then
					adder = 0
				end
				Current.Selection = {relCursor + adder, relCursor + 1 + adder}
			else
				Current.Selection[2] = relCursor + 1
			end
		end
		Draw()
		return true
	end
}

function GetAbsolutePosition(object)
	local obj = object
	local i = 0
	local x = 1
	local y = 1
	while true do
		x = x + obj.X - 1
		y = y + obj.Y - 1

		if not obj.Parent then
			return {X = x, Y = y}
		end

		obj = obj.Parent

		if i > 32 then
			return {X = 1, Y = 1}
		end

		i = i + 1
	end

end

function Draw()
	if not Current.Window then
		Drawing.Clear(colours.lightGrey)
	else
		Drawing.DrawArea(1, 2, Drawing.Screen.Width, Drawing.Screen.Height, '|', colours.black, colours.lightGrey)
	end

	if Current.Document then
		Current.Document:Draw()
	end

	Current.MenuBar:Draw()

	if Current.Window then
		Current.Window:Draw()
	end

	if Current.Menu then
		Current.Menu:Draw()
	end

	Drawing.DrawBuffer()

	if Current.TextInput and Current.CursorPos and not Current.Menu and not(Current.Window and Current.Document and Current.TextInput == Current.Document.TextInput) and Current.CursorPos[2] > 1 then
		term.setCursorPos(Current.CursorPos[1], Current.CursorPos[2])
		term.setCursorBlink(true)
		term.setTextColour(Current.CursorColour)
	else
		term.setCursorBlink(false)
	end
end
MainDraw = Draw

LongestString = function(input, key)
	local length = 0
	for i = 1, #input do
		local value = input[i]
		if key then
			if value[key] then
				value = value[key]
			else
				value = ''
			end
		end
		local titleLength = string.len(value)
		if titleLength > length then
			length = titleLength
		end
	end
	return length
end

function LoadMenuBar()
	Current.MenuBar = MenuBar:Initialise({
		Button:Initialise(1, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle)
			if toggle then
				Menu:New(1, 2, {
					{
						Title = "New...",
						Click = function()
							Current.Document = Document:Initialise('')							
						end,
						Keys = {
							keys.leftCtrl,
							keys.n
						}
					},
					{
						Title = 'Open...',
						Click = function()
							DisplayOpenDocumentWindow()
						end,
						Keys = {
							keys.leftCtrl,
							keys.o
						}
					},
					{
						Separator = true
					},
					{
						Title = 'Save...',
						Click = function()
							SaveDocument()
						end,
						Keys = {
							keys.leftCtrl,
							keys.s
						},
						Enabled = function()
							return true
						end
					},
					{
						Separator = true
					},
					{
						Title = 'Print...',
						Click = function()
							PrintDocument()
						end,
						Keys = {
							keys.leftCtrl,
							keys.p
						},
						Enabled = function()
							return true
						end
					},
					{
						Separator = true
					},
					{
						Title = 'Quit',
						Click = function()
							Close()
						end
					},
			--[[
					{
						Title = 'Save As...',
						Click = function()

						end
					}	
			]]--
				}, self, true)
			else
				Current.Menu = nil
			end
			return true 
		end, 'File', colours.lightGrey, false),
		Button:Initialise(7, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle)
			if not self.Toggle then
				Menu:New(7, 2, {
			--[[
					{
						Title = "Undo",
						Click = function()
						end,
						Keys = {
							keys.leftCtrl,
							keys.z
						},
						Enabled = function()
							return false
						end
					},
					{
						Title = 'Redo',
						Click = function()
							
						end,
						Keys = {
							keys.leftCtrl,
							keys.y
						},
						Enabled = function()
							return false
						end
					},
					{
						Separator = true
					},
			]]--
					{
						Title = 'Cut',
						Click = function()
							Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text')
						end,
						Keys = {
							keys.leftCtrl,
							keys.x
						},
						Enabled = function()
							return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil
						end
					},
					{
						Title = 'Copy',
						Click = function()
							Clipboard.Copy(Current.Document.TextInput:Extract(), 'text')
						end,
						Keys = {
							keys.leftCtrl,
							keys.c
						},
						Enabled = function()
							return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil
						end
					},
					{
						Title = 'Paste',
						Click = function()
							local paste = Clipboard.Paste()
							Current.Document.TextInput:Insert(paste)
							Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1
						end,
						Keys = {
							keys.leftCtrl,
							keys.v
						},
						Enabled = function()
							return Current.Document ~= nil and (not Clipboard.isEmpty()) and Clipboard.Type == 'text'
						end
					},
					{
						Separator = true,	
					},
					{
						Title = 'Select All',
						Click = function()
							Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')}
						end,
						Keys = {
							keys.leftCtrl,
							keys.a
						},
						Enabled = function()
							return Current.Document ~= nil
						end
					}
				}, self, true)
			else
				Current.Menu = nil
			end
			return true 
		end, 'Edit', colours.lightGrey, false)
	})
end

function LoadMenuBar()
	Current.MenuBar = MenuBar:Initialise({
		Button:Initialise(1, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle)
			if toggle then
				Menu:New(1, 2, {
					{
						Title = "New...",
						Click = function()
							Current.Document = Document:Initialise('')							
						end,
						Keys = {
							keys.leftCtrl,
							keys.n
						}
					},
					{
						Title = 'Open...',
						Click = function()
							DisplayOpenDocumentWindow()
						end,
						Keys = {
							keys.leftCtrl,
							keys.o
						}
					},
					{
						Separator = true
					},
					{
						Title = 'Save...',
						Click = function()
							SaveDocument()
						end,
						Keys = {
							keys.leftCtrl,
							keys.s
						},
						Enabled = function()
							return Current.Document ~= nil
						end
					},
					{
						Separator = true
					},
					{
						Title = 'Print...',
						Click = function()
							PrintDocument()
						end,
						Keys = {
							keys.leftCtrl,
							keys.p
						},
						Enabled = function()
							return true
						end
					},
					{
						Separator = true
					},
					{
						Title = 'Quit',
						Click = function()
							if Close() and OneOS then
								OneOS.Close()
							end
						end,
						Keys = {
							keys.leftCtrl,
							keys.q
						}
					},
			--[[
					{
						Title = 'Save As...',
						Click = function()

						end
					}	
			]]--
				}, self, true)
			else
				Current.Menu = nil
			end
			return true 
		end, 'File', colours.lightGrey, false),
		Button:Initialise(7, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle)
			if not self.Toggle then
				Menu:New(7, 2, {
			--[[
					{
						Title = "Undo",
						Click = function()
						end,
						Keys = {
							keys.leftCtrl,
							keys.z
						},
						Enabled = function()
							return false
						end
					},
					{
						Title = 'Redo',
						Click = function()
							
						end,
						Keys = {
							keys.leftCtrl,
							keys.y
						},
						Enabled = function()
							return false
						end
					},
					{
						Separator = true
					},
			]]--
					{
						Title = 'Cut',
						Click = function()
							Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text')
						end,
						Keys = {
							keys.leftCtrl,
							keys.x
						},
						Enabled = function()
							return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil
						end
					},
					{
						Title = 'Copy',
						Click = function()
							Clipboard.Copy(Current.Document.TextInput:Extract(), 'text')
						end,
						Keys = {
							keys.leftCtrl,
							keys.c
						},
						Enabled = function()
							return Current.Document ~= nil and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil
						end
					},
					{
						Title = 'Paste',
						Click = function()
							local paste = Clipboard.Paste()
							Current.Document.TextInput:Insert(paste)
							Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1
						end,
						Keys = {
							keys.leftCtrl,
							keys.v
						},
						Enabled = function()
							return Current.Document ~= nil and (not Clipboard.isEmpty()) and Clipboard.Type == 'text'
						end
					},
					{
						Separator = true,	
					},
					{
						Title = 'Select All',
						Click = function()
							Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')}
						end,
						Keys = {
							keys.leftCtrl,
							keys.a
						},
						Enabled = function()
							return Current.Document ~= nil
						end
					}
				}, self, true)
			else
				Current.Menu = nil
			end
			return true 
		end, 'Edit', colours.lightGrey, false),
		Button:Initialise(13, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle)
			if not self.Toggle then
				Menu:New(13, 2, {
					{
						Title = 'Red',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.red,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Orange',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.orange,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Yellow',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.yellow,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Pink',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.pink,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Magenta',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.magenta,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Purple',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.purple,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Light Blue',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.lightBlue,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Cyan',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.cyan,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Blue',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.blue,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Green',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.green,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Light Grey',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.lightGrey,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Grey',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.grey,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Black',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.black,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					},
					{
						Title = 'Brown',
						Click = function(item)
							Current.Document:SetSelectionColour(item.Colour)
						end,
						Colour = colours.brown,
						Enabled = function()
							return (Current.Document ~= nil and Current.Selection ~= nil and Current.Selection[1] ~= nil and Current.Selection[2] ~= nil)
						end
					}
				}, self, true)
			else
				Current.Menu = nil
			end
			return true 
		end, 'Colour', colours.lightGrey, false)
	})
end

function Initialise(arg)
	if OneOS then
		fs = OneOS.FS
	end

	if not OneOS then
		SplashScreen()
	end
	EventRegister('mouse_click', TryClick)
	EventRegister('mouse_drag', function(event, side, x, y)TryClick(event, side, x, y, true)end)
	EventRegister('mouse_scroll', Scroll)
	EventRegister('key', HandleKey)
	EventRegister('char', HandleKey)
	EventRegister('timer', Timer)
	EventRegister('terminate', function(event) if Close() then error( "Terminated", 0 ) end end)
	
	term.setBackgroundColour(colours.lightGrey)
	term.clear()

	LoadMenuBar()

	--Current.Document = Document:Initialise('abcdefghijklmnopqrtuvwxy')--'Hello everybody!')
	if tArgs[1] then
		if fs.exists(tArgs[1]) then
			OpenDocument(tArgs[1])
		else
			--new
		end
	else
		Current.Document = Document:Initialise('')--'Hello everybody!')
	end

	--[[
	if arg and fs.exists(arg) then
			OpenDocument(arg)
		else
			DisplayNewDocumentWindow()
		end
	]]--
	Draw()

	EventHandler()
end

local isControlPushed = false
controlPushedTimer = nil
closeWindowTimer = nil
function Timer(event, timer)
	if timer == closeWindowTimer then
		if Current.Window then
			Current.Window:Close()
		end
		Draw()
	elseif timer == controlPushedTimer then
		isControlPushed = false
	end
end

local ignoreNextChar = false
function HandleKey(...)
	local args = {...}
	local event = args[1]
	local keychar = args[2]
																							--Mac left command character
	if event == 'key' and keychar == keys.leftCtrl or keychar == keys.rightCtrl or keychar == 219 then
		isControlPushed = true
		controlPushedTimer = os.startTimer(0.5)
	elseif isControlPushed then
		if event == 'key' then
			if CheckKeyboardShortcut(keychar) then
				isControlPushed = false
				ignoreNextChar = true
			end
		end
	elseif ignoreNextChar then
		ignoreNextChar = false
	elseif Current.TextInput then
		if event == 'char' then
			Current.TextInput:Char(keychar)
		elseif event == 'key' then
			Current.TextInput:Key(keychar)
		end
	end
end

function CheckKeyboardShortcut(key)
	local shortcuts = {}
	shortcuts[keys.n] = function() Current.Document = Document:Initialise('') end
	shortcuts[keys.o] = function() DisplayOpenDocumentWindow() end
	shortcuts[keys.s] = function() if Current.Document ~= nil then SaveDocument() end end
	shortcuts[keys.left] = function() if Current.TextInput then Current.TextInput:Key(keys.home) end end
	shortcuts[keys.right] = function() if Current.TextInput then Current.TextInput:Key(keys["end"]) end end
--	shortcuts[keys.q] = function() DisplayOpenDocumentWindow() end
	if Current.Document ~= nil then
		shortcuts[keys.s] = function() SaveDocument() end
		shortcuts[keys.p] = function() PrintDocument() end
		if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then
			shortcuts[keys.x] = function() Clipboard.Cut(Current.Document.TextInput:Extract(true), 'text') end
			shortcuts[keys.c] = function() Clipboard.Copy(Current.Document.TextInput:Extract(), 'text') end
		end
		if (not Clipboard.isEmpty()) and Clipboard.Type == 'text' then
			shortcuts[keys.v] = function() local paste = Clipboard.Paste()
									Current.Document.TextInput:Insert(paste)
									Current.Document.TextInput.CursorPos = Current.Document.TextInput.CursorPos + #paste - 1
								end
		end
		shortcuts[keys.a] = function() Current.Selection = {1, #Current.Document.TextInput.Value:gsub('\n','')} end
	end
							
	if shortcuts[key] then
		shortcuts[key]()
		Draw()
		return true
	else
		return false
	end
end

--[[
	Check if the given object falls under the click coordinates
]]--
function CheckClick(object, x, y)
	if object.X <= x and object.Y <= y and object.X + object.Width > x and object.Y + object.Height > y then
		return true
	end
end

--[[
	Attempt to clicka given object
]]--
function DoClick(object, side, x, y, drag)
	local obj = GetAbsolutePosition(object)
	obj.Width = object.Width
	obj.Height = object.Height
	if object and CheckClick(obj, x, y) then
		return object:Click(side, x - object.X + 1, y - object.Y + 1, drag)
	end	
end

--[[
	Try to click at the given coordinates
]]--
function TryClick(event, side, x, y, drag)
	if Current.Menu then
		if DoClick(Current.Menu, side, x, y, drag) then
			Draw()
			return
		else
			if Current.Menu.Owner and Current.Menu.Owner.Toggle then
				Current.Menu.Owner.Toggle = false
			end
			Current.Menu = nil
			Draw()
			return
		end
	elseif Current.Window then
		if DoClick(Current.Window, side, x, y, drag) then
			Draw()
			return
		else
			Current.Window:Flash()
			return
		end
	end
	local interfaceElements = {}

	table.insert(interfaceElements, Current.MenuBar)
	table.insert(interfaceElements, Current.ScrollBar)
	for i, page in ipairs(Current.Document.Pages) do
		table.insert(interfaceElements, page)
	end

	for i, object in ipairs(interfaceElements) do
		if DoClick(object, side, x, y, drag) then
			Draw()
			return
		end		
	end
	Draw()
end

function Scroll(event, direction, x, y)
	if Current.Window and Current.Window.OpenButton then
		Current.Document.Scroll = Current.Document.Scroll + direction
		if Current.Window.Scroll < 0 then
			Current.Window.Scroll = 0
		elseif Current.Window.Scroll > Current.Window.MaxScroll then
			Current.Window.Scroll = Current.Window.MaxScroll
		end
		Draw()
	elseif Current.ScrollBar then
		if Current.ScrollBar:DoScroll(direction*2) then
			Draw()
		end
	end
end


--[[
	Registers functions to run on certain events
]]--
function EventRegister(event, func)
	if not Events[event] then
		Events[event] = {}
	end

	table.insert(Events[event], func)
end

--[[
	The main loop event handler, runs registered event functinos
]]--
function EventHandler()
	while true do
		local event, arg1, arg2, arg3, arg4 = os.pullEventRaw()
		if Events[event] then
			for i, e in ipairs(Events[event]) do
				e(event, arg1, arg2, arg3, arg4)
			end
		end
	end
end


local function Extension(path, addDot)
	if not path then
		return nil
	elseif not string.find(fs.getName(path), '%.') then
		if not addDot then
			return fs.getName(path)
		else
			return ''
		end
	else
		local _path = path
		if path:sub(#path) == '/' then
			_path = path:sub(1,#path-1)
		end
		local extension = _path:gmatch('\.[0-9a-z]+$')()
		if extension then
			extension = extension:sub(2)
		else
			--extension = nil
			return ''
		end
		if addDot then
			extension = '.'..extension
		end
		return extension:lower()
	end
end

local RemoveExtension = function(path)
	if path:sub(1,1) == '.' then
		return path
	end
	local extension = Extension(path)
	if extension == path then
		return fs.getName(path)
	end
	return string.gsub(path, extension, ''):sub(1, -2)
end

local acknowledgedColour = false
function PrintDocument()
	if OneOS then
		OneOS.LoadAPI('/System/API/Helpers.lua')
		OneOS.LoadAPI('/System/API/Peripheral.lua')
		OneOS.LoadAPI('/System/API/Printer.lua')
	end

	local doPrint = function()
		local window = PrintDocumentWindow:Initialise():Show()
	end

	if Peripheral.GetPeripheral('printer') == nil then
		ButtonDialougeWindow:Initialise('No Printer Found', 'Please place a printer next to your computer. Ensure you also insert dye (left slot) and paper (top slots)', 'Ok', nil, function(window, ok)
			window:Close()
		end):Show()
	elseif not acknowledgedColour and FindColours(Current.Document.TextInput.Value) ~= 0 then
		ButtonDialougeWindow:Initialise('Important', 'Due to the way printers work, you can\'t print in more than one colour. The dye you use will be the colour of the text.', 'Ok', nil, function(window, ok)
			acknowledgedColour = true
			window:Close()
			doPrint()
		end):Show()
	else
		doPrint()
	end
end

function SaveDocument()
	local function save()
		local h = fs.open(Current.Document.Path, 'w')
		if h then
			if Current.Document.Format == TextFormatPlainText then
				h.write(Current.Document.TextInput.Value)
			else
				local lines = {}
				for p, page in ipairs(Current.Document.Pages) do
					for i, line in ipairs(page.Lines) do
						table.insert(lines, line)
					end
				end
				h.write(textutils.serialize(lines))
			end
			Current.Modified = false
		else
			ButtonDialougeWindow:Initialise('Error', 'An error occured while saving the file, try again.', 'Ok', nil, function(window, ok)
				window:Close()
			end):Show()
		end
		h.close()
	end

	if not Current.Document.Path then
		SaveDocumentWindow:Initialise(function(self, success, path)
			self:Close()
			if success then
				local extension = ''
				if Current.Document.Format == TextFormatPlainText then
					extension = '.txt'
				elseif Current.Document.Format == TextFormatInkText then
					extension = '.ink'
				end
				
				if path:sub(-4) ~= extension then
					path = path .. extension
				end

				Current.Document.Path = path
				Current.Document.Title = fs.getName(path)
				save()
			end
			if Current.Document then
				Current.TextInput = Current.Document.TextInput
			end
		end):Show()
	else
		save()
	end
end

function DisplayOpenDocumentWindow()
	OpenDocumentWindow:Initialise(function(self, success, path)
		self:Close()
		if success then
			OpenDocument(path)
		end
	end):Show()
end

function OpenDocument(path)
	Current.Selection = nil
	local h = fs.open(path, 'r')
	if h then
		Current.Document = Document:Initialise(h.readAll(), RemoveExtension(fs.getName(path)), path)
	else
		ButtonDialougeWindow:Initialise('Error', 'An error occured while opening the file, try again.', 'Ok', nil, function(window, ok)
			window:Close()
			if Current.Document then
				Current.TextInput = Current.Document.TextInput
			end
		end):Show()
	end
	h.close()
end

local TidyPath = function(path)
	path = '/'..path
	local fs = fs
	if OneOS then
		fs = OneOS.FS
	end
	if fs.isDir(path) then
		path = path .. '/'
	end

	path, n = path:gsub("//", "/")
	while n > 0 do
		path, n = path:gsub("//", "/")
	end
	return path
end

OpenDocumentWindow = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	CursorPos = 1,
	Visible = true,
	Return = nil,
	OpenButton = nil,
	PathTextBox = nil,
	CurrentDirectory = '/',
	Scroll = 0,
	MaxScroll = 0,
	GoUpButton = nil,
	SelectedFile = '',
	Files = {},
	Typed = false,

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		if not self.Visible then
			return
		end
		Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey)
		Drawing.DrawBlankArea(self.X, self.Y, self.Width, 3, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-6, colours.white)
		Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y + self.Height - 5, self.Width, 5, colours.lightGrey)
		self:DrawFiles()

		if (fs.exists(self.PathTextBox.TextInput.Value)) or (self.SelectedFile and #self.SelectedFile > 0 and fs.exists(self.CurrentDirectory .. self.SelectedFile)) then
			self.OpenButton.TextColour = colours.black
		else
			self.OpenButton.TextColour = colours.lightGrey
		end

		self.PathTextBox:Draw()
		self.OpenButton:Draw()
		self.CancelButton:Draw()
		self.GoUpButton:Draw()
	end,

	DrawFiles = function(self)
		for i, file in ipairs(self.Files) do
			if i > self.Scroll and i - self.Scroll <= 11 then
				if file == self.SelectedFile then
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.white, colours.lightBlue)
				elseif string.find(file, '%.txt') or string.find(file, '%.text') or string.find(file, '%.ink') or fs.isDir(self.CurrentDirectory .. file) then
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.black, colours.white)
				else
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.grey, colours.white)
				end
			end
		end
		self.MaxScroll = #self.Files - 11
		if self.MaxScroll < 0 then
			self.MaxScroll = 0
		end
	end,

	Initialise = function(self, returnFunc)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 32
		new.Height = 17
		new.Return = returnFunc
		new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2)
		new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2)
		new.Title = 'Open Document'
		new.Visible = true
		new.CurrentDirectory = '/'
		new.SelectedFile = nil
		if OneOS and fs.exists('/Desktop/Documents/') then
			new.CurrentDirectory = '/Desktop/Documents/'
		end
		new.OpenButton = Button:Initialise(new.Width - 6, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			if fs.exists(new.PathTextBox.TextInput.Value) and self.TextColour == colours.black and not fs.isDir(new.PathTextBox.TextInput.Value) then
				returnFunc(new, true, TidyPath(new.PathTextBox.TextInput.Value))
			elseif new.SelectedFile and self.TextColour == colours.black and fs.isDir(new.CurrentDirectory .. new.SelectedFile) then
				new:GoToDirectory(new.CurrentDirectory .. new.SelectedFile)
			elseif new.SelectedFile and self.TextColour == colours.black then
				returnFunc(new, true, TidyPath(new.CurrentDirectory .. '/' .. new.SelectedFile))
			end
		end, 'Open', colours.black)
		new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			returnFunc(new, false)
		end, 'Cancel', colours.black)
		new.GoUpButton = Button:Initialise(2, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			local folderName = fs.getName(new.CurrentDirectory)
			local parentDirectory = new.CurrentDirectory:sub(1, #new.CurrentDirectory-#folderName-1)
			new:GoToDirectory(parentDirectory)
		end, 'Go Up', colours.black)
		new.PathTextBox = TextBox:Initialise(2, new.Height - 3, new.Width - 2, 1, new, new.CurrentDirectory, colours.white, colours.black)
		new:GoToDirectory(new.CurrentDirectory)
		return new
	end,

	Show = function(self)
		Current.Window = self
		return self
	end,

	Close = function(self)
		Current.Input = nil
		Current.Window = nil
		self = nil
	end,

	GoToDirectory = function(self, path)
		path = TidyPath(path)
		self.CurrentDirectory = path
		self.Scroll = 0
		self.SelectedFile = nil
		self.Typed = false
		self.PathTextBox.TextInput.Value = path
		local fs = fs
		if OneOS then
			fs = OneOS.FS
		end
		self.Files = fs.list(self.CurrentDirectory)
		Draw()
	end,

	Flash = function(self)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
		sleep(0.15)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
	end,

	Click = function(self, side, x, y)
		local items = {self.OpenButton, self.CancelButton, self.PathTextBox, self.GoUpButton}
		local found = false
		for i, v in ipairs(items) do
			if CheckClick(v, x, y) then
				v:Click(side, x, y)
				found = true
			end
		end

		if not found then
			if y <= 12 then
				local fs = fs
				if OneOS then
					fs = OneOS.FS
				end
				self.SelectedFile = fs.list(self.CurrentDirectory)[y-1]
				self.PathTextBox.TextInput.Value = TidyPath(self.CurrentDirectory .. '/' .. self.SelectedFile)
				Draw()
			end
		end
		return true
	end
}

PrintDocumentWindow = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	CursorPos = 1,
	Visible = true,
	Return = nil,
	PrintButton = nil,
	CopiesTextBox = nil,
	Scroll = 0,
	MaxScroll = 0,
	PrinterSelectButton = nil,
	Title = '',
	Status = 0, --0 = neutral, 1 = good, -1 = error
	StatusText = '',
	SelectedPrinter = nil,

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		if not self.Visible then
			return
		end
		Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey)
		Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white)
		Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey)
		
		self.PrinterSelectButton:Draw()
		Drawing.DrawCharactersCenter(self.X,  self.Y + self.PrinterSelectButton.Y - 2, self.Width, 1, 'Printer', colours.black, colours.white)
		Drawing.DrawCharacters(self.X + self.Width - 3, self.Y + self.PrinterSelectButton.Y - 1, '\\/', colours.black, colours.lightGrey)
		Drawing.DrawCharacters(self.X + 1, self.Y + self.CopiesTextBox.Y - 1, 'Copies', colours.black, colours.white)
		local statusColour = colours.grey
		if self.Status == -1 then
			statusColour = colours.red
		elseif self.Status == 1 then
			statusColour = colours.green
		end
		Drawing.DrawCharacters(self.X + 1, self.Y + self.CopiesTextBox.Y + 1, self.StatusText, statusColour, colours.white)

		self.CopiesTextBox:Draw()
		self.PrintButton:Draw()
		self.CancelButton:Draw()
	end,

	Initialise = function(self)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 32
		new.Height = 11
		new.Return = returnFunc
		new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2)
		new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2)
		new.Title = 'Print Document'
		new.Visible = true
		new.PrintButton = Button:Initialise(new.Width - 7, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle)
			local doPrint = true
			if new.SelectedPrinter == nil then
				local p = Peripheral.GetPeripheral('printer')
				if p then
					new.SelectedPrinter = p.Side
					new.PrinterSelectButton.Text = p.Fullname
				else
					new.StatusText = 'No Connected Printer'
					new.Status = -1
					doPrint = false
				end
			end
			if doPrint then
				local printer = Printer:Initialise()
				local err = printer:PrintLines(Current.Document.Lines, Current.Document.Title, tonumber(new.CopiesTextBox.TextInput.Value))
				if not err then
					new.StatusText = 'Document Printed!'
					new.Status = 1
					closeWindowTimer = os.startTimer(1)
				else
					new.StatusText = err
					new.Status = -1
				end
			end
		end, 'Print', colours.black)
		new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle)
			new:Close()
			Draw()
		end, 'Close', colours.black)
		new.PrinterSelectButton = Button:Initialise(2, 4, new.Width - 2, nil, colours.lightGrey, new, function(self, side, x, y, toggle)
			local printers = {
					{
						Title = "Automatic",
						Click = function()
							new.SelectedPrinter = nil
							new.PrinterSelectButton.Text = 'Automatic'
						end
					},
					{
						Separator = true
					}
				}
			for i, p in ipairs(Peripheral.GetPeripherals('printer')) do
				table.insert(printers, {
					Title = p.Fullname,
					Click = function(self)
						new.SelectedPrinter = p.Side
						new.PrinterSelectButton.Text = p.Fullname
					end
				})
			end
			Current.Menu = Menu:New(x, y+4, printers, self, true)
		end, 'Automatic', colours.black)
		new.CopiesTextBox = TextBox:Initialise(9, 6, 4, 1, new, 1, colours.lightGrey, colours.black, nil, true)
		Current.TextInput = new.CopiesTextBox.TextInput
		new.StatusText = 'Waiting...'
		new.Status = 0
		return new
	end,

	Show = function(self)
		Current.Window = self
		return self
	end,

	Close = function(self)
		Current.Input = nil
		Current.Window = nil
		self = nil
	end,

	Flash = function(self)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
		sleep(0.15)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
	end,

	Click = function(self, side, x, y)
		local items = {self.PrintButton, self.CancelButton, self.CopiesTextBox, self.PrinterSelectButton}
		for i, v in ipairs(items) do
			if CheckClick(v, x, y) then
				v:Click(side, x, y)
			end
		end
		return true
	end
}

SaveDocumentWindow = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	CursorPos = 1,
	Visible = true,
	Return = nil,
	SaveButton = nil,
	PathTextBox = nil,
	CurrentDirectory = '/',
	Scroll = 0,
	MaxScroll = 0,
	ScrollBar = nil,
	GoUpButton = nil,
	Files = {},
	Typed = false,

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		if not self.Visible then
			return
		end
		Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey)
		Drawing.DrawBlankArea(self.X, self.Y, self.Width, 3, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-6, colours.white)
		Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y + self.Height - 5, self.Width, 5, colours.lightGrey)
		Drawing.DrawCharacters(self.X + 1, self.Y + self.Height - 5, self.CurrentDirectory, colours.grey, colours.lightGrey)
		self:DrawFiles()

		if (self.PathTextBox.TextInput.Value) then
			self.SaveButton.TextColour = colours.black
		else
			self.SaveButton.TextColour = colours.lightGrey
		end

		self.PathTextBox:Draw()
		self.SaveButton:Draw()
		self.CancelButton:Draw()
		self.GoUpButton:Draw()
	end,

	DrawFiles = function(self)
		for i, file in ipairs(self.Files) do
			if i > self.Scroll and i - self.Scroll <= 10 then
				if file == self.SelectedFile then
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.white, colours.lightBlue)
				elseif fs.isDir(self.CurrentDirectory .. file) then
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.black, colours.white)
				else
					Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.lightGrey, colours.white)
				end
			end
		end
		self.MaxScroll = #self.Files - 11
		if self.MaxScroll < 0 then
			self.MaxScroll = 0
		end
	end,

	Initialise = function(self, returnFunc)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 32
		new.Height = 16
		new.Return = returnFunc
		new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2)
		new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2)
		new.Title = 'Save Document'
		new.Visible = true
		new.CurrentDirectory = '/'
		if OneOS and fs.exists('/Desktop/Documents/') then
			new.CurrentDirectory = '/Desktop/Documents/'
		end
		new.SaveButton = Button:Initialise(new.Width - 6, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			if self.TextColour == colours.black and not fs.isDir(new.CurrentDirectory ..'/' .. new.PathTextBox.TextInput.Value) then
				returnFunc(new, true, TidyPath(new.CurrentDirectory ..'/' .. new.PathTextBox.TextInput.Value))
			elseif new.SelectedFile and self.TextColour == colours.black and fs.isDir(new.CurrentDirectory .. new.SelectedFile) then
				new:GoToDirectory(new.CurrentDirectory .. new.SelectedFile)
			end
		end, 'Save', colours.black)
		new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			returnFunc(new, false)
		end, 'Cancel', colours.black)
		new.GoUpButton = Button:Initialise(2, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle)
			local folderName = fs.getName(new.CurrentDirectory)
			local parentDirectory = new.CurrentDirectory:sub(1, #new.CurrentDirectory-#folderName-1)
			new:GoToDirectory(parentDirectory)
		end, 'Go Up', colours.black)
		new.PathTextBox = TextBox:Initialise(2, new.Height - 3, new.Width - 2, 1, new, '', colours.white, colours.black, function(key)
			if key == keys.enter then
				new.SaveButton:Click()
			end
		end)
		new.PathTextBox.Placeholder = 'Document Name'
		Current.TextInput = new.PathTextBox.TextInput
		new:GoToDirectory(new.CurrentDirectory)
		return new
	end,

	Show = function(self)
		Current.Window = self
		return self
	end,

	Close = function(self)
		Current.Input = nil
		Current.Window = nil
		self = nil
	end,

	GoToDirectory = function(self, path)
		path = TidyPath(path)
		self.CurrentDirectory = path
		self.Scroll = 0
		self.Typed = false
		local fs = fs
		if OneOS then
			fs = OneOS.FS
		end
		self.Files = fs.list(self.CurrentDirectory)
		Draw()
	end,

	Flash = function(self)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
		sleep(0.15)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
	end,

	Click = function(self, side, x, y)
		local items = {self.SaveButton, self.CancelButton, self.PathTextBox, self.GoUpButton}
		local found = false
		for i, v in ipairs(items) do
			if CheckClick(v, x, y) then
				v:Click(side, x, y)
				found = true
			end
		end

		if not found then
			if y <= 11 then
				local files = fs.list(self.CurrentDirectory)
				if files[y-1] then
					self:GoToDirectory(self.CurrentDirectory..files[y-1])
					Draw()
				end
			end
		end
		return true
	end
}

local WrapText = function(text, maxWidth)
	local lines = {''}
    for word, space in text:gmatch('(%S+)(%s*)') do
            local temp = lines[#lines] .. word .. space:gsub('\n','')
            if #temp > maxWidth then
                    table.insert(lines, '')
            end
            if space:find('\n') then
                    lines[#lines] = lines[#lines] .. word
                    
                    space = space:gsub('\n', function()
                            table.insert(lines, '')
                            return ''
                    end)
            else
                    lines[#lines] = lines[#lines] .. word .. space
            end
    end
	return lines
end

ButtonDialougeWindow = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	CursorPos = 1,
	Visible = true,
	CancelButton = nil,
	OkButton = nil,
	Lines = {},

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		if not self.Visible then
			return
		end
		Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey)
		Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white)
		Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey)

		for i, text in ipairs(self.Lines) do
			Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white)
		end

		self.OkButton:Draw()
		if self.CancelButton then
			self.CancelButton:Draw()
		end
	end,

	Initialise = function(self, title, message, okText, cancelText, returnFunc)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 28
		new.Lines = WrapText(message, new.Width - 2)
		new.Height = 5 + #new.Lines
		new.Return = returnFunc
		new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2)
		new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2)
		new.Title = title
		new.Visible = true
		new.Visible = true
		new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function()
			returnFunc(new, true)
		end, okText)
		if cancelText then
			new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function()
				returnFunc(new, false)
			end, cancelText)
		end

		return new
	end,

	Show = function(self)
		Current.Window = self
		return self
	end,

	Close = function(self)
		Current.Window = nil
		self = nil
	end,

	Flash = function(self)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
		sleep(0.15)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
	end,

	Click = function(self, side, x, y)
		local items = {self.OkButton, self.CancelButton}
		local found = false
		for i, v in ipairs(items) do
			if CheckClick(v, x, y) then
				v:Click(side, x, y)
				found = true
			end
		end
		return true
	end
}

TextDialougeWindow = {
	X = 1,
	Y = 1,
	Width = 0,
	Height = 0,
	CursorPos = 1,
	Visible = true,
	CancelButton = nil,
	OkButton = nil,
	Lines = {},
	TextInput = nil,

	AbsolutePosition = function(self)
		return {X = self.X, Y = self.Y}
	end,

	Draw = function(self)
		if not self.Visible then
			return
		end
		Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey)
		Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey)
		Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white)
		Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey)

		for i, text in ipairs(self.Lines) do
			Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white)
		end


		Drawing.DrawBlankArea(self.X + 1, self.Y + self.Height - 4, self.Width - 2, 1, colours.lightGrey)
		Drawing.DrawCharacters(self.X + 2, self.Y + self.Height - 4, self.TextInput.Value, colours.black, colours.lightGrey)
		Current.CursorPos = {self.X + 2 + self.TextInput.CursorPos, self.Y + self.Height - 4}
		Current.CursorColour = colours.black

		self.OkButton:Draw()
		if self.CancelButton then
			self.CancelButton:Draw()
		end
	end,

	Initialise = function(self, title, message, okText, cancelText, returnFunc, numerical)
		local new = {}    -- the new instance
		setmetatable( new, {__index = self} )
		new.Width = 28
		new.Lines = WrapText(message, new.Width - 2)
		new.Height = 7 + #new.Lines
		new.Return = returnFunc
		new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2)
		new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2)
		new.Title = title
		new.Visible = true
		new.Visible = true
		new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function()
			if #new.TextInput.Value > 0 then
				returnFunc(new, true, new.TextInput.Value)
			end
		end, okText)
		if cancelText then
			new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function()
				returnFunc(new, false)
			end, cancelText)
		end
		new.TextInput = TextInput:Initialise('', function(enter)
			if enter then
				new.OkButton:Click()
			end
			Draw()
		end, numerical)

		Current.Input = new.TextInput

		return new
	end,

	Show = function(self)
		Current.Window = self
		return self
	end,

	Close = function(self)
		Current.Window = nil
		Current.Input = nil
		self = nil
	end,

	Flash = function(self)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
		sleep(0.15)
		self.Visible = false
		Draw()
		sleep(0.15)
		self.Visible = true
		Draw()
	end,

	Click = function(self, side, x, y)
		local items = {self.OkButton, self.CancelButton}
		local found = false
		for i, v in ipairs(items) do
			if CheckClick(v, x, y) then
				v:Click(side, x, y)
				found = true
			end
		end
		return true
	end
}

function DoVanillaClose()
	term.setBackgroundColour(colours.black)
	term.setTextColour(colours.white)
	term.clear()
	term.setCursorPos(1, 1)
	PrintCentered("Thanks for using Sketch!", (Drawing.Screen.Height/2)-1)
	term.setTextColour(colours.lightGrey)
	PrintCentered("Photoshop Inspired Image Editor for ComputerCraft", (Drawing.Screen.Height/2))
	term.setTextColour(colours.white)
	PrintCentered("(c) oeed 2013 - 2014", (Drawing.Screen.Height/2)+3)
	term.setCursorPos(1, Drawing.Screen.Height)
	error('', 0)
end

function Close()
	if isQuitting or not Current.Document or not Current.Modified then
		if not OneOS then
			DoVanillaClose()
		end
		return true
	else
		local _w = ButtonDialougeWindow:Initialise('Quit Ink?', 'You have unsaved changes, do you want to quit anyway?', 'Quit', 'Cancel', function(window, success)
			if success then
				if OneOS then
					OneOS.Close(true)
				else
					DoVanillaClose()
				end
			end
			window:Close()
			Draw()
		end):Show()
		--it's hacky but it works
		os.queueEvent('mouse_click', 1, _w.X, _w.Y)
		return false
	end
end

if OneOS then
	OneOS.CanClose = function()
		return Close()
	end
end

Initialise()