#!/usr/bin/env lua
--[[
    (C) Copyright 2008 Matthew Wild

	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; either version 2
	of the License, or (at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
--]]

require "lpkg.pkgdb"
require "lpkg.pkgutil"
require "lpkg.lpkgman"

lpkg = { config = {} };

-- Actions --

function lpkg:list()
	local db = lpkgman.db;
	print("Status","Name","Version", "Installed-Version");
	for _, pkg in db:entries() do
		-- Installed version info
		print((pkg.installed and "i") or "u", pkg.name or "unknown", (pkg.version and table.concat(pkg.version, ".")) or "unknown", (pkg.installed_version and table.concat(pkg.installed_version, ".")) or "unknown");
	end
end

function lpkg:update()
	require "lpkg.pkgfeeds"
	pkgfeeds:refresh(arg[1]);
	showMessage("info", "Feed(s) updated");
end

lpkg.refresh = lpkg.update;

function lpkg:upgrade(args)
	local db = lpkgman.db;
	local num_upgraded = 0;
	for _, pkg in db:entries() do
		if pkg.installed and pkg.installed_version and pkg.installed_version < pkg.version then
			showMessage("info", "Removing old version of "..pkg.name.."...");
			lpkgman:uninstall(pkg.name, true)
			showMessage("info", "Installing new version of "..pkg.name.."...");
			lpkgman:install(pkg.name);
			num_upgraded = num_upgraded + 1;
		end
	end
	showMessage("info", "Upgrade complete, "..num_upgraded.." package(s) upgraded");
end

function lpkg:install(arg)
	local db = lpkgman.db;
	local name = arg[1];
	
	if not name then
		error("No package specified for installation", 0);
	end
	local pkg = db:getpackage(name);
	if not pkg then
		error("No such package '"..name.."'", 0);
	end
	if pkg.installed and not lpkg.config.reinstall then
		error(string.format("Package '%s' is already installed", name), 2);
	end
	
	local res, askeddeps = nil, false;
	while type(res) ~= "boolean" do
		res = lpkgman:install(name, function (option) print("Option: ", option.name, "("..tostring(option.default)..")"); print(option.hint or ""); io.write("Value: "); return io.read("*l") end, ((askeddeps or lpkg.config.assumeyes) and "yes") or "ask");
		if type(res) == "table" then
			print("The installation of "..name.." requires installing "..#res.." other packages first: ");
			for _, dep in ipairs(res) do
				io.write(dep, ", ");
			end
			io.write("\nInstall these and continue? [Y/n] ");
			local choice = io.read("*l");
			if #choice < 1 or choice:match("[yY]$") then
				askeddeps = true;
			else
				error("Installation cancelled", 0);
				break;
			end
		end
	end
	
	if res == true then
		showMessage("info", "Package '"..name.."' installed successfully");
	else
		showMessage("warn", "Operation failed to complete successfully");
	end
end

function lpkg:remove(args)
	local db = lpkgman.db;
	local name = args[1] or error("Please supply the name of the package you would like to remove", 0);
	local pkg = db:getpackage(args[1]);
	if not pkg then error("No package '"..name.."' found", 0); end
	if not pkg.installed then error(name.." is not currently installed", 0); end
	lpkgman:uninstall(name);
	showMessage("info", "Operation completed successfully");
end

function lpkg:show(args)
	local db = lpkgman.db;
	local name = args[1] or error("Please supply the name of a package to show", 0);
	local pkg = db:getpackage(name);
	if not pkg then error("No package '"..name.."' found", 0); end
	print("Name: ", name);
	print("Installed: ", pkg.installed and "Yes" or "No");
	if pkg.installed then
		print("Installed version: ", table.concat(pkg.installed_version or { "Unknown" }, "."));
	end
	print("Latest version: ", table.concat(pkg.version or { "Unknown" }, "."));
end

-- Feed management

function lpkg:addfeed(args)
	local url = args[1] or error("Please supply a feed URL", 0);
	require "lpkg.pkgfeeds"
	pkgfeeds:add(url);
	showMessage("info", "Feed added successfully");
end

function lpkg:showfeeds()
	require "lpkg.pkgfeeds"
	print("ID", "URL");
	for id, url in ipairs(pkgfeeds:list()) do
		print(id, url);
	end
end

lpkg.listfeeds = lpkg.showfeeds;

function lpkg:removefeed(args)
	require "lpkg.pkgfeeds"
	if not pkgfeeds:remove(tonumber(args[1]) or args[1] or error("Please supply a feed URL or id to remove")) then
		error("Error while removing feed. Does that feed exist?", 0);
	end
	showMessage("info", "Feed removed successfully");
end

-- Service package control --

function lpkg:control(args)
	local name = args[1] or error("Please supply the name of the package to control", 0);
	local command = args[2] or error("Please supply a command to send to "..name, 0);
	require "lpkg.pkgutil"
	if pkgutil.control(name, command) then
		showMessage("info", "Command sent successfully");
	else
		showMessage("warn", "Command failed to send successfully");
	end
end

--------------------

function lpkg:usage()
	print "Lua Package Manager v1.1"
	print "(C) 2007 Matthew Wild"
	print ""
	print "Available actions:"
	print ""
	print "-- Package management"
	print "\tinstall\t-\tInstall a package"
	print "\tremove\t-\tRemove an installed package"
	print "\tshow\t-\tShow info about any known package"
	print "\tlist\t-\tLists available packages"
	print ""
	print "-- Feed management"
	print "\trefresh\t-\tUpdate information about packages from their feeds"
	print "\taddfeed\t-\tAdd a feed"
	print "\tshowfeeds\t-\tShows all feeds lpkg is using"
	print "\tremovefeed\t-\tRemove a feed"
	print ""
	print "-- Service control"
	print "\tcontrol <package> <command>\tSends <command> to <package>, common commands are 'start' and 'stop'"
	print ""
end

-- Initialisation --

if not pcall(require,"config") then
	error("Unable to load 'config' module", 0);
end

--[[
if not pcall(require, "lfs") then
	lfs = {};
	
	function lfs.exists(path)
		return os.execute(string.format("test -e %q", path)) == 0;
	end
end ]]

if not (arg and arg[1]) then
	lpkg:usage();
end

-- Handle message --

local errorprefix = { info = "(II)", warn = "(WW)", error = "(EE)", verbose = "(++)" }

function showMessage(type, message)
	print((errorprefix[type] or "(??)").." "..message);
end

-- Command line parsing

parsecl = {}

parsecl.flags = {
				s = function () lpkg.config.simulate = true; end;
				y = function () lpkg.config.assumeyes = true; end;
				f = function () lpkg.config.reinstall = true; end;
			}

function parsearg(currarg)
	if currarg:match("^%-") then
		for flag in currarg:gmatch("%a") do
			if parsecl.flags[flag] then
				parsecl.flags[flag]();
			end
		end
	else
		if not parsecl.action then
			parsecl.action = currarg;
		else
			return false;
		end
	end
end

local maxarg = 0;

for _, currarg in ipairs(arg) do
	if parsearg(currarg) == false then
		break;
	else
		maxarg = maxarg + 1;
	end
end

for i = 0, maxarg-1 do
	table.remove(arg, 1);
end


if lpkg[parsecl.action] then
	local success, msg = pcall(lpkg[parsecl.action], lpkg, arg);
	if not success then
		showMessage("error", "Fatal error: "..msg);
		return 1;
	else
		return 0;
	end
else
	showMessage("warn", "Unknown action '"..tostring(parsecl.action).."'");
end

