#!/usr/bin/lua
-- --- SDE-COPYRIGHT-NOTE-BEGIN ---
-- This copyright note is auto-generated by ./scripts/Create-CopyPatch.
--
-- Filename: package/.../sam/sam.lua
-- Copyright (C) 2006 The T2 SDE Project
--
-- More information can be found in the files COPYING and README.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; version 2 of the License. A copy of the
-- GNU General Public License can be found in the file COPYING.
-- --- SDE-COPYRIGHT-NOTE-END ---

-- identification
local _NAME        = "SAM"
local _VERSION     = "0.0-devel"
local _COPYRIGHT   = "Copyright (C) 2006 The T2 SDE Project"
local _DESCRIPTION = "System Administration Manager for systems based on T2"

-- SAM namespace
sam = sam or {
	command = {}
}

require "sam.log"

-- default options
sam.opt = sam.opt or {
	loglevel = sam.log.DEBUG, -- sam.log.WARN
}

--[[ DESCRIPTION ] ----------------------------------------------------------

Provided functions:

* sam.command["command-name"](args...)
* sam.command["command-name"].main(args...)

    Execute a command (extended by modulues) with given arguments.
	The only built-in command currently is "help".

--]] ------------------------------------------------------------------------

-- fprintf alike helper function
local function fprintf(stream, ...)
	stream:write( string.format(unpack(arg)) )
end

-- MODULES ------------------------------------------------------------------

-- load_module(name)
--   Load the previously detected module.
local function load_module(name)
	sam.info(_NAME, "Loading module %s (from %s)\n", name, sam.command[name]._module._FILE)

	-- sanity check for module info
	if not sam.command[name] or not sam.command[name]._module then
		sam.error(_NAME, "No such command module '%s', giving up.\n", name)
		return
	end

	-- load and execute the module
	local module, emsg = loadfile(sam.command[name]._module._FILE)

	if not module then
		print(emsg)
		os.exit(-1)
	end

	module = module()

	-- module sanity check
	if not module.main or not module._NAME then
		sam.error(_NAME, "Command module '%s' is probably not a SAM module.\n", name)
		return
	end

	-- copy module data
	sam.command[name]._NAME         = module._NAME
	sam.command[name]._DESCRIPTION  = module._DESCRIPTION
	sam.command[name]._USAGE        = module._USAGE
	sam.command[name]._module.main  = module.main

	sam.command[name]._load  = nil

	-- set real methods
	sam.command[name].main  = function(self,...) return self._module.main(unpack(arg)) end

	-- set correct metatable
	setmetatable(sam.command[name], {
		__call = function(self, ...) return self._module.main(unpack(arg)) end,
	})
end

-- detect_modules()
--   Detect all SAM modules
local function detect_modules()
	local lfs = require("lfs")
	local moddir = os.getenv("SAM_MODULES") or "/usr/share/sam"

	for file in lfs.dir( moddir ) do
		local name
		local path

		_,_,name = string.find(file, "^sam_([%a][_%w%a]*).lua")
		path = moddir .. "/" .. file

		if name and lfs.attributes(path).mode == "file" and "sam_" .. name .. ".lua" == file then
			sam.dbg(_NAME, "Found '%s' (%s)\n", name, path)

			-- preset the module structure of the detected module
			-- for auto-loading
			sam.command[name] = {
				_module = {
					_NAME = name,
					_FILE = path,
				},

				_load = function(self,...) load_module(self._module._NAME) end,

				_NAME = name,
				_DESCRIPTION = "",
				_USAGE = "",

				main  = function(self,...)
						load_module(self._module._NAME)
						return self:main(unpack(arg))
					end,
			}

			-- add a metatable so the commands can be used, however,
			-- it is anly a intermediate metatable, as the module is not
			-- loaded yet. The module gets loaded (dynamic linker alike)
			-- once it is called

			setmetatable(sam.command[name], {
				__call = function(self, ...)
						load_module(self._module._NAME)
						return self:main(unpack(arg))
					end,
			})

		end
	end
end

-- COMMANDS -----------------------------------------------------------------
local function usage(cmd)
	fprintf(io.stdout, "%s v%s %s\n\n", _NAME, _VERSION, _COPYRIGHT)
	if cmd then
		if sam.command[cmd]._load then sam.command[cmd]:_load() end
		fprintf(io.stdout, "Usage: sam %s\n", sam.command[cmd]._USAGE)
	else
		fprintf(io.stdout, "Usage: sam <command> [command options]\n\n%s\n",
			[[Commands:
                help    Show command overview (this)
      help <command>    Show command specific usage information]])

		for k,_ in pairs(sam.command) do
			if sam.command[k]._load then sam.command[k]:_load() end
			fprintf(io.stdout, "    %16s    %s\n", k, sam.command[k]._DESCRIPTION)
		end
	end
end

-- --------------------------------------------------------------------------
-- INITIALIZE SAM
-- --------------------------------------------------------------------------
detect_modules()

-- --------------------------------------------------------------------------
-- MAIN
-- --------------------------------------------------------------------------

if arg[1] then
	-- help
	if arg[1] == "help" then
		usage(arg[2])
	elseif arg[2] == "help" then
		usage(arg[1])
	else
		-- split command and command arguments
		local cmd = arg[1]
		local args = arg ; table.remove(args, 1)

		sam.command[cmd](unpack(args or {}))
	end
end