-- acf model for displaying logfiles 
local mymodule = {}

posix = require("posix")
modelfunctions = require("modelfunctions")
fs = require("acf.fs")
format = require("acf.format")

-- Function to get detailed information on a specific file.
local function file_info ( path )
	-- Check if file is in use
	local st = fs.stat(path)
	while st.type == "link" do
		st = fs.stat(posix.readlink(st.path))
	end
	local size = st.size or "0"
	local lastmod = st.mtime or "---"
	local fileinuseresult = modelfunctions.run_executable({"fuser", path}) or "unknown"
	fileinuseresult = (fileinuseresult ~= "")
	return lastmod,size,fileinuseresult
end

-- Function to recursively insert all filenames in a dir into an array
local function recursedir(path, filearray)
	filearray = filearray or {}
	local k,v
	for k,v in pairs(posix.dir(path) or {}) do
		-- Ignore files that begins with a '.'
		if not string.match(v, "^%.") then
			local f = path .. "/" .. v
			-- If subfolder exists, list files in this subfolder
			local st = fs.stat(f)
			while st and st.type == "link" do
				st = fs.stat(posix.readlink(st.path))
			end
			if st and st.type == "directory" then
				recursedir(f, filearray)
			elseif st then
				table.insert(filearray, f)
			end
		end
	end
	return filearray
end

-- Function to list available files for view/delete
local function list_files ( ... )
	local listed_files = {}
	local open_files = {}
	local files = {}
	local k,v
	-- Generate a single table with all the files
	for k,v in pairs{...} do
		recursedir(v, files)
	end
	-- Loop through each file and present its info
	for k,v in pairs(files) do
		-- Get info on this specific file and put it in a table
		local lastmod,size,fileinuseresult = file_info(v)
		local filename = cfe({ value=v, label="File name" })
		local filesize = cfe({ value=size, label="File size" })
		local mtime = cfe({ value=lastmod, label="File date" })
		local inuse = cfe({ type="boolean", value=fileinuseresult, label="File in use" })
		table.insert ( listed_files , cfe({ type="group", value={filename=filename, filesize=filesize, mtime=mtime,
			inuse=inuse, label="File details"} }) )
	end
	table.sort(listed_files, function (a,b) return (a.value.filename.value < b.value.filename.value) end )
	return cfe({ type="list", value=listed_files, label="Log Files" })
end

local do_grep = function(filecontent, grep)
	if grep and grep ~= "" then
		local lines = {}
		for line in string.gmatch(filecontent.value, "[^\n]*\n?") do
			if string.match(line, grep) then
				lines[#lines+1] = line
			end
		end
		filecontent.value = table.concat(lines) or ""
	end
end

mymodule.get_filedetails = function(self, clientdata)
	local retval = cfe({ type="group", value={}, label="Logfile details" })
	retval.value.filename = cfe({ label="File name", key=true })
	retval.value.grep = cfe({ label="Grep", key=true })
	self.handle_clientdata(retval, clientdata)
	
	local success = false
	local available_files = mymodule.get()
	for i,file in ipairs(available_files.value) do
		if file.value.filename.value == retval.value.filename.value then
			success = true
			break
		end
	end
	local file = retval.value.filename.value
	if success then
		local st = fs.stat(file)
		while st.type == "link" do
			st = fs.stat(posix.readlink(st.path))
		end
		file = st.path
	end
	local filedetails = modelfunctions.getfiledetails(file, function(filename) return success end)
	for n,v in pairs(filedetails.value) do
		if n ~= "filename" then
			retval.value[n] = v
		end
	end
	if filedetails.errtxt then
		retval.errtxt = filedetails.errtxt
	else
		do_grep(retval.value.filecontent, retval.value.grep.value)
	end
	return retval
end

mymodule.tail = function(self, clientdata)
	local retval = cfe({ type="group", value={}, label="Tail Logfile" })
	retval.value.filename = cfe({ label="File name", errtxt="File not found", key=true })
	retval.value.offset = cfe({ value="0", label="File offset", key=true })
	retval.value.grep = cfe({ label="Grep", key=true })
	self.handle_clientdata(retval, clientdata)

	retval.value.filesize = cfe({ value="0", label="File size" })
	retval.value.filecontent = cfe({ type="longtext", label="File content" })

	local available_files = mymodule.get()
	for i,file in ipairs(available_files.value) do
		if ( file.value.filename.value == retval.value.filename.value ) then
			retval.value.filename.errtxt = nil
			local f = io.open(retval.value.filename.value)
			if tonumber(retval.value.offset.value) then
				local offset = tonumber(retval.value.offset.value)
				if offset < 0 then
					f:seek("end", offset)
				else
					f:seek("set", offset)
				end
				retval.value.filecontent.value = f:read("*all")
				retval.value.filesize.value = f:seek()
			else
				retval.value.filesize.value = f:seek("end")
				retval.value.offset.value = retval.value.filesize.value
			end
			f:close()
			do_grep(retval.value.filecontent, retval.value.grep.value)
			break
		end
	end

	return retval
end

mymodule.get = function ()
	-- These folders (and their subfolers) are going to be listed
	return list_files("/var/log")
end

mymodule.get_delete = function()
	local filename = cfe({ type="select", label="File name", option={} })
	-- Get a list of files that could be deleted
	local available_files = mymodule.get()
	for i,file in ipairs(available_files.value) do
		filename.option[#filename.option+1] = file.value.filename.value
	end
	
	return cfe({ type="group", value={filename=filename}, label="Delete logfile" })
end

-- Function to check if a file is deletable, and if it is, then delete it.
mymodule.delete = function (self, filetodelete)
	local success = modelfunctions.validateselect(filetodelete.value.filename)

	if success then
		local lastmod,size,fileinuseresult = file_info(filetodelete.value.filename.value)
		-- Check if file is deletable (or in use)
		if fileinuseresult then
			success = false
			filetodelete.value.filename.errtxt = "File in use"
		else
			local st = fs.stat(filetodelete.value.filename.value)
			while st.type == "link" do
				st = fs.stat(posix.readlink(st.path))
			end
			local status, err = os.remove( st.path )
			if err then
				success = false
				filetodelete.value.filename.errtxt = err
			end
		end
	end

	if not success then
		filetodelete.errtxt = "Failed to delete file"
	end

	return filetodelete
end

return mymodule
