local mymodule = {}

-- Load libraries
modelfunctions = require("modelfunctions")
fs = require("acf.fs")
format = require("acf.format")

-- Set variables
local packagename = "iproute2-qos"
local initscript = "qos"
local initfile = "/etc/init.d/"..initscript
local conffile = "/etc/conf.d/qos"
local conffilesample = "/etc/conf.d/qos.eth0.sample"

local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin "

local ints

-- ################################################################################
-- LOCAL FUNCTIONS

local function list_interfaces(self)
	if not ints then
		local interfacescontroller = self:new("alpine-baselayout/interfaces")
		local interfaces = interfacescontroller.model.get_interfaces()
		interfacescontroller:destroy()
		ints = interfaces.value
	end
	return ints
end

local function validate_filename(filename)
	return string.find(filename, "^"..conffile.."%.%w+$")
end

-- modify /etc/network/interfaces with the up/down lines for ifb device
local function modify_interfaces(self, DEV, IFB_DEV)
	local up = "ip link set ifb%d+ up"
	local down = "ip link set ifb%d+ down"
	local interfacescontroller = self:new("alpine-baselayout/interfaces")
	local interface = interfacescontroller.model.get_iface_by_name(interfacescontroller, {name=DEV})
	interface.value.up.value = string.gsub(interface.value.up.value, up, "")
	interface.value.down.value = string.gsub(interface.value.down.value, down, "")
	if IFB_DEV ~= "" then
		interface.value.up.value = string.gsub(up, "ifb%%d%+", IFB_DEV) .. "\n" .. interface.value.up.value
		interface.value.down.value = string.gsub(down, "ifb%%d%+", IFB_DEV) .. "\n" .. interface.value.down.value
	end
	interfacescontroller.model.update_iface(interfacescontroller, interface)
	interfacescontroller:destroy()
end

-- ################################################################################
-- PUBLIC FUNCTIONS

function mymodule.get_startstop(self, clientdata)
        return modelfunctions.get_startstop(clientdata.init)
end
	
function mymodule.startstop_service(self, startstop, action)
        return modelfunctions.startstop_service(startstop, action)
end

function mymodule.getstatus()
	return modelfunctions.getstatus(nil, packagename, "QOS Status")
end

function mymodule.listinterfacedetails(self)
	local result = {}
	local interface
	for i,int in ipairs(list_interfaces(self)) do
		if interface ~= int then
			local temp = {interface=int, init=initscript.."."..int, filename=conffile.."."..int, enabled=true}
			local status = modelfunctions.getenabled(temp.init)
			temp.status = status.value
			if status.errtxt then
				temp.enabled = false
			end
			result[#result+1] = temp
		end
	end
	table.sort(result, function(a,b) return a.interface < b.interface end)
	return cfe({ type="structure", value=result, label="QOS Interface List" })
end

function mymodule.get_enable(self, clientdata)
	local retval = {}
	retval.interface = cfe({ value=clientdata.interface or "", label="Interface" })
	return cfe({ type="group", value=retval, label="Enable QOS on Interface" })
end

function mymodule.enable(self, int_enable)
	local interface = int_enable.value.interface.value
	local init = initfile.."."..interface
	int_enable.errtxt = "Failed to enable QOS"
	if not posix.stat(initfile) then
		int_enable.value.interface.errtxt = initfile.." does not exist"
	elseif posix.stat(init) then
		int_enable.value.interface.errtxt = init.." already exists"
	else
		posix.link(initfile, init, true)
		local conf = string.gsub(init, "init%.d", "conf.d")
		if not posix.stat(conf) then
			local filecontent = fs.read_file(conffilesample) or ""
			filecontent = format.update_ini_file(filecontent, "", "DEV", interface)
			if string.find(interface, "^ifb") then
				filecontent = format.update_ini_file(filecontent, "", "IFB_DEV", "")
			end
			fs.write_file(conf, filecontent)
		end
		local IFB_DEV = format.parse_ini_file(fs.read_file(conf) or "", "", "IFB_DEV") or ""
		modify_interfaces(self, interface, IFB_DEV)
		int_enable.errtxt = nil
	end
	return int_enable
end

function mymodule.get_filedetails(self, clientdata)
	return modelfunctions.getfiledetails(clientdata.filename, validate_filename)
end

function mymodule.update_filedetails(self, filedetails)
	return modelfunctions.setfiledetails(self, filedetails, validate_filename)
end

function mymodule.get_config(self, clientdata)
	local interface = clientdata.DEV
	local config = {}
	local ifbs = {""}
	if not string.find(interface, "^ifb") then
		for i,int in ipairs(list_interfaces(self)) do
			if string.find(int, "^ifb") then
				ifbs[#ifbs+1] = int
			end
		end
	end
	local configfile = conffile.."."..interface
	local configopts = format.parse_ini_file(fs.read_file(configfile) or "", "") or {}
	config.DEV = cfe({ value=configopts.DEV or interface, label="Network device", readonly=true, seq=1 })
	config.DEV_RATE = cfe({ value=configopts.DEV_RATE or "", label="Device Rate", descr="Actual speed of physical device (units: mbps, mbit, kbit, kbps, bps)", seq=2 })
	config.INGRESS_ALG = cfe({ type="select", value=configopts.INGRESS_ALG or "none", label="Ingress Algorithm", option={"police", "cpolice", "ifb", "none"}, seq=3 })
	config.IFB_DEV = cfe({ type="select", value=configopts.IFB_DEV or "", label="Inbound IFB device", option=ifbs, seq=4 })
	config.INGRESS_RATE = cfe({ value=configopts.INGRESS_RATE or "", label="Ingress rate", descr="(units: mbps, mbit, kbit, kbps, bps)", seq=5 })
	config.EGRESS_ALG = cfe({ type="select", value=configopts.EGRESS_ALG or "none", label="Egress Algorithm", option={"htb", "hfsc", "prio", "none"}, seq=6 })
	config.EGRESS_RATE = cfe({ value=configopts.EGRESS_RATE or "", label="Egress rate", descr="(units: mbps, mbit, kbit, kbps, bps)", seq=7 })

	config.DEV.errtxt = "Invalid device"
	for i,int in ipairs(list_interfaces(self)) do
		if int == interface then
			config.DEV.errtxt = nil
			break
		end
	end

	return cfe({ type="group", value=config, label=interface.." QOS Config" })
end

function mymodule.update_config(self, config)
	local success = false
	config.value.DEV.errtxt = "Invalid device"
	for i,int in ipairs(list_interfaces(self)) do
		if int == config.value.DEV.value then
			config.value.DEV.errtxt = nil
			success = true
			break
		end
	end
	success = modelfunctions.validateselect(config.value.INGRESS_ALG) and success
	success = modelfunctions.validateselect(config.value.IFB_DEV) and success
	success = modelfunctions.validateselect(config.value.EGRESS_ALG) and success
	local rates = { mbps=1, mbit=1, kbit=1, kbps=1, bps=1, [""]=1 } -- last entry allows for no units
	for i,rate in ipairs({"DEV_RATE", "INGRESS_RATE", "EGRESS_RATE"}) do
		if config.value[rate] and ( not string.find(config.value[rate].value, "^%s*%d+%s*%l*%s*$") or not rates[string.match(config.value[rate].value, "(%l*)")] ) then
			config.value[rate].errtxt = "Invalid rate"
			success = false
		end
	end
	-- ifb device should have nothing defined for ingress
	if string.find(config.value.DEV.value, "^ifb") then
		if config.value.IFB_DEV.value ~= "" then
			config.value.IFB_DEV.errtxt = "Must be undefined for ifb"
			success = false
		end
		if config.value.INGRESS_ALG.value ~= "none" then
			config.value.INGRESS_ALG.errtxt = "Must be 'none' for ifb"
			success = false
		end
	elseif config.value.INGRESS_ALG.value == "ifb" then
		if config.value.IFB_DEV.value == "" then
			config.value.IFB_DEV.errtxt = "Must define ifb device when using ifb ingress algorithm"
			success = false
		end
	elseif config.value.IFB_DEV.value ~= "" then
		config.value.IFB_DEV.errtxt = "Must be undefined if not using ifb ingress algorithm"
		success = false
	end
	if string.find(config.value.DEV.value, "^ifb") or config.value.INGRESS_ALG.value == "ifb" then
		if tonumber(string.match(config.value.INGRESS_RATE.value, "%d+") or "") ~= 0 then
			config.value.INGRESS_RATE.errtxt = "Must be 0 for ifb"
			success = false
		end
	end

	if success then
		local configfile = conffile.."."..config.value.DEV.value
		local contents = fs.read_file(configfile)
		contents = format.update_ini_file(contents, "", "DEV", config.value.DEV.value)
		contents = format.update_ini_file(contents, "", "DEV_RATE", config.value.DEV_RATE.value)
		contents = format.update_ini_file(contents, "", "INGRESS_ALG", config.value.INGRESS_ALG.value)
		contents = format.update_ini_file(contents, "", "IFB_DEV", config.value.IFB_DEV.value)
		contents = format.update_ini_file(contents, "", "INGRESS_RATE", config.value.INGRESS_RATE.value)
		contents = format.update_ini_file(contents, "", "EGRESS_ALG", config.value.EGRESS_ALG.value)
		contents = format.update_ini_file(contents, "", "EGRESS_RATE", config.value.EGRESS_RATE.value)
		fs.write_file(configfile, contents)

		-- need to set lines in /etc/network/interfaces
		modify_interfaces(self, config.value.DEV.value, config.value.IFB_DEV.value)
	else
		config.errtxt = "Failed to set config"
	end

	return config
end

return mymodule
