/**
 * File:	include/network/services/routing.ycp
 * Package:	Network configuration
 * Summary:	Routing configuration dialogs
 * Authors:	Michal Svec <msvec@suse.cz>
 *
 * $Id: routing.ycp 22129 2005-03-02 10:04:23Z locilka $
 *
 * Routing configuration dialogs
 */

{

textdomain "network";

import "IP";
import "Label";
import "Netmask";
import "Popup";
import "Routing";
import "Wizard";

include "network/routines.ycp";

define term RoutingEditDialog(integer id, term entry, list devs);

/**
 * Main routing dialog
 * @param standalone true if not run from another ycp client
 * @return dialog result
 */
define any RoutingMainDialog(boolean standalone) {

    ScreenName("routing");

    /* Routing dialog caption */
    string caption = _("Routing Configuration");

    /* Routing dialog help 1/2 */
    string help = _("<p>The routing can be set up in this dialog.
The <b>Default Gateway</b> matches every possible destination, but poorly. 
If any other entry exists that matches the required address, it is
used instead of the default route. The idea of the default route is simply
to enable you to say \"and everything else should go here.\"</p>
") +

    /* Routing dialog help 2/2 */
_("<p>Enable <b>IP Forwarding</b> if the system is a router.</p>");

    integer max = 0;
    integer items = 0;
    list<term> table_items = [];
    list<term> table_items_orig = [];
    boolean expert = false;
    string defgw = "";
    string defgwdev = "";

    boolean forward = Routing::Forward;
    list<map> route_conf = Routing::Routes;

    /* make ui items from the routes list */
    term item = nil;
    maplist(map r, route_conf, {
	if(r["destination"]:"" == "default") {
	    defgw = r["gateway"]:"";
	    defgwdev = r["device"]:"";
	}
	else {
	    item = `item(`id(items),
		    r["destination"]:"",
		    r["gateway"]:"",
		    r["netmask"]:"",
		    r["device"]:"");
	    table_items = add(table_items, item);
	    items = items + 1;
	}
    });
    table_items_orig = (list<term>) eval(table_items);

    y2debug("table_items=%1", table_items);
    y2debug("items=%1", items);
    max = items;

    list devs = Routing::GetDevices();
    y2debug("devs=%1", devs);

    /* Routing dialog contents */
    term contents = `HBox(
	`HSpacing(5),
	`VBox(
	    `VStretch(),
	    /* ComboBox label */
	    `ComboBox(`id(`gw), `opt(`editable, `hstretch), _("Default &Gateway"), [ defgw ]),
	    `VSpacing(1),
	    /* Frame label */
	    `Frame(_("Routing Table"), `VBox(
		/* CheckBox label */
		`Left(`CheckBox(`id(`expert), `opt(`notify), _("E&xpert Configuration"))),
		`Table(`id(`table), `opt(`notify), `header(
		    /* Table header 1/4 */
		    _("Destination"),
		    /* Table header 2/4 */
		    _("Gateway"),
		    /* Table header 3/4 */
		    _("Netmask"),
		    /* Table header 4/4 */
		    _("Device")), []),
		/* PushButton label */
		`HBox(`PushButton(`id(`add), _("Ad&d")),
		/* PushButton label */
		`PushButton(`id(`edit), `opt(`disabled), _("&Edit")),
		/* PushButton label */
		`PushButton(`id(`delete), `opt(`disabled), _("De&lete"))))),
	    `VSpacing(1),
	    /* CheckBox label */
	    `Left(`CheckBox(`id(`forward), _("Enable &IP Forwarding"))),
	    `VStretch()
	),
	`HSpacing(5)
    );

    if(standalone == true)
	Wizard::SetContentsButtons(caption, contents, help,
		Label::BackButton(), Label::FinishButton());
    else
	Wizard::SetContentsButtons(caption, contents, help,
		Label::BackButton(), Label::OKButton());

    UI::ChangeWidget(`id(`gw), `ValidChars, IP::ValidChars);
    UI::ChangeWidget(`id(`table), `Items, table_items);
    UI::ChangeWidget(`id(`forward), `Value, forward);
    UI::ChangeWidget(`id(`table), `Enabled, expert);
    UI::ChangeWidget(`id(`add), `Enabled, expert);
    UI::SetFocus(`id(`gw));

    any ret = nil;
    while(true) {

	UI::ChangeWidget(`id(`edit), `Enabled, expert && (items > 0));
	UI::ChangeWidget(`id(`delete), `Enabled, expert && (items > 0));

	ret = UI::UserInput();

	/* abort? */
	if(ret == `abort || ret == `cancel) {
	    /* handle the changed dialog values */
	    boolean modified = (table_items_orig != table_items ||
		    forward != UI::QueryWidget(`id(`forward), `Value) ||
		    defgw != UI::QueryWidget(`id(`gw), `Value));
	    if(ReallyAbortCond(modified)) break;
	    else continue;
	}
	/* expert mode */
	else if(ret == `expert) {
	    expert = (boolean) UI::QueryWidget(`id(`expert), `Value);
	    UI::ChangeWidget(`id(`table), `Enabled, expert);
	    UI::ChangeWidget(`id(`add), `Enabled, expert);
	    continue;
	}
	/* edit route */
	else if(ret == `edit || ret == `table) {
	    integer cur = (integer) UI::QueryWidget(`id(`table), `CurrentItem);
	    list<term> cur_item = filter(term e, table_items, {
		return cur == e[0, 0]:nil;
	    });

	    y2debug("cur=%1", cur);
	    y2debug("cur_item=%1", cur_item);

	    term item = cur_item[0]:nil;
	    /* add at least the current device, if not, device is lost */
	    string dev = item[4]:"";
	    if (dev != "" && !contains(devs, dev))
		devs = add(devs, dev);
	    devs = sort(devs);

	    item = RoutingEditDialog(cur, item, devs);
	    if(item == nil) continue;

	    table_items = maplist(term e, table_items, {
		if(cur == e[0, 0]:-1)
		    return item;
		return e;
	    });
	    UI::ChangeWidget(`id(`table), `Items, table_items);
	    UI::ChangeWidget(`id(`table), `CurrentItem, cur);
	    continue;
	}
	/* add route */
	else if(ret == `add) {
	    term item = RoutingEditDialog(max, `empty(), devs);
	    if(item == nil) continue;

	    table_items = add(table_items, item);
	    UI::ChangeWidget(`id(`table), `Items, table_items);
	    UI::ChangeWidget(`id(`table), `CurrentItem, max);
	    items = items + 1;
	    max = max + 1;
	    continue;
	}
	/* delete route */
	else if(ret == `delete) {
	    items = items - 1;
	    integer cur = (integer) UI::QueryWidget(`id(`table), `CurrentItem);
	    table_items = filter(term e, table_items, {
		return cur != e[0, 0]:nil;
	    });
	    UI::ChangeWidget(`id(`table), `Items, table_items);
	    continue;
	}
	else if(ret == `back) {
	    break;
	}
	else if(ret == `next) {
	    string gw = (string) UI::QueryWidget(`id(`gw), `Value);
	    if(gw != "" && !IP::Check(gw)) {
		/* Popup::Error text */
		Popup::Error(_("The default gateway is invalid."));
		UI::SetFocus(`id(`gw));
		continue;
	    }
	    break;
	}
	else {
	    y2error("Unexpected retcode: %1", ret);
	    continue;
	}
    }

    /* update routes from widgets */
    if(ret == `next) {
	route_conf = maplist(term e, table_items, {
	    return $[
		"destination" : e[1]:"",
		"gateway"     : e[2]:"",
		"netmask"     : e[3]:"",
		"device"      : e[4]:"",
	    ];
	});

	if(defgw != UI::QueryWidget(`id(`gw), `Value))
	    defgwdev = "-"; // FIXME: find correct device

	defgw = (string) UI::QueryWidget(`id(`gw), `Value);
	forward = (boolean) UI::QueryWidget(`id(`forward), `Value);

	if(defgw != "")
	    route_conf = add(route_conf, $[
		"destination"	: "default",
		"gateway"	: defgw,
		"netmask"	: "-",
		"device"	: defgwdev
	    ]);

    }

    Routing::Routes = route_conf;
    Routing::Forward = forward;

    y2debug("table_items=%1",table_items);
    y2debug("route_conf=%1",route_conf);

    return ret;
}

/**
 * Route edit dialog
 * @param id id of the edited route
 * @param entry edited entry
 * @param devs available devices
 * @return route or nil, if canceled
 */
define term RoutingEditDialog(integer id, term entry, list devs) {

    ScreenName("routing-edit");

    UI::OpenDialog(`opt(`decorated),`VBox(
	`HSpacing(1),
	`VBox(
	    /* TextEntry label */
	    `TextEntry(`id(`destination), _("&Destination"), entry[1]:""),
	    /* TextEntry label */
	    `TextEntry(`id(`gateway), _("&Gateway"), entry[2]:""),
	    /* TextEntry label */
	    `TextEntry(`id(`netmask), _("&Netmask"), entry[3]:""),
	    /* ComboBox label */
	    `ComboBox(`id(`device), `opt(`editable, `hstretch), _("De&vice (optional)"), devs)
	),
	`HSpacing(1),
	`HBox(
	    `PushButton(`id(`ok), `opt(`default), Label::OKButton()),
	    `PushButton(`id(`cancel), Label::CancelButton())
	)
    ));

    if(entry[4]:"" != "")
	UI::ChangeWidget(`id(`device), `Value, entry[4]:"");
    UI::ChangeWidget(`id(`destination), `ValidChars, IP::ValidChars + "default");
    UI::ChangeWidget(`id(`gateway), `ValidChars, IP::ValidChars);
    UI::ChangeWidget(`id(`netmask), `ValidChars, Netmask::ValidChars);

    if(entry == `empty())
	UI::SetFocus(`id(`destination));
    else
	UI::SetFocus(`id(`gateway));

    any ret = nil;
    term route = nil;

    while(true) {
	route = nil;
	ret = UI::UserInput();
	if(ret != `ok) break;

	route = `item(`id(id));
	string val = (string) UI::QueryWidget(`id(`destination), `Value);
	if(val != "default" && !IP::Check(val)) {
	    /* Popup::Error text */
	    Popup::Error(_("Destination is invalid."));
	    UI::SetFocus(`id(`destination));
	    continue;
	}
	route = add(route, val);
	val = (string) UI::QueryWidget(`id(`gateway), `Value);
	if(!IP::Check(val)) {
	    /* Popup::Error text */
	    Popup::Error(_("Gateway IP address is invalid."));
	    UI::SetFocus(`id(`gateway));
	    continue;
	}
	route = add(route, val);
	val = (string) UI::QueryWidget(`id(`netmask), `Value);
	if(val != "0.0.0.0" && !Netmask::Check(val)) {
	    /* Popup::Error text */
	    Popup::Error(_("Subnetmask is invalid."));
	    UI::SetFocus(`id(`netmask));
	    continue;
	}
	route = add(route, val);
	val = (string) UI::QueryWidget(`id(`device), `Value);
	route = add(route, val);
	break;
    }

    UI::CloseDialog();
    if(ret != `ok) return nil;
    y2debug("route=%1", route);
    return route;
}

/**
 * Check if internal data differ from the dialog values
 * @param defgw current default gw widget contents
 * @return true if differ
 */
define boolean RoutingModified(string defgw) {
    boolean forward = (boolean) UI::QueryWidget(`id(`forward), `Value);
    string defg = (string) UI::QueryWidget(`id(`gw), `Value);
    return forward != Routing::Forward || defg != defgw;
}

/* EOF */
}
