/**
 * File:	OSRPkgUI.ycp
 * Module:	repair
 * Summary:	Packages check UI
 * Authors:	Johannes Buchhold <jbuch@suse.de>
 *
 * $Id: OSRPkgUI.ycp 21333 2005-02-07 15:59:08Z jsuchome $
 *
 * Provide osr mode information.
 */
{
    module "OSRPkgUI";

    import "HTML";
    import "Report";
    import "Label";
    import "Popup";

    import "OSRSystem";
    import "OSRExecute";
    import "OSRLogFile";
    import "OSRProgress";
    import "OSRMode";
    import "OSRPkgVerify";
    import "OSRPopup";

    textdomain "repair";

    ////////////////////////////////////////////////////////
    // Static
    ////////////////////////////////////////////////////////

    /**
     * Symbol and key's for the widget comunication
     */
    symbol package_progress_bar  = `package_progress_bar;
    string package_widget	 = "package_widget";
    string ok_or_cancel_key	 = "ok_or_cancel";
    string pause_key		 = "pause";
    string mode_key		 = "mode";
    string mode_replace_point_key = "replace_point";

    // buttons label
    string pause_label		= _("&Pause");
    // buttons label
    string continue_label	= _("&Continue");

    /**
     * All allowed rpm test options with description.
     */
    list<list> modes = [
	// 'rpm -V <package>' output flag
	["S",	_("The File Size")			],
	["M",	_("The File Mode")			],
	["5",	_("The MD5 Checksum of the File")	],
	["D",	_("The File Major and Minor Number")	],
	["L",	_("The File Symbolic Link Content")	],
	["U",	_("The Owner of the File")		],
	["G",	_("The File Group")			],
	["T",	_("The Last Modification Time")		]
    ];

    ////////////////////////////////////////////////////////
    // Not static
    ////////////////////////////////////////////////////////

    /**
     * True if the user have paused the verifying process.
     */
    boolean pause		 = false;

    /**
     * Show successful package checks too.
     */
    boolean show_ok		 = false;

    /**
     * Summary text of all passed and not passed packages.
     */
    string verified_text_ok	 = "";

    /**
     * Summary text of the not passed packages only.
     */
    string verified_text	 = "";

    /**
     * Count of packages which should be checked.
     */
    integer package_count        = 0;

    /**
     * Dialogs for expert and normal mode.
     */
    list dialogs		 =  [];

    /**
     * If expert is activated.
     */
    global boolean expert_mode	 = false;

    /**
     * if current UI can use `DownloadProgress
     */
    boolean has_progress_widget	= false;

    /**
     * packages that should be reinstalled after verification
     * (only helper structure, for showing list in dialogs)
     */
    list packages_to_reinstall	= [];


    //-----------------------------------------------------------------

    /**
     *
     */
    boolean protocol_mode	= false;

    // crate header for verification output richtext
    define void set_verify_text_header() {

	string header        = "";
	string hr            = "";
	list<string> special = [];

	// Build headers for the tables in qt-mode
	has_progress_widget = UI::HasSpecialWidget (`DownloadProgress);
	if (has_progress_widget)
	{
	    special = [ "width=400", "align=right"];

	    header = "<table %5><tr> <th>%1</th>
                                 <th>%2</th>
                                 <th>%3</th>
                                 <th %6> %4</th>
                            </tr>";

	    hr     = sformat("<tr> <th><hr></th>
                               <td><hr></td>
                               <td><hr></td>
                               <td %1><hr></td>
                               </tr>", special[1]:"");

	    verified_text_ok = sformat( header,
					HTML::Bold(_("Package Name")),
					HTML::Bold(_("Files ")),
					HTML::Bold(_("Configuration File")),
					HTML::Bold(_("Status")),
					special[0]:"",
					special[1]:"" );

	    verified_text_ok = verified_text_ok + hr;
	    verified_text    = verified_text_ok;
	}
	else {
	    y2debug("no headline");
	}
    }

     /**
     * Reset all (not static)  module settings.
     */
    global define void Reset()``{

	// Reset values
	show_ok			= false;
	pause			= false;
	package_count		= 0;
	expert_mode		= false;
	dialogs			= [];
	protocol_mode		= false;
	verified_text		= "";
	verified_text_ok	= "";
	packages_to_reinstall	= [];

	OSRProgress::Reset();

    }

    /**
     * Constructor- reset module settings.
     */
    global define void OSRPkgUI()``{
	Reset();
    }

    /**
     * Fill a string with the specified character.
     * Used in ncurses mode for formatting.
     */
    define string fill(string str, integer length, string c)``{
	if (size(str) > length ) return substring(str, 0, length);
	while( size(str) < length ) { str = str +c ;}
	return str;
    }

    /**
     * Return the current dialog ( expert or normal dialog).
     */
    define term get_verify_dialog()``{

	integer pos =  0;
	if (protocol_mode ) pos = 3;
	else {
	    pos = ( expert_mode ) ? 0 : 1 ;
	}
	return dialogs[ pos ]:`Empty();
    }

    /**
     * Repaint the displayed text depending on the show_ok value (CheckBox Show all).
     */
    define boolean repaint()``{

	if (UI::WidgetExists(`id(package_widget )))
	{
	    string table = "</table>";

	    if (!has_progress_widget)
	    {
		table = "";
	    }

	    if (show_ok && UI::WidgetExists(`id(package_widget )))
	    {
		return UI::ChangeWidget(`id( package_widget), `Value,  verified_text_ok + table );
	    }
	    else {
		return UI::ChangeWidget(`id( package_widget), `Value,  verified_text + table );
	    }
	}
	return false;
    }

    /**
     * Switch between expert and normal mode.
     */
    define void set_expert_mode(boolean t_expert_mode )``{
	//boolean changed  = t_expert_mode != expert_mode;

	expert_mode = t_expert_mode;
	show_ok     = true;

	if (UI::WidgetExists(`id(mode_replace_point_key)))
	{
	    UI::ReplaceWidget(`id(mode_replace_point_key), get_verify_dialog());
	    UI::ChangeWidget( `id(mode_key ), `Value, expert_mode);
	    UI::RecalcLayout();
	    repaint();
	}

	if (UI::WidgetExists(`id( package_progress_bar )))
	{
	    UI::ChangeWidget(`id( package_progress_bar), `Value, OSRPkgVerify::VerifiedSize ());
	}
    }


    /**
     * Build string: Checking package: (43/234)
     */
    define string check_string()``{
	// current action label; %1 is current package number, %2 all packages
	return sformat(_("Checking package: (%1/%2)"), OSRPkgVerify::VerifiedSize (), package_count);
    }

    /**
     * Create to dialogs. One for the expert user mode and one for the
     * normal user mode. Save both dialogs in the list dialogs for
     * later use.
     */
    define void build_verify_dialog_contents(string headline)``{
	dialogs = [];

	//components for both
	term ok_cancel_button =  `PushButton( `id(ok_or_cancel_key),  Label::CancelButton());
	term switch_button    = `CheckBox(`id(mode_key ),    `opt(`notify),  _("Show &details"), expert_mode );
	term progress_bar     =  OSRProgress::CreateProgressBar(package_progress_bar,  check_string(),  package_count  );
	term show_ok_term     = `Left(`CheckBox(`id("show_ok"), `opt(`notify), _("Also Show Successfully Verified &Packages"),  show_ok));
	term protocol	      =  `RichText(`id(package_widget), `opt(`vstretch, `autoScrollDown )  , (show_ok ) ? verified_text_ok : verified_text );

	dialogs = add( dialogs,
		       `HBox(`VSpacing(20),
			     `VBox(
				   `HSpacing(80),
				   show_ok_term,
				   protocol,
				   `Left(`PushButton(`id(pause_key),  pause_label )))
			     ));

	dialogs = add( dialogs, `Empty());

	// normal dialog
	dialogs = add( dialogs,
		       `HBox(`HSpacing(3),
			     `VBox(
				   `HSpacing(80),
				   `VBox(
					 `HSpacing(80),
					 `Left(`Heading(headline)),
					 `VSpacing(0.3),
					 `Left(`Label(_("
YaST verifies packages now."))),
					 `VSpacing(1),
					 progress_bar,
					 `Left(switch_button) ),
				   `ReplacePoint(`id(mode_replace_point_key ),  dialogs[1]:`Empty() ),
				   ok_cancel_button
				   ),
			     `HSpacing(3)
			     ));

	dialogs = add( dialogs, `HBox(`HWeight(1, `HSpacing(3)),
				      `HWeight(30,`VBox(`VWeight(10, show_ok_term ),
							 `VWeight(80, protocol ),
							 `VWeight(10, `PushButton( `id(ok_or_cancel_key), Label::OKButton())  ))),
				      `HWeight(1, `HSpacing(1))));
    }

    /**
     * Open the main dialog depending on the values t_expert_mode (expert_mode).
     */
    global define boolean OpenVerifyDialog(integer size_progress , string headline , boolean t_expert_mode )``{

	if (verified_text == "")
	{
	    set_verify_text_header();
	}
	// count of package which should be verified.
	package_count   = size_progress;


	// build the dialog depending on the mode.
	build_verify_dialog_contents(headline);

	// open the dialog.
	boolean ret = UI::OpenDialog( dialogs[2]:`Empty() );

	// set mode - expert mode or normal mode.
	// set_expert_mode( t_expert_mode );

	return ret;
    }

    /**
     * Generate a key for a package without error.
     */
    define string pkg_name2ok_key(string pkg_name)``{
	return "status" + pkg_name ;
    }

    /**
     * Decode the package name from a key for a package without error.
     */
    define string ok_key2pkg_name(string input ) ``{
	if (substring(input, 0, size("status")) == "status" )
	{
	    string pkg_name = substring(input, size("status") );
	    if (OSRPkgVerify::PackageVerified (pkg_name))
		return pkg_name;
	}
	return "";
    }

    /**
     * 'translate' booleqan to string which can be shown in dialogs
     */
    define string boolean2string (boolean val) ``{

	// table entry (value in the column "Configuration file")
	// translate it either as True/False or Yes/No
	return val ? _("yes") : _("no");
    }

    /**
     * Update the list of files, which failed during package verification
     * returns contents of the richtext
     */
    define string update_files (string pkg_name, list<map> pkg_data) {

	string ret			= "";
	string tab_pkg_name_color	= contains (
	    OSRPkgVerify::not_successful_verified, pkg_name) ? "red" : "blue" ;
	string line_sf			= "<li>%1 %2 %3</li>%4";
	string align			= "";
	if (has_progress_widget)
	{
	    align	= "align=right";
	    line_sf	= "<tr><td  colspan=2 align=right> %1</td>
				<td>%2</td>
                                <td %4>%3</td>
			   </tr>";
	}

	foreach (map line, pkg_data, {

	    string color	= line["green_status"]:"" == ""? "red": "green";
	    string file_status	=
	        HTML::Bold (HTML::Colorize (line["status"]:"", color));
	    string file		= line["file"]:"";
	    string config_file	= boolean2string (line["config_file"]:false);

	    if (!has_progress_widget)
	    {
		file		= fill (file, 50, ".") + "..";
		config_file	= fill (config_file, 7, ".");
	    }

	    if (line["more"]:false)
	    {
		file		= HTML::Link (
		    // last entry of file list (cut because of length)
		    HTML::Colorize (_("more..."), tab_pkg_name_color),
		    pkg_name + "_more_packages"
		);
		config_file	= "";
		file_status	= "";
	    }

	    ret = ret + sformat(line_sf, file, config_file, file_status, align);
	});
	return ret;
    }

    /**
     * Add a new package entry to the internal list and the displayed text.
     * Update the process bar too.
     * Afterwards call repaint define.
     */
    define boolean update (string pkg_name) {

	if (UI::WidgetExists(`id("show_ok")))
	    show_ok = (boolean) UI::QueryWidget(`id("show_ok"), `Value );

	string tab_body			= "";
	string tab_pkg_status		= "";
	string tab_pkg_name_color	= contains (
	    OSRPkgVerify::not_successful_verified, pkg_name) ? "red" : "blue" ;
	string tab_pkg_name		= HTML::Link (HTML::Bold (
	    HTML::Colorize (pkg_name, tab_pkg_name_color)), pkg_name);

	boolean ok	= size (OSRPkgVerify::verify_data) == 0;
	if (ok)
	{
	    tab_pkg_status = HTML::Link (
		HTML::Bold (HTML::Colorize ("ok", "blue")),
		pkg_name2ok_key (pkg_name)
	    );
	}
	else
	{
	    if (tab_pkg_status == "" ) tab_pkg_status = HTML::Bold("...");
	    tab_body	= update_files (pkg_name, OSRPkgVerify::verify_data);
	}

	string line_sf = "<tr><th>%1</th>
                                    <th></th>
                                    <th></th>
                                    <th %4> %2</th> %3
                                </tr>";
	string align	= "align=right";

	if (!has_progress_widget)
	{
	    string n1	= HTML::Bold (_("Package:"));
	    string n2   = HTML::Bold (ok ? "": _("Files:")) + "<br>";
	    line_sf	=
		"<br><br>" + n1 +"<li><b>%1</b> <b>%2</b></li>" + n2 + "%3%4";
	    tab_pkg_name = tab_pkg_name + fill ("", 60 - size(pkg_name), ".");
	    align	= "";
	}

	string table	=
	    sformat (line_sf, tab_pkg_name, tab_pkg_status, tab_body, align);

	verified_text_ok = verified_text_ok + table;

	if (!ok)
	{
	    verified_text = verified_text + table;
	}
	return repaint();
    }

    /**
     * Show the complete ouptput of package verification
     * (in the summary, it is limited to OSRPkgVerify::max_files entries)
     */
    define symbol show_complete_verify_list (string pkg_name) {

	UI::OpenDialog (`HBox (
	    `HSpacing (3),
	    `VSpacing (OSRPkgVerify::max_files + 10),
	    `VBox (
		`HSpacing(80),
		`VSpacing (1),
		// label - %1 is package name; file list (=result) will follow
		`Label (sformat (_("Result of Package '%1' Verification"),
		    pkg_name)),
		`VSpacing (1),
		`RichText (`id (`files), `opt(`vstretch, `autoScrollDown), ""),
		`PushButton (`id(`ok), Label::OKButton())
	    ),
	    `HSpacing(3)
	));

	UI::BusyCursor ();

	OSRExecute::CommandOutput (.local.bash,
	    sformat("LANG=ENG /bin/rpm --root %2 -V %1", pkg_name,
	    OSRPkgVerify::root_for_verification));

	map out = OSRPkgVerify::convert_verify_output (
	    pkg_name, OSRExecute::stdout, true);

	string start	= "";
	string end	= "";
        if (has_progress_widget)
	{
	    start	= sformat (
		"<table><tr>
		    <th colspan=2 align=right>%1</th>
		    <th>%2</th>
		    <th align=right>%3</th>
		</tr>",
		_("Files "), _("Configuration File"), _("Status"));
	    end		= "</table>";
	}
	UI::ChangeWidget (`id(`files), `Value,
	    start + update_files (pkg_name, out["verify_data"]:[]) + end);

	UI::NormalCursor ();
	UI::UserInput ();
	UI::CloseDialog ();
	return `again;
    }

    /**
     * popup with the summary of the package
     */
    define symbol show_package_details (string pkg_name) {
	// popup headline
	map ret = OSRPopup::CheckBoxDialog (_("Package Details:"),
	    Pkg::PkgSummary (pkg_name),
	    [ [ pkg_name,
		// checkbox label
		_("Reinstall the Package"),
		contains (packages_to_reinstall, pkg_name)
	    ] ]
	);
	foreach (string pkg, boolean reinstall, (map<string,boolean>) ret , ``{

	    if (reinstall)
	    {
		packages_to_reinstall = union (packages_to_reinstall, [ pkg ]);
	    }
	    else
	    {
		packages_to_reinstall = filter (string name,
		    (list<string>) packages_to_reinstall, ``( name != pkg ));
	    }
	});

	return `next;
    }


    /**
     * Toggle the pause button between pause and continue.
     */
    define symbol toggle_pause_button() ``{
	pause = ! pause;

	if (UI::WidgetExists(`id(pause_key)))
	{
	    if (!pause)
	    {
		UI::ChangeWidget(`id(pause_key), `Label,  pause_label  );
		return `continue;
	    }
	    else
	    {
		UI::ChangeWidget(`id(pause_key), `Label,  continue_label  );
		return `pause;
	    }
	}
    }

    /**
     * Eval the user input.
     */
    global define symbol EvalUserInput (string input) ``{

	// if the user pressed the or or cancel button ask or abort
	if (input == ok_or_cancel_key )
	{
	    // if not all packages are verified ask the user if he
	    // want really cancel.
	    if (package_count > OSRPkgVerify::VerifiedSize () && !protocol_mode)
	    {
		// popup question
		if (!Popup::YesNo(_("Really cancel package verification?")))
		{
		    return `again;
		}

	    }
	    return `abort;
	}
	else if (input == pause_key )
	{
	    // if the user pressed the pause or continue button
	    // change the label of the button.
	    return toggle_pause_button();
	}
	else if (input == "show_ok")
	{
	    // if the dialog in the expert mode and the user
	    // clicked the checkbox "show_ok" change the
	    // shown verify text
	    show_ok = (boolean) UI::QueryWidget(`id("show_ok"), `Value );
	    repaint();
	}
	else if (OSRPkgVerify::PackageVerified (input))
	{
	    // if the dialog in the expert mode and the
	    // user clicked a package name open a dialog
	    // with the summary of the package
	    show_package_details (input);
	}
	else if (ok_key2pkg_name (input) != "" )
	{
	    // if the dialog in the expert mode and the
	    // user clicked a package status open a
	    // dialog with the following message.
	    Report::Message(sformat(_("
Verifying the packages %1 was successful.
"), ok_key2pkg_name(input)));
	}
	else if (input == mode_key )
	{
	    // change the dialog mode between the expert
	    // and the normal mode.
	    set_expert_mode ((boolean)UI::QueryWidget(`id(mode_key ), `Value));
	    if (pause)
	    {
		toggle_pause_button();
		return `continue;
	    }
	}
	else if (issubstring (input, "_more_packages"))
	{
	    string pkg_name = substring (input,0,find (input,"_more_packages"));
	    if (!OSRPkgVerify::PackageVerified (pkg_name))
	    {
		y2warning ("strange, package %1 not verified", pkg_name);
		return `again;
	    }
	    show_complete_verify_list (pkg_name);
	}
	else
	{
	    // open a extended dialog which describe the packages status
	    //S.5....T c  <- Config file
	    string pkg_name = regexpsub (input,
		sformat("(.*%1).*", OSRPkgVerify::separator),  "\\1");
	    pkg_name        = substring (pkg_name, 0,
		size(pkg_name) - size (OSRPkgVerify::separator));

	}
	return `again;
    }

    /**
     * update the item list of 'missing_packages' with the ones
     * selected to reinstall by user during verification
     */
    global define list<list> update_missing_packages (
	list<list> item_list, boolean check) {

	foreach (string item, (list<string>) packages_to_reinstall, {
	    if (!contains (OSRPkgVerify::not_successful_verified, item))
		item_list = add (item_list, [item, check]);
	});
	return item_list;
    }

    global define list ProtocolDialog()``{

	protocol_mode = true;

	UI::OpenDialog( get_verify_dialog()  );

	symbol ret  = `next;
	string rret = "";

	repaint();
	repeat
	{
	    rret = (string) UI::UserInput();
	    y2milestone(" user input in protocol dialogs : %1", rret);
	    ret  = EvalUserInput (rret);
	} until (ret == `abort || ret == `cancel);

	UI::CloseDialog();
	protocol_mode = false;

	list<list> item_list = [];
	foreach (string item, OSRPkgVerify::not_successful_verified , {
	    boolean checked = contains (packages_to_reinstall, item);
	    item_list = add (item_list, [item, checked]);
	});
	return update_missing_packages (item_list, true);
    }

    global define boolean AddPackageSummary (string pkg_name) {

	boolean ret = update (pkg_name);

	if (UI::WidgetExists(`id( package_progress_bar )))
	{
	    OSRProgress::SetLabel (package_progress_bar, check_string());
	}
	return ret;
    }

    /**
     * Finish the dialog.
     */
    global define void Finish()``{
	UI::ChangeWidget(`id(ok_or_cancel_key), `Label, Label::OKButton());

	if (UI::WidgetExists(`id(pause_key ) ))
	{
	    UI::ChangeWidget(`id(pause_key),	`Enabled, false);
	}
    }


}//EOF

