-- 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 (path, grep)
	local success = false
	local available_files = mymodule.get()
	for i,file in ipairs(available_files.value) do
		if file.value.filename.value == path then
			success = true
			break
		end
	end
	local file = path
	if success then
		local st = fs.stat(path)
		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)
	filedetails.value.filename.value = path
	if not filedetails.errtxt then
		do_grep(filedetails.value.filecontent, grep)
	end
	filedetails.value.grep = cfe({ value=grep or "", label="Grep" })
	return filedetails
end

mymodule.tail = function(path, offset, grep)
	local filename = cfe({ value=path, label="File name", errtxt="File not found" })
	local filesize = cfe({ value="0", label="File size" })
	local filecontent = cfe({ type="longtext", label="File content" })
	local fileoffset = cfe({ value="0", label="File offset" })
	local filegrep = cfe({ value=grep or "", label="Grep" })

	local available_files = mymodule.get()
	for i,file in ipairs(available_files.value) do
		if ( file.value.filename.value == path ) then
			filename.errtxt = nil
			local f = io.open(path)
			if tonumber(offset) then
				fileoffset.value = offset
				offset = tonumber(offset)
				if offset < 0 then
					f:seek("end", offset)
				else
					f:seek("set", offset)
				end
				filecontent.value = f:read("*all")
				filesize.value = f:seek()
			else
				filesize.value = f:seek("end")
				fileoffset.value = filesize.value
			end
			f:close()
			do_grep(filecontent, grep)
			break
		end
	end
	
	return cfe({ type="group", value={filename=filename, filecontent=filecontent, filesize=filesize, fileoffset=fileoffset, grep=filegrep}, label="Tail File" })
end

mymodule.get = function ()
	-- These folders (and their subfolers) are going to be listed
	return list_files( "/var/log", "/tmp/squid/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
