local awful = require("awful")
local beautiful = require("beautiful")
local gears = require("gears")
local naughty = require("naughty")
local wibox = require("wibox")

local config = require("config").widgets.system_resources

local inspect = require("simple/debug/inspect")

local bar = {
    {
        {
            max_value = 1,
            value = 0,
            widget = wibox.widget.progressbar,
            color = beautiful.system_resources_widget_bar_color,
            background_color = beautiful.system_resources_widget_bar_bg,
            shape = beautiful.system_resources_widget_bar_shape,
            border_color = beautiful.system_resources_widget_border_color,
            border_width = beautiful.system_resources_widget_border_width,
        },
        forced_width = beautiful.system_resources_widget_bar_width,
        direction = "east",
        layout = wibox.container.rotate,
    },
    margins = beautiful.system_resources_widget_bar_margin,
    widget = wibox.container.margin,
}

local bars = wibox.widget {
    bar,
    bar,
    bar,
    layout = wibox.layout.flex.horizontal
}

function fileExists(name)
    local f = io.open(name, "r")
    if f ~= nil then
        io.close(f)
        return true
    end
    return false
end

function readCommand(command)
    local handle = io.popen(command, "r")
    local r = handle:read("*a")
    handle:close()
    return r
end

function readFile(file)
	local handle = io.open(file, "r")
	local r = handle:read("*a")
	handle:close()
	return r
end


local cpuBar = bars:get_children()[1]:get_children()[1]:get_children()[1]
function getCpuData()
    local raw = readCommand("cat /proc/stat | head -n 1")
    local data = {raw:match((raw:gsub("[^ ]* ", "([^ ]*) ")))}
    return {
        user = data[3],
        nice = data[4],
        system = data[5],
        idle = data[6],
        iowait = data[7],
        irq = data[8],
        softirq = data[9],
        steal = data[10],
    }
end

local lastCpuTime = -1
local lastCpuIdle = -1

gears.timer {
    timeout = config.bars.refresh_interval,
    call_now = true,
    autostart = true,
    callback = function()

        local data = getCpuData()

        local currentCpuTime = data.user + data.nice + data.system + data.idle + data.iowait + data.irq + data.softirq + data.steal
        local currentCpuIdle = data.idle + data.iowait

        if(lastCpuTime >= 0) then
            cpuBar.value = 1 - (currentCpuIdle - lastCpuIdle) / (currentCpuTime - lastCpuTime)
        end

        lastCpuTime = currentCpuTime
        lastCpuIdle = currentCpuIdle
    end
}


local memoryBar = bars:get_children()[2]:get_children()[1]:get_children()[1]
function getTotalMemory()
    return readCommand("cat /proc/meminfo | grep MemTotal | awk '{print $2}'")
end
function getAvailableMemory()
    return readCommand("cat /proc/meminfo | grep MemAvailable | awk '{print $2}'")
end
function getFreeMemory()
    return readCommand("cat /proc/meminfo | grep MemFree | awk '{print $2}'")
end

gears.timer {
    timeout = config.bars.refresh_interval,
    call_now = true,
    autostart = true,
    callback = function()
        memoryBar.value = 1 - getAvailableMemory() / getTotalMemory()
    end
}


local swapBar = bars:get_children()[3]:get_children()[1]:get_children()[1]
function getTotalSwap()
    return readCommand("cat /proc/meminfo | grep SwapTotal | awk '{print $2}'")
end
function getFreeSwap()
    return readCommand("cat /proc/meminfo | grep SwapFree | awk '{print $2}'")
end

gears.timer {
    timeout = config.bars.refresh_interval,
    call_now = true,
    autostart = true,
    callback = function()
        swapBar.value = 1 - getFreeSwap() / getTotalSwap()
    end
}



---
--- Batteries
---
rawBatteries = {}
function refreshRawBatteries()
	rawBatteries = readCommand("ls -A1 /sys/class/power_supply")
end

function getBatteryLevel(battery)
    local charge = 0
    local capacity = 0
    if fileExists(battery .. "/energy_now") then
        charge = charge + readFile(battery .. "/energy_now")
        capacity = capacity + readFile(battery .. "/energy_full")
    elseif fileExists(battery .. "/charge_now") then
        charge = charge + readFile(battery .. "/charge_now")
        capacity = capacity + readFile(battery .. "/charge_full")
    elseif fileExists(battery .. "/capacity_level") then
        return readFile(battery .. "/capacity_level")
    else
        return "Unsupported"
    end
    return string.format("%." .. config.battery.precision .. "f", (charge / capacity) * 100) .. "%"
end

function isCharging(battery)
    return readFile(battery .. "/status") == "Charging\n"
end


local batteryContainer = wibox.widget {
    layout = wibox.layout.fixed.horizontal
}
local timers = {}
function refreshBatteries()
	-- refresh raw batteries and check for change
	local oldBatteries = rawBatteries
	refreshRawBatteries()
	if rawBatteries == oldBatteries then
		return
	end

	-- clear timers
	for i,v in ipairs(timers) do
		v:stop()
	end
	timers = {};

	-- clear layout
	batteryContainer:set_children({})

	for battery in rawBatteries:gmatch("([^\n]+)") do
		local path = "/sys/class/power_supply/" .. battery
		if readFile(path .. "/type"):match("Battery") then
			-- widget
			local widget = wibox.widget {
				{
					markup = "-- %",
					align = "center",
					valign = "center",
					widget = wibox.widget.textbox,
				},
				margins = beautiful.system_resources_widget_battery_margin,
				widget = wibox.container.margin,
			}

			-- tooltip
			local name = readFile(path .. "/model_name")
			local tooltip = awful.tooltip {
				text = name
			}
			tooltip:add_to_object(widget)

			-- update battery level
			local batteryText = widget:get_children()[1]
			local timer = gears.timer {
				timeout = config.battery.refresh_interval,
				call_now = true,
				autostart = true,
				callback = function()
					batteryText.markup = (isCharging(path) and "+" or "") .. getBatteryLevel(path)
				end
			}
			table.insert(timers, timer)

			batteryContainer:add(widget)
		end
	end
end

gears.timer {
    timeout = config.battery.list_refresh_interval,
    call_now = true,
    autostart = true,
    callback = function()
        refreshBatteries()
    end
}

return {
    bars = bars,
    battery = batteryContainer,
}
