/**
 * File:
 *   ui.ycp
 *
 * Module:
 *   Configuration of nfs
 *
 * Summary:
 *   Network NFS client dialogs
 *
 * Authors:
 *   Jan Holesovsky <kendy@suse.cz>
 *   Dan Vesely <dan@suse.cz>
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: ui.ycp 22617 2005-03-16 14:49:25Z mvidner $
 *
 * Network NFS client dialogs
 *
 */

{
    textdomain "nfs";

    import "CWMFirewallInterfaces";
    import "Label";
    import "Nfs";
    import "Popup";
    import "Wizard";
    include "nfs/routines.ycp";

    /**
     * Read settings dialog
     * @return `abort if aborted and `next otherwise
     */
    symbol ReadDialog() {
	boolean ret = nil;
	// move portmap check somewhere else?
	// this may be too early
	ret = IsPortmapInstalled () && Nfs::Read ();
	return ret ? `next : `abort;
    }

    /**
     * Write settings dialog
     * @return `abort if aborted and `next otherwise
     */
    symbol WriteDialog() {
	boolean ret = Nfs::Write ();
	return ret ? `next : `abort;
    }

    /**
     * Caches names of nfs servers for GetFstabEntry
     */

    list<string> hosts = nil;

    /**
     * Let the user choose one of a list of items
     * @param title	selectionbox title
     * @param items	a list of items
     * @return		one item or nil
     */
    define string ChooseItem (string title, list<string> items) ``{
	string item = nil;

	UI::OpenDialog (
	    `VBox (
		`HSpacing (40),
		`HBox (
		    `SelectionBox (`id (`items), title, items),
		    `VSpacing(10)
		    ),
		`HBox (
		    `PushButton (`id (`ok), `opt (`default, `key_F10),
				 Label::OKButton ()),
		    `PushButton (`id (`cancel), `opt (`key_F9),
				 Label::CancelButton())
		    )
		));
	UI::SetFocus (`id (`items));
	any ret = nil;
	do
	{
	    ret = UI::UserInput();
	}
	while (ret != `cancel && ret != `ok);

	if (ret == `ok)
	{
	    item = (string) UI::QueryWidget (`id (`items), `CurrentItem);
	}
	UI::CloseDialog();

	return item;
    }

    /**
     * Give me one name from the list of hosts
     * @param hosts	a list of hostnames
     * @return		a hostname
     */
    define string ChooseHostName(list<string> hosts) ``{
	Wizard::SetScreenShotName ("nfs-client-1aa-hosts");
	// selection box label
	// changed from "Remote hosts" because now it shows
	// NFS servers only
	string ret = ChooseItem (_("&NFS Servers"), hosts);
	Wizard::RestoreScreenShotName ();
	return ret;
    }

    /**
     * Give me one name from the list of exports
     * @param hosts	a list of export
     * @return		an export
     */
    define string ChooseExport (list<string> exports) ``{
	Wizard::SetScreenShotName ("nfs-client-1ab-exports");
	// selection box label
	string ret = ChooseItem (_("&Exported Directories"), exports);
	Wizard::RestoreScreenShotName ();
	return ret;
    }

    /**
     * Nicely put a `TextEntry and its helper `PushButton together
     * @param text   textentry widget
     * @param button pushbutton widget
     * @return a HBox
     */
    define term TextAndButton (term text, term button) ``{
	return `HBox (
	    `Bottom (text),
	    `HSpacing(0.5),
	    `Bottom (button)
	    );
    }
    /**
     * Ask user for an entry.
     * @param fstab_ent	$["spec": "file": "mntopts":] or nil
     * @param existing	list of fstab entries for duplicate mount-point checking
     * @return		a nfs_entry or nil
     */
    define map<string,any> GetFstabEntry(map<string,any> fstab_ent, list<map> existing) ``{
	Wizard::SetScreenShotName ("nfs-client-1a-edit");

	string server = "";
	string pth = "";
	string mount = "";
	string options = "defaults";

	if (fstab_ent != nil)
	{
	    term couple = SpecToServPath(fstab_ent["spec"]:"");
	    server = couple[0]:"";
	    pth = couple[1]:"";
	    mount = fstab_ent["file"]:"";
	    options = fstab_ent["mntops"]:"";
	}

	UI::OpenDialog(
	    `opt(`decorated),
	    `HBox(
		`HSpacing(1),
		`VBox(
		    `VSpacing(0.2),
		    TextAndButton (
			`TextEntry(
			    `id(`serverent),
			    // text entry label
			    _("Host name of the &NFS server:"), server),
			// pushbutton label
			// choose a host from a list
			// appears in help text too
			`PushButton (`id (`choose), _("Choo&se"))
			),
		    `HBox(
			TextAndButton (
			    `TextEntry(`id(`pathent),
				       // textentry label
				       _("&Remote filesystem:"), pth),
			    // pushbutton label,
			    // select from a list of remote filesystems
			    // make it short
			    // appears in help text too
			    `PushButton (`id (`pathent_list), _("&Select"))
			    ),
			`HSpacing(0.5),
			TextAndButton (
			    `TextEntry(`id(`mountent),
				       // textentry label
				       _("&Mountpoint (local):"), mount),
			    // button label
			    // browse directories to select a mount point
			    // appears in help text too
			    `PushButton(`id(`browse), _("&Browse"))
			    )
			),
		    // textentry label
		    `TextEntry(`id(`optionsent), _("O&ptions:"), options),
		    `VSpacing(0.2),
		    `HBox(
			`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
			`PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton()),
			`PushButton(`id(`help), `opt (`key_F1), Label::HelpButton())),
		    `VSpacing(0.2)),
		`HSpacing(1)
		));
	UI::SetFocus (`id (`serverent));

	any ret = nil;
	do
	{
	    ret = UI::UserInput();

	    if (ret == `choose)
	    {
		if (hosts == nil)
		{
		    // label message
		    UI::OpenDialog(`Label(_("Scanning for hosts on this LAN...")));
		    // #71064
		    // this works also if ICMP broadcasts are ignored
		    string cmd = "/usr/sbin/rpcinfo -b mountd 1 | cut -d ' ' -f 2 | sort -u";
		    map out = (map) SCR::Execute (.target.bash_output, cmd);
		    hosts = filter (string s, splitstring (out["stdout"]:"", "\n"), ``( s != ""));
		    UI::CloseDialog();
		}
		string host = ChooseHostName(hosts);
		if (host != nil)
		{
		    UI::ChangeWidget(`id(`serverent), `Value, host);
		}
	    }
	    else if (ret == `pathent_list)
	    {
		string server = (string) UI::QueryWidget(`id(`serverent), `Value);
		UI::OpenDialog (
		    `Label (
			// Popup dialog, %1 is a host name
			sformat (_("Getting directory list for \"%1\"..."),
				 server)
			));
		list<string> dirs = (list<string>) SCR::Read (.net.showexports, server);
		if (dirs == nil)
		{
		    dirs = ["internal error"];
		}
		UI::CloseDialog ();
		string dir = ChooseExport (dirs);
		if (dir != nil)
		{
		    UI::ChangeWidget (`id (`pathent), `Value, dir);
		}
	    }
	    else if (ret == `browse)
	    {
		string dir = (string) UI::QueryWidget(`id (`mountent), `Value);
		if (dir == nil || size (dir) == 0)
		{
		    dir = "/";
		}

		dir = UI::AskForExistingDirectory(dir, _("Select mount point"));

		if (dir != nil && size(dir) > 0)
		{
		    UI::ChangeWidget (`id (`mountent), `Value, dir);
		}
	    }
	    else if (ret == `ok)
	    {
		server = (string) UI::QueryWidget(`id(`serverent), `Value);
		pth = StripExtraSlash ((string) UI::QueryWidget(`id(`pathent), `Value));
		mount = StripExtraSlash ((string) UI::QueryWidget(`id(`mountent), `Value));
		options = deletechars ((string) UI::QueryWidget(`id(`optionsent), `Value), " ");

		ret = nil;
		string options_error = check_options (options);
		if (!CheckHostName (server))
		{
		    UI::SetFocus (`id (`serverent));
		}
		else if (! CheckPath (pth))
		{
		    UI::SetFocus (`id (`pathent));
		}
		else if (! CheckPath (mount) || IsMpInFstab (existing, mount))
		{
		    UI::SetFocus (`id (`mountent));
		}
		else if (size (options_error) > 0)
		{
		    Popup::Error(options_error);
		    UI::SetFocus (`id (`optionsent));
		}
		else
		{
		    fstab_ent = $[
			"spec"    : server+":"+pth,
			"file"    : mount,
			"mntops"  : options,
			];
		    ret = `ok;
		}
	    }
	    else if (ret == `help)
	    {
		//help text 1/3
		// change: locally defined -> servers on LAN
		string helptext = _("<p>Enter the <b>host name</b> of the NFS server.  With
<i>Choose</i>, browse through a list of
NFS servers on the local network.</p>
");
		// help text 2/3
		// added "Select" button
		helptext = helptext + _("<p>Also supply the directory on the remote NFS server and a
local <b>mount point</b> at which the directory should be mounted. With
<i>Select</i>, select from the exported directories
of the chosen server.  With
<i>Browse</i>, select your mount point
interactively.</p>");
		// help text 3/3
		helptext = helptext + _("<p>For a list of <b>Options</b> please read the manpage mount(8).</p>");
		// popup heading
		Popup::LongText (_("Help"), `RichText (helptext), 50, 18);
	    }
	}
	while (ret != `ok && ret != `cancel);

	UI::CloseDialog();
	Wizard::RestoreScreenShotName ();

	if (ret == `ok)
	    return fstab_ent;
	return nil;
    }



    /**
     * NFS client dialog itselfs
     * @return `back, `abort or `next
     */
    define symbol FstabDialog () ``{
	Wizard::SetScreenShotName ("nfs-client-1-fstab");

	boolean changed = false;

	// firewall widget using CWM
	map<string, any> fw_settings = $[
	    "services": [ "nfs-client" ],
	    "display_details": true,
	    ];
	map<string,any> fw_cwm_widget = CWMFirewallInterfaces::CreateOpenFirewallWidget (fw_settings);

	// Help, part 1 of 3
	string help_text = _("<P>The table contains all the NFS entries which will be written to /etc/fstab.
For changing the configuration please use the <B>Add</B>, <B>Edit</B> and
<B>Delete</B> buttons.</P>
");

	// Help, part 2 of 3; Label::BackButton, Label::FinishButton
	help_text = help_text + _("<P>To confirm the changes, press <B>Finish</B>. To dismiss them,
use <B>Back</B>.</P>\n");

	// Help, part 3 of 3
	help_text = help_text + _("<P>For further information about fstab please type <B>man fstab</B>
in a terminal.</P>
");

	help_text = help_text + fw_cwm_widget["help"]:"";


	list<map<string,any> > nfs_entries = Nfs::nfs_entries;

	term contents =
	    `VBox(
		`Table(`id(`fstable), `opt(`notify, `immediate),
		       `header(
			   // table header
			   _("Server")+"  ",
			   // table header
			   _("Remote filesystem")+"  ",
			   // table header
			   _("Mountpoint")+"  ",
			   // table header
			   _("Options")+"  "),
		       FstabTableItems(nfs_entries)),
		// button label
		`HBox(`PushButton(`id(`newbut), `opt (`key_F3), _("A&dd")),
		      // button label
		      `PushButton(`id(`editbut), `opt (`key_F4), _("&Edit")),
		      // button label
		      `PushButton(`id(`delbut), `opt (`key_F5), _("De&lete"))),
		`VSpacing (1),
		fw_cwm_widget["custom_widget"]:`Empty (),
		// frame label
		`VSpacing(0.2)
		);

	// dialog heading
	Wizard::SetContentsButtons(_("Configuration of the NFS client"),
				   contents, help_text,
				   Label::BackButton (),
				   Label::FinishButton ());

	// initialize the widget (set the current value)
	CWMFirewallInterfaces::OpenFirewallInit (fw_cwm_widget, "");

	UI::ChangeWidget (`id(`editbut), `Enabled, false);
	UI::ChangeWidget (`id(`delbut), `Enabled, false);

	map event = nil;
	any ret = nil;

	do
	{

	   integer entryno = (integer) UI::QueryWidget(`id(`fstable), `CurrentItem);
	   UI::ChangeWidget (`id(`editbut), `Enabled, entryno != nil);
	   UI::ChangeWidget (`id(`delbut), `Enabled, entryno != nil);

	   // Kludge, because a `Table still does not have a shortcut.
	   // Simple to solve here: there's only the table and buttons,
	   // so it is OK to always set focus to the table
	   UI::SetFocus (`id (`fstable));

	   event = UI::WaitForEvent ();	   
	   ret = event["ID"]:nil;
	   if (ret == `cancel)
	   {
	       ret = `abort;
	   }

	   // handle the events, enable/disable the button, show the popup if button clicked
	   CWMFirewallInterfaces::OpenFirewallHandle (fw_cwm_widget, "", event);
	   changed =
	       true ||		// TODO: fw changed, #44106
	       true;		// TODO: nfs changed
	   entryno = (integer) UI::QueryWidget(`id(`fstable), `CurrentItem);

	   if (ret == `newbut)
	   {
	       map<string,any> entry = GetFstabEntry (nil, (list<map>) union (Nfs::non_nfs_entries, nfs_entries));

	       if (entry != nil)
		   nfs_entries = add (nfs_entries, entry);

	       UI::ChangeWidget (`id(`fstable), `Items, FstabTableItems (nfs_entries));
	   }
	   else if (ret == `editbut)
	   {
		   integer count = 0;
		   map<string,any> entry = GetFstabEntry (nfs_entries[entryno]:$[], // Default values
					      (list<map>) union (Nfs::non_nfs_entries, remove (nfs_entries, entryno)));
		   if (entry != nil)
		   {
		       integer count = 0;
		       nfs_entries = maplist(map<string,any> ent, nfs_entries, ``{
			   count = count+1;
			   if (count-1 != entryno)
			   return ent;
			   return entry;
		       });

		       UI::ChangeWidget (`id(`fstable), `Items, FstabTableItems (nfs_entries));
		   }
	   }
	   else if (ret == `delbut && size (nfs_entries) > 0)
	   {
		   integer count = 0;
		   nfs_entries = remove (nfs_entries, entryno);

		   UI::ChangeWidget (`id(`fstable), `Items, FstabTableItems (nfs_entries));
	   }
	   else if (ret == `abort && changed && !Popup::ReallyAbort (true))
	   {
	       ret = `again;
	   }
	}
	while (ret != `back && ret != `next && ret != `abort);

	if (ret == `next)
	{
	    // grab current settings, store them to SuSEFirewall::
	    CWMFirewallInterfaces::OpenFirewallStore (fw_cwm_widget, "", event);

	    Nfs::nfs_entries = nfs_entries;
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) ret;
    }

}
