/**
 * File:	include/autoinstall/classes.ycp
 * Module:	AutoInstallation
 * Summary:	This file handles classes
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * $Id: classes.ycp 20307 2004-11-14 02:19:38Z nashif $
 */
{
    textdomain "autoinst";
    import "Label";
    import "Popup";
    import "AutoinstClass";
    import "XML";


    /**
     * XML_cleanup()
     * @return boolean
     */
    define boolean XML_cleanup(string in, string out)
	``{
	map ycpin = XML::XMLToYCPFile(in);
	y2debug("Writing clean XML file to  %1, YCP is (%2)", out, ycpin);
	return XML::YCPToXMLFile(`profile, ycpin, out);
    }


    /**
     * class_dialog_contents()
     * @return term
     */
    define term class_dialog_contents ()
	``{
	list classes = maplist(map class , AutoinstClass::Classes,  ``{

	    string pathtoClass = sformat("%1/%2", AutoinstConfig::classDir, class["name"]:("Unknown"));
	    list files_in_class = (list) SCR::Read(.target.dir, pathtoClass);
	    y2milestone("class: %1", class);
	    term i = `item(
			  `id(class["name"]:"Unknown"),
			  class["name"]:"No name",
			  class["order"]:0,
			  size(files_in_class)
			  );
	    return (i) ;
	});

	term contents =
	    `VBox(
		  `VWeight(40, `Table( `id(`table), `opt (`notify, `immediate),
				       `header(_("Class Name"), _("Order"), _("Configurations")),
				       classes
				       )
			   ),
		  `VSpacing(1),
		  `VWeight(40,`RichText(`id(`description), _("Class Description"))),
		  `VSpacing(1),
		  `HBox(
			`PushButton(`id(`new), _("Ne&w")),
			`PushButton(`id(`edit), _("&Edit")),
			`PushButton(`id(`delete), _("&Delete"))
			)
		  );
	return contents;
    }



    /**
     * AddEditClasses()
     * Add or Edit a class
     * @param symbol mode
     * @param symbol
     */
    define symbol AddEditClasses(symbol mode, string name)
	{
	list classNames = maplist(map c, AutoinstClass::Classes, ``(c["name"]:"")
				  );

	map class = $[];
	if (mode == `edit)
	{
	    list cl = filter( map c,
				  AutoinstClass::Classes,
				  ``(c["name"]:""== name));
            map class = cl[0]:$[];
	}

        string tmp = sformat("%1", size(AutoinstClass::Classes) + 1);
	integer order =  tointeger(class["order"]:tmp);

	UI::OpenDialog( `opt(`decorated),
			`HBox(
			      `HSpacing(0.5),
			      `VBox(
				    `Heading( _("Edit or Create Classes")),
				    `VSpacing(1),
				    `HBox(
					  `TextEntry(`id(`name), _("Na&me"), class["name"]:"" ),
					  `IntField(`id(`order), _("Or&der:"), 1, 10, class["order"]:order)
					  ),
				    `MultiLineEdit(`id(`description), `opt(`hstretch),
						   _("Descri&ption:"),
						   class["description"]:""),
				    `VSpacing(1),
				    `HBox(
					  `PushButton(`id(`save), Label::SaveButton()),
					  `PushButton(`id(`cancel), Label::CancelButton())
					  )
				    ),
			      `HSpacing(0.5)
			      )
			);
	if (mode == `edit)
	{
	    UI::ChangeWidget(`id(`name), `Enabled, false);
	}
	symbol ret = `none;
	repeat {
	    ret = (symbol) UI::UserInput();
	    if (ret == `save )
	    {
		if ((string) UI::QueryWidget(`id(`name),`Value) == "")
		{
		    Popup::Message(_("That name is already used. Select another name."));
		    ret = `again;
		    continue;
		}
		if (mode !=`edit && contains(classNames, UI::QueryWidget(`id(`name),`Value)))
		{
		    Popup::Message(_("That name is already used. Select another name."));
		    ret = `again;
		    continue;
		}
		string name = (string) UI::QueryWidget(`id(`name),`Value);
		if (checkFileName(name) != 0 || name == "")
		{
		    Popup::Error(invalidFileName());
		    ret = `again;
		    continue;
		}
		map newClass = $[
				 "name": name,
				 "order": (integer) UI::QueryWidget(`id(`order),`Value),
				 "description": (string)UI::QueryWidget(`id(`description),`Value)
		];
		if (mode == `new)
		{
		    AutoinstClass::Classes=add(AutoinstClass::Classes, newClass);
		    SCR::Execute(.target.mkdir, AutoinstConfig::classDir + "/" + name);
		}
		else
		{
		    AutoinstClass::Classes=    maplist(
						 map c, AutoinstClass::Classes,``{
						     if (c["name"]:"" == (string) UI::QueryWidget(`id(`name),`Value)) {
							 return newClass;
						     }
						     else
						     {
							 return c;
						     }
						 });
		}

	    }
	} until (ret == `save || ret == `cancel);

	UI::CloseDialog();

	return (symbol)ret;
    }




    /**
     * Manage Classes
     *
     */
    define symbol ManageClasses()
	``{

        Wizard::CreateDialog();
	string title = _("Classes");

	string help = _("<p>Use this interface to define classes of control files. </p>
");

	help = help + _("<p>For example, you can define a class of configurations for
a specific  department, group, or site in your company environment.</p>
");

	help = help + _("<p>The order (priority) defines the hierarchy of a class
and when it is merged when creating a control file.
</p>
");


	Wizard::SetContents(title,
			    class_dialog_contents() , help, true, true);

	Wizard::HideAbortButton();
	Wizard::SetNextButton(`next, Label::FinishButton() );

	Wizard::HideAbortButton();
	any ret = nil;
	repeat
	    {
		if (size(AutoinstClass::Classes) == 0 )
		{
		    UI::ChangeWidget(`id(`edit), `Enabled, false);
		    UI::ChangeWidget(`id(`delete),`Enabled, false);
		}

		string class =  (string) UI::QueryWidget(`id(`table), `CurrentItem);
                list cl = filter(map c,  AutoinstClass::Classes,``(c["name"]:""  == class) );
		map selected_class = cl[0]:$[];
		if ( class != nil )
		{
		    UI::ChangeWidget(`id(`description), `Value, selected_class["description"]:_("No Description"));
		}

		ret =(symbol) UI::UserInput();

		if (ret == `new)
		{
		    symbol defret = AddEditClasses((symbol)ret, "");

		    Wizard::SetContents(title,
					 class_dialog_contents(), help, true, true);
		}
		else if (ret ==`edit)
		{
		    if (class == nil)
		    {
			Popup::Message(_("Select at least one class
to edit.
"));
			continue;
		    }
		    symbol defret = AddEditClasses((symbol)ret, class);
		    Wizard::SetContents(title,
					class_dialog_contents(), help, true, true);
		}
		else if (ret ==`delete)
		{
		    if (class == nil)
		    {
			Popup::Message(_("Select at least one class
to delete.
"));
			continue;
		    }
		    AutoinstClass::Classes = filter(map c,  AutoinstClass::Classes,``( c["name"]:""!=class) );

		    Wizard::SetContents(title,
					class_dialog_contents(), help, true, true);

		}



	    } until (ret == `back || ret == `next );

	if (ret == `next)
	{
	    AutoinstClass::Save();
	}
        Wizard::CloseDialog();
	return (symbol)ret;

    }


    /**
     * GetClassOrder()
     * @param string class name
     * @return integer class order
     */
    define integer GetClassOrder(string name)``{
	integer order = 0;
	foreach(map class, AutoinstClass::Classes, ``{
	    if (class["name"]:"" == name)
		order = class["order"]:0;
	});
	return order;
    }




    /**
     * The merge operation
     */
    define boolean MergeAll(list<map> selected_profiles, symbol base)
	``{
	string tmpdir = AutoinstConfig::tmpDir;
	SCR::Execute(.target.mkdir, tmpdir);

	if (base == `current)
	{
	    Profile::Save( tmpdir + "/base_profile.xml");
	}


	boolean error = false;
	boolean skip = false;
	foreach( map c, selected_profiles,
		``{
	    list pl = filter(map cc, AutoinstClass::confs,
					``( cc["name"]:"" == c["profile"]:""  && cc["class"]:"yyy" == c["class"]:"xxx")
					);
            map profile = pl[0]:$[];
	    if (base == `empty && !skip)
	    {
		y2milestone("Working on profile: %1", profile);
		SCR::Execute(.target.bash, "cp " + AutoinstConfig::classDir + "/" + profile["class"]:""  + "/" +  profile["name"]:"" +  "  " + tmpdir + "/base_profile.xml");
		skip = true;
	    }
	    else
	    {
		y2milestone("Working on profile: %1", profile);
		string base_text = "empty control file";
		if (base != `empty)
		{
		    base_text = "current control file";
		}

		if (profile["name"]:"" != "" && !error)
		{
                    Popup::ShowFeedback(sformat("Merging %1 (%2) with %3 ....",
                                profile["name"]:"",profile["class"]:"",
                                base_text), "");

		    y2milestone("Merging %1 with %2....", profile["name"]:"", base_text);

		    map xsltret = AutoinstClass::MergeClasses(profile,  tmpdir + "/base_profile.xml", "result.xml");
		    if (xsltret["exit"]:-1 != 0 )
		    {
			Popup::Error(sformat(_("Merge failed:\n %1"), xsltret["stderr"]:"error"));
			error =  true;
		    }
		    XML_cleanup(tmpdir + "/result.xml",  tmpdir + "/base_profile.xml");

		}
		else
		{
		    error = true;
		}
	    }
	});


	if (error)
	{
            Popup::ClearFeedback();
	    return false;
	}

	// Backup file
	SCR::Execute(.target.bash, "cp " + tmpdir + "/result.xml /var/lib/autoinstall/tmp/autoinst_result.xml" );

	Profile::ReadXML("/var/lib/autoinstall/tmp/autoinst_result.xml" );
	SCR::Execute(.target.remove, " /var/lib/autoinstall/tmp/autoinst_result.xml");
        Popup::ClearFeedback();

	return true;
    }


    /**
     * Merge Dialog
     * @return symbol
     */
    define symbol MergeDialog()
	``{


        Wizard::CreateDialog();
	string title = _("Merge Classes");
	map<string, list> profiles = $[];

	term Combo = `VBox();
	AutoinstClass::Files();
	foreach(map prof, AutoinstClass::confs, ``{

	    string class = prof["class"]:"Default";
	    list ui_list = profiles[class]:[];

	    if (size(ui_list)==0)
	    {
		ui_list=add(ui_list,`item(`id("none"),
					  _("None"))
			    );
	    }

	    ui_list=add(ui_list,`item(`id(prof["name"]:"Unknown"),
				      prof["name"]:"Unknown")
			);

	    profiles[class] =  ui_list;
	});


	if ( size(profiles) > 0 )
	{
	    foreach(string k, list v, profiles,``{
		Combo = add(Combo,
			  `HBox(
				`HWeight(50,(`Left(
						   `ComboBox(`id(k),`opt(`hstretch,`autoShortcut),
							     k,
							     v
							     )
						   )
					     )
					 ),
				`HWeight(50, `Empty()
					 )
				)
			  );
	    });
	}
	else
	{
	    Combo = `Left(`Label(`id(`emptyclasses), _("No control files defined")));
	}


	term contents =
    `Top(
	 `Left(
	       `VBox(
		     Combo,
		     `VSpacing(),
		     `RadioButtonGroup(`id(`rbg),
				       `VBox(
					     `Left(
						   `RadioButton(`id(`empty),
								_("&Merge with Empty Control File"), true)
						   ),
					     `Left(
						   `RadioButton(`id(`current),
								_("Merge with &Currently-Loaded Control File"))
						   )
					     )
				       ),
		     `PushButton(`id(`merge), _("Merge Cla&sses"))
		     )
	       )
	 );


	string help = _("<p>If you have defined and created <b>
classes</b>, you will be able to merge them using this interface to create
a new <i>Profile</i>, which will contain information from every class
depending on the priority (order) set when
creating the classes.</P>
");

	help = help + _("<P>To merge the classes, an <b>XSLT</b>
script is used.</P>
");

	Wizard::SetContents(title,
			    contents, help , true, true);

	Wizard::HideAbortButton();
	list widgets = [ `merge, `empty, `current ];

	Wizard::SetNextButton(`next, Label::FinishButton() );
	Wizard::DisableNextButton();

	any ret = nil;
	repeat {
	    ret = UI::UserInput();
	    symbol base = (symbol)UI::QueryWidget(`id(`rbg), `CurrentButton);
	    integer n = 0;
	    if (ret == `merge)
	    {
		list<map> selected_profiles = [];
		foreach (map prof, AutoinstClass::confs,``{

		    any selected = UI::QueryWidget(`id(prof["class"]:""), `Value);

		    if (selected != nil) {
			if (selected != "none") {
			    n = n +1;
			}
			selected_profiles = add(selected_profiles,
						$[
						  "order": GetClassOrder(prof["class"]:""),
						  "class":prof["class"]:"none",
						  "profile":selected
						]
						);
		    }

		});

		if (n ==  0) {
		    integer min = 1;

		    if (base == `empty)
		    {
			min = 2;
		    }

		    Popup::Error(sformat(_("Select at least %1  configurations
to perform a merge.
"), min));
		    continue;
		}

		list<map> sorted_profiles =  sort(map x, map y, filter(map c, toset(selected_profiles),``(c["profile"]:""!= "none")),
					     ``(x["order"]:0<y["order"]:0)
					     );
		y2debug("Selected Profiles: %1", sorted_profiles);

		if ( size(sorted_profiles)>0 )
		{
		    y2milestone("Calling merge with %1 (%2)", sorted_profiles, base);
		    MergeAll(sorted_profiles, base);
		}

		Wizard::EnableNextButton();
	    }
	} until  (ret == `next || ret == `back );

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



    define symbol classConfiguration() ``{

	string title = _("Class Configuration");
	string help = _("<p>Choose one or more of the listed classes to which the current control
file should belong.</p>
");

	AutoinstClass::Files();

	map<string, list> profiles = $[];

	foreach(map prof, AutoinstClass::confs, ``{
	    string class = prof["class"]:"default";
	    list ui_list = profiles[class]:[];


	    if (size(ui_list)==0)
	    {
		ui_list=add(ui_list,`item(`id("none"),
					  _("None"))
			    );
	    }

	    ui_list=add(ui_list,`item(`id(prof["name"]:"Unknown"),
				      prof["name"]:"Unknown"));

	    profiles[class] =  ui_list;
	});

	term Combo = `VBox();

	if (size(profiles)>0) {
	    foreach(string k, list v, profiles,``{
		Combo=add(Combo,
			  `Left(
				`ComboBox(`id(k),`opt(`hstretch,`autoShortcut),  k,
					  v
					  )
				)
			  );
	    });
	}
	else
	{
	    Combo = `Left(`Label(`id(`emptyclasses), _("No profiles in this class")));
	}

	term contents =
	    `Top(
		 `Left(
		       `VBox(
			     Combo
			     )
		       )
		 );


	Wizard::SetContents(title,
			    contents, help , true, true);

	Wizard::HideAbortButton();
	Wizard::SetNextButton(`next, Label::FinishButton() );


	any ret = nil;
	symbol next = nil;
	repeat {
	    ret = UI::UserInput();
	    integer n = 0;
	    if (ret ==`next)
	    {
		list<map> selected_profiles = [];
		foreach(map prof, AutoinstClass::confs,``{

		    any selected = UI::QueryWidget(`id(prof["class"]:""), `Value);

		    if (selected != nil) {
			if (selected != "none") {
			    n = n +1;
			}
			selected_profiles = add(selected_profiles,
						$[
						  "class_name":prof["class"]:"none",
						  "configuration":selected
						]
						);
		    }

		});

		if (n ==  0) {


		    Popup::Error(_("Select at least one class configuration.
"));
		    ret = `again;
		    continue;
		}

		list<map> sorted_profiles =  filter(map c, toset(selected_profiles),``(c["configuration"]:""!= "none")
					       );

		y2debug("Selected Profiles: %1", sorted_profiles);
		AutoinstClass::profile_conf = sorted_profiles;
	    }


	} until  (ret == `next || ret ==`back );
	return (symbol)ret;
    }












}
