OneOS.ToolBarColour = colours.grey
OneOS.ToolBarTextColour = colours.white

OneOS.LoadAPI('/System/API/Drawing.lua')
OneOS.LoadAPI('/System/API/Button.lua')
OneOS.LoadAPI('/System/API/Helpers.lua')
OneOS.LoadAPI('/System/API/Peripheral.lua')
OneOS.LoadAPI('/System/API/ScrollBar.lua')
OneOS.LoadAPI('/System/API/Menu.lua')
OneOS.LoadAPI('/System/API/TextInput.lua')
OneOS.LoadAPI('/System/API/ButtonDialogueWindow.lua')
OneOS.LoadAPI('/System/API/TextDialogueWindow.lua')

Current = {
	Clicks = {},
	Page = 2,
	MEController = nil,
	Items = {},
	Resources = {},
	Menu = nil,
	Input = nil,
	CursorPos = {1,1},
	CursorColour = colours.black,
}

CustomisedResources = {}

local appliedEnergisticsActive = false
local openpInstalled = false
ts = textutils.serialize

Events = {
	
}

InterfaceElements = {
	
}

Pages = {
	Items = 1,
	Resources = 2,
	Control = 3
}

function Initialise()
	EventRegister('mouse_click', TryClick)
	EventRegister('mouse_drag', TryClick)
	EventRegister('timer', Update)
	EventRegister('key', HandleKey)
	EventRegister('char', HandleKey)

	LoadPrefs()

	if OneOS.FS.exists('/openp/') and OneOS.FS.isDir('/openp/') then
		openpInstalled = true
	end
	
	itemsButton = Button:Initialise(2, 2, nil, 1, colours.lightGrey, nil, colours.lightBlue, nil, nil, function(self, x, y, toggle) ChangePage(Pages.Items) end, 'Items', Current.Page == Pages.Items):Register()
	resourcesButton = Button:Initialise(10, 2, nil, 1, colours.lightGrey, nil, colours.lightBlue, nil, nil, function(self, x, y, toggle) ChangePage(Pages.Resources) end, 'Resources', Current.Page == Pages.Resources):Register()
	controlButton = Button:Initialise(22, 2, nil, 1, colours.lightGrey, nil, colours.lightBlue, nil, nil, function(self, x, y, toggle) ChangePage(Pages.Controller) end, 'Switches', Current.Page == Pages.Controller):Register()
	itemsScrollBar = ScrollBar:Initialise(Drawing.Screen.Width, 5, Drawing.Screen.Height - 4, 10, nil, nil, nil, function()end)

	Update(true)
	EventHandler()
		--[[
Drawing.Clear(colours.grey)
		Drawing.DrawCharactersCenter(0, -4, nil, nil, 'OpenPeripheral not installed!', colours.white, colours.grey)
		Drawing.DrawCharactersCenter(0, -4, nil, nil, 'Inspector is a program which monitors various things around your base. However, it required OpenPeripheral to be installed, please', colours.white, colours.grey)
		Drawing.DrawBuffer()
		os.pullEvent('mouse_click')
		OneOS.Close()
]]--
end

function ChangePage(page)
	Current.Page = page
	itemsButton.Toggle = false
	resourcesButton.Toggle = false
	controlButton.Toggle = false
	Update()
end

local function contains(table, element)
  for _, value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end

function ItemKey(item)
	return item.rawName .. ':' .. item.dmg
end

function UpdateItems(isFirst)
	local avilableItems = Current.MEController.getAvailableItems()
	local oldItems = Current.Items
	local proccessedItems = {}
	for i, item in ipairs(avilableItems) do
		local change = 0
		if Current.Items[ItemKey(item)] then
			change = Current.Items[ItemKey(item)].Change + item.qty - Current.Items[ItemKey(item)].Quantity
		elseif not isFirst then
			change = item.qty
		end
		Current.Items[ItemKey(item)] = {
			Name = item.name,
			RawName = item.rawName,
			Quantity = item.qty,
			ItemID = item.id,
			Change = change,
			Damage = item.damage
		}
		table.insert(proccessedItems, ItemKey(item))
	end

	for key, item in pairs(oldItems) do
		if not contains(proccessedItems, key) then
			if item.Quantity ~= 0 then
				Current.Items[key].Change = 0-item.Quantity
				Current.Items[key].Quantity = 0
			else
				Current.Items[key].Change = item.Change
			end
		end	
	end
end

function UpdateResourceMonitor()
	local resourcePeripheralTypes = {'rcirontankvalvetile', 'rcsteeltankvalvetile', 'net_minecraft_src_buildcraft_factory_tiletank', 'openblocks_tank', 'cofh_thermalexpansion_energycell'}
	local peripherals = Peripheral.GetPeripherals()
	Current.Resources = {}
	for i, v in ipairs(peripherals) do
		for _, _type in ipairs(resourcePeripheralTypes) do
			if v.Type == _type then
				local fluid = nil
				local percentage = 0
				if v.Type == 'cofh_thermalexpansion_energycell' then
					fluid = 'RF Energy'
					local min = Peripheral.CallNamed(v.Side, 'getEnergyStored', '1')
					local max = Peripheral.CallNamed(v.Side, 'getMaxEnergyStored', '1')
					percentage = min / max
				else
					local tankData = Peripheral.CallNamed(v.Side, 'getTankInfo', v.Side)
					if tankData[1] then
						fluid = tankData[1].rawName
						if fluid then
							percentage = tankData[1].amount / tankData[1].capacity
						end
					end
				end

				if not fluid then
					fluid = 'Empty'
				end
				table.insert(Current.Resources, {
					Side = v.Side,
					Type = v.Type,
					Fluid = fluid,
					Percentage = percentage
				})
				Current.Resources[#Current.Resources].Name = GetResourcePrefs(Current.Resources[#Current.Resources], false).Name
				break
			end
		end
	end
end

function Update(isFirst)
	if isFirst ~= true then
		isFirst = false
	end 
	if isFirst or Current.Page == Pages.Items then
		local meController = Peripheral.GetPeripheral('appeng_me_tilecontroller')
		if meController then
			appliedEnergisticsActive = true
			Current.MEController = peripheral.wrap(meController.Side)
			UpdateItems(isFirst)
		else
			appliedEnergisticsActive = false
			Current.MEController = nil
		end
	end

	if isFirst or Current.Page == Pages.Resources then
		UpdateResourceMonitor()
	end

	Draw()
	updateTimer = os.startTimer(10)
end

function DrawItems()
	local collumnWidths = {
		Qty = 6,
		Name = 40,
		Chng = 6
	}
	local y = 4
	local odd = false
	local x = 1
	for name, width in pairs(collumnWidths) do
		local colour = colours.grey
		--[[
		if odd then
			colour = colours.lightGrey
		end
		odd = not odd
		]]--
		Drawing.DrawBlankArea(x, y, width, 1, colour)
		Drawing.DrawCharacters(x + 1, y, Helpers.TruncateString(tostring(name), width), colours.white, colour)
		x = x  + width
	end
	y = y + 1
	odd = false
	for rawName, item in pairs(Current.Items) do
		x = 1
		local bgColour = colours.black
		local textColour = colours.white
		if odd then
			bgColour = colours.black
			textColour = colours.lightGrey
		end
		odd = not odd
		Drawing.DrawBlankArea(1, y, Drawing.Screen.Width, 1, bgColour)

		for name, width in pairs(collumnWidths) do
			if name == 'Qty' then
				local colour = bgColour
				local tc = textColour
				if item.Quantity == 0 then
					colour = colours.red
					tc = colours.white
				end
				Drawing.DrawBlankArea(x, y, width, 1, colour)
				Drawing.DrawCharacters(x + 1, y, Helpers.TruncateString(tostring(item.Quantity), width - 2), tc, colour)
			elseif name == 'Chng' then
				local colour = bgColour
				local tc = textColour
				if item.Change > 0 then
					colour = colours.green
					tc = colours.white
				elseif item.Change < 0 then
					colour = colours.red
					tc = colours.white
				end
				Drawing.DrawBlankArea(x, y, width, 1, colour)
				Drawing.DrawCharacters(x + 1, y, Helpers.TruncateString(tostring(item.Change), width - 2), tc, colour)
			elseif name == 'Name' then
				Drawing.DrawBlankArea(x, y, width, 1, bgColour)
				Drawing.DrawCharacters(x + 1, y, Helpers.TruncateString(tostring(item.Name), width - 2), textColour, bgColour)
			end
			x = x + width
		end
		y = y + 1
	end

	itemsScrollBar:Draw()
end

local resourceWidth = 15
local resourceHeight = 3

local fluidColour = {
	['Lava'] = colours.orange,
	['Water'] = colours.blue,
	['Milk'] = colours.white,
	['Ethanol'] = colours.orange,
	['Creosote'] = colours.brown,
	['Acid'] = colours.lime,
	['Poison'] = colours.magenta,
	['Liquid Nitrogen'] = colours.cyan,
	['Sap'] = colours.orange,
	['Resin'] = colours.orange,
	['Latex'] = colours.lightGrey,
	['Turpentine'] = colours.brown,
	['Sweage'] = colours.brown,
	['Sludge'] = colours.purple,
	['Mob Essence'] = colours.green,
	['BioFuel'] = colours.purple,
	['Meat'] = colours.pink,
	['Pink Slime'] = colours.pink,
	['Chocolate Milk'] = colours.brown,
	['Mushroom Soup'] = colours.brown,
	['Toxic Waste'] = colours.brown,
	['Energized Glowstone'] = colours.yellow,
	['Resonant Ender'] = colours.cyan,
	['Blazing Pyrotheum'] = colours.orange,
	['Destabilized Redstone'] = colours.red,
	['Gelid Cryotheum'] = colours.lightBlue,
	['Liquifacted Coal'] = colours.lightGrey,
	['RF Energy'] = colours.red,
	['Oil'] = colours.lightGrey,
	['Liquid XP'] = colours.lime,
	['Fuel'] = colours.yellow,
	['Default'] = colours.green
}

function DrawResource(x, y, resource)
	local textColour = colours.lightGrey
	local bgColour = colours.transparent
	local barBg = colours.grey
	Drawing.DrawBlankArea(x, y, resourceWidth, 1, bgColour)
	Drawing.DrawCharactersCenter(x, y, resourceWidth, 1, Helpers.TruncateString(resource.Name, resourceWidth), textColour, bgColour)
	Drawing.DrawBlankArea(x, y + 1, resourceWidth, 1, barBg)
	local colour = fluidColour[resource.Fluid]
	if not colour then
		colour = fluidColour['Default']
	end

	if resource.Percentage > 0 then
		Drawing.DrawBlankArea(x, y + 1, math.ceil(resource.Percentage * resourceWidth), 1, colour)
	end
	
	local text = math.ceil(100*resource.Percentage) .. '%'

	Drawing.DrawCharactersCenter(x, y + 1, resourceWidth, 1, text, colours.white, colours.transparent)
end

function EachResource(func)
	local xStart = 2
	local x = xStart
	local y = 5
	local paddingX = 2
	local paddingY = 1
	for i, resource in ipairs(Current.Resources) do
		if GetResourcePrefs(resource, false).Hidden ~= true then
			if func(x, y, resource) then
				return
			end
			x = x + resourceWidth + paddingX
			if x + resourceWidth - paddingX + 1 >= Drawing.Screen.Width then
				x = xStart
				y = y + resourceHeight
			end
		end
	end
end

function DrawResources()
	EachResource(function(x, y, resource)
		DrawResource(x, y, resource)
	end)
end

function SavePrefs()
	local h = fs.open('.Resources.settings', 'w')
	if h then
		h.write(textutils.serialize(CustomisedResources))
		h.close()
	end
end

function LoadPrefs()
	local h = fs.open('.Resources.settings', 'r')
	if h then
		CustomisedResources = textutils.unserialize(h.readAll())
		h.close()
	end
	if not CustomisedResources or type(CustomisedResources) ~= 'table' then
		CustomisedResources = {}
	end
end

function SetResourcePrefs(resource, prefs, save)
	if not prefs then
		prefs = {}
	end

	if prefs.Hidden == nil then
		prefs.Hidden = false
	end

	if prefs.Name == nil or prefs.Name == '' then
		if CustomisedResources[resource.Side] then
			prefs.Name = CustomisedResources[resource.Side].Name
		else
			prefs.Name = resource.Fluid
		end
	end

	if save ~= false then
		CustomisedResources[resource.Side] = prefs
		SavePrefs()
	end
	return prefs
end

function GetResourcePrefs(resource, save)
	if not CustomisedResources[resource.Side] then
		return SetResourcePrefs(resource, nil, save)
	end
	return CustomisedResources[resource.Side]
end

function SetResourceHidden(resource, hidden)
	SetResourcePrefs(resource, {Hidden = hidden})
end

function SetResourceName(resource, name)
	SetResourcePrefs(resource, {Name = name})
end

function Draw()
	Drawing.Clear(colours.black)

	Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, 3, colours.grey)

	for i, elem in ipairs(InterfaceElements) do
		if elem.Draw then
			elem:Draw()
		end
	end

	if Current.Page == Pages.Items then
		if appliedEnergisticsActive then
			DrawItems()
		elseif not openpInstalled then
			Drawing.DrawBlankArea(1, 4, Drawing.Screen.Width, Drawing.Screen.Height - 1, colours.grey)
			Drawing.DrawCharactersCenter(0, -4, nil, nil, 'OpenPeripheral Not Installed!', colours.white, colours.grey)
			local lines = Helpers.WrapText('OpenPeripheral is required to use mod blocks. Please install it first to use this feature.', Drawing.Screen.Width - 4)
			for i, v in ipairs(lines) do
				Drawing.DrawCharactersCenter(1, 0-3+i, nil, nil, lines[i], colours.lightGrey, colours.grey)
			end
		else
			Drawing.DrawBlankArea(1, 4, Drawing.Screen.Width, Drawing.Screen.Height - 1, colours.grey)
			Drawing.DrawCharactersCenter(0, -4, nil, nil, 'ME Controller Not Connected!', colours.white, colours.grey)
			local lines = Helpers.WrapText('The items tab allows you to view and monitor the content of an ME System. Either install the Applied Energistics mod or attach an ME Controller. You can either place the ME Controller next to this computer, use a network cable or use a controller computer.', Drawing.Screen.Width - 4)
			for i, v in ipairs(lines) do
				Drawing.DrawCharactersCenter(1, 0-3+i, nil, nil, lines[i], colours.lightGrey, colours.grey)
			end
		end
	elseif Current.Page == Pages.Resources then
		DrawResources()
	end

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

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

	term.setCursorBlink(false)
	Drawing.DrawBuffer()
	UpdateCursor()
end

function UpdateCursor()
	if Current.Input then
		term.setCursorPos(Current.CursorPos[1], Current.CursorPos[2])
		term.setCursorBlink(true)
		term.setTextColour(Current.CursorColour)
	else
		term.setCursorBlink(false)
	end
end

MainDraw = Draw

function RegisterElement(elem)
	table.insert(InterfaceElements, elem)
end

function UnregisterElement(elem)
	for i, e in ipairs(InterfaceElements) do
		if elem == e then
			InterfaceElements[i] = nil
		end
	end
end

function RegisterClick(elem)
	table.insert(Current.Clicks, elem)
end

function CheckClick(object, x, y)
	local pos = GetAbsolutePosition(object)
	if pos.X <= x and pos.Y <= y and  pos.X + object.Width > x and pos.Y + object.Height > y then
		return true
	end
end

function HandleKey(...)
	local args = {...}
	local event = args[1]
	local keychar = args[2]
	if Current.Window then
		if event == 'key' then
			if keychar == keys.enter then
				if Current.Window.OkButton then
					Current.Window.OkButton:Click(1,1,1)
					Draw()
				end
			end
		end
		if Current.Input then
			if event == 'char' then
				Current.Input:Char(keychar)
			elseif event == 'key' then
				Current.Input:Key(keychar)
			end
			UpdateCursor()
		end
	end

	--TODO
	if keychar == keys.up then
		Scroll('mouse_scroll', -1)
	elseif keychar == keys.down then
		Scroll('mouse_scroll', 1)
	end
end

function DoClick(event, object, side, x, y)
	if object and CheckClick(object, x, y) then
		return object:Click(side, x - object.X + 1, y - object.Y + 1)
	end	
end

function TryClick(event, side, x, y)
	if Current.Menu and DoClick(event, Current.Menu, side, x, y) then
		Draw()
		return
	elseif Current.Window then
		if DoClick(event, Current.Window, side, x, y) then
			Draw()
			return
		else
			Current.Window:Flash()
		end
	else
		if Current.Menu then
			Current.Menu:Close()
			Draw()
			return
		end

		for i, object in ipairs(Current.Clicks) do
			if DoClick(event, object, side, x, y, event == 'mouse_drag') then
				Draw()
				return
			end		
		end

		if Current.Page == Pages.Resources then
			local clicked = false
			EachResource(function(_x, _y, resource)
				if CheckClick({X = _x, Y = _y, Width = resourceWidth, Height = resourceHeight}, x, y) then
					Menu:Initialise(x, y, nil, nil, self,{ 
						{
							Title = 'Rename...',
							Click = function()
								TextDialogueWindow:Initialise("Rename '"..Helpers.TruncateString(resource.Name, 17).."'", function(success, value)
									if success and #value ~= 0 then
										SetResourceName(resource, value)
										Update()
									end
								end):Show()
							end
						},
						{
							Title = 'Hide',
							Click = function()
								ButtonDialogueWindow:Initialise("Warning!", "To unhide this and all other resources click an empty area and select 'Unhide All'.", 'Hide', 'Cancel', function(success)
									if success then
										SetResourceHidden(resource, true)
										Update()
									end
								end):Show()
							end
						},
					}):Show()
					Draw()
					clicked = true
					return true
				end
			end)

			if not clicked then
				Menu:Initialise(x, y, nil, nil, self,{ 
					{
						Title = 'Unhide All',
						Click = function()
							for k, v in pairs(CustomisedResources) do
								v.Hidden = false
							end
							Update()
						end
					},
					{
						Title = 'Reset All',
						Click = function()
							CustomisedResources = {}
							Update()
						end
					},
				}):Show()
				Draw()
			end

		end
	end
end

function GetAbsolutePosition(obj)
	if not obj.Parent then
		return {X = obj.X, Y = obj.Y}
	else
		local pos = GetAbsolutePosition(obj.Parent)
		local x = pos.X + obj.X - 1
		local y = pos.Y + obj.Y - 1
		return {X = x, Y = y}
	end
end

function EventRegister(event, func)
	if not Events[event] then
		Events[event] = {}
	end

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

function EventHandler()
	while true do
		local event = { coroutine.yield() }
		if Events[event[1]] then
			for i, e in ipairs(Events[event[1]]) do
				e(event[1], event[2], event[3], event[4], event[5])
			end
		end
	end
end

Initialise()