/**
 * File:	Packages.ycp
 * Package:	Package selections
 * Authors:	Anas Nashif <nashif@suse.de>
 *
 * $Id: Packages.ycp 22481 2005-03-12 03:43:57Z nashif $
 */

{
    module "Packages";

    textdomain "packager";

    import "Arch";
    import "Directory";
    import "InstURL";
    import "SourceManager";
    import "Kernel";
    import "Mode";
    import "Stage";
    import "Linuxrc";
    import "Language";
    import "ProductFeatures";
    import "ProductControl";
    import "Report";
    import "SlideShow";
    import "SpaceCalculation";
    import "Storage";
    import "String";
    import "Popup";
    import "Label";
    import "Wizard";

    global boolean install_sources = false;	// Installing source packages ?
    global integer timestamp = 0;		// last time of getting the target map

    global string metadir = "/yast-install";
    global boolean metadir_used = false;	// true if meta data and inst-sys is in ramdisk

    global list<integer> theSources = [];		// id codes of sources in priority order
    global list<string> theSourceDirectories = [];	// product directories on sources
    global map<integer,integer> theSourceOrder = $[];	// installation order
    global boolean desktop_dialog_shown = false;	// flag, that the Desktop software selection frame has been shownw
    string servicepack_metadata = "/servicepack.tar.gz";

    global string desktop_help_text =
// help text
_("<p>SUSE Linux offers a selection of efficient and comfortable desktops.
Select which desktop to install.</p>
");

    // to remember if warning should occurre if switching base selection
    global boolean base_selection_modified = false;

    // Local variables

    boolean init_called = false;

    string choosen_base_selection = "";

    string pm_init_blocker = nil;



    /*-----------------------------------------------------------------------
     * LOCALE FUNCTIONS
     *-----------------------------------------------------------------------*/

    /**
     * Compute architecture packages
     * @return list(string)
     */
    define list<string> architecturePackages ()
    {
	list<string> packages = [];

	// remove unneeded / add needed packages for ppc
	if (Arch::ppc ())
	{
	    if (Arch::board_iseries ())
	    {
		packages = add (packages, "ibmsis");
		packages = add (packages, "scsi");
		packages = add (packages, "iprutils");
	    }

	    if (Arch::board_mac ())
	    {
		packages = add (packages, "mouseemu");
	    }

	    if (Arch::board_mac_new ()
		|| Arch::board_mac_old ())
	    {
		string pmac_board = "";
		list<map> pmac_compatible = (list<map>) SCR::Read(.probe.cpu);
		foreach (map pmac_compatible_tmp, pmac_compatible, {
		    pmac_board = pmac_compatible_tmp["system"]:"";
		});

		// install pbbuttonsd on PowerBooks and iMacs
		if (issubstring (pmac_board, "PowerBook")
		    || issubstring (pmac_board, "PowerMac2,1")
		    || issubstring (pmac_board, "PowerMac2,2")
		    || issubstring (pmac_board, "PowerMac4,1")
		    || issubstring (pmac_board, "iMac,1"))
		{
		    packages = add (packages, "pbbuttonsd");
		    packages = add (packages, "powerprefs");
		}

		// mol is fun, and needs root access to kmem
		packages = add (packages, "mol");
		packages = add (packages, "sudo");
	    }

	    if (Arch::ppc64 () && Arch::board_chrp ())
	    {
		packages = add (packages, "ppc64-utils");
		packages = add (packages, "iprutils");
	    }
	}

	if (Arch::ia64 ())
	{
	    // install fpswa if the firmware has an older version
	    if (SCR::Execute(.target.bash, "/sbin/fpswa_check_version") != 0)
	    {
		packages = add (packages, "fpswa");
	    }
	}

	// add numactl on x86_64 with SMP
	if (Arch::has_smp () && Arch::x86_64 ())
	{
	    packages = add (packages, "numactl");
	}

	return packages;
    }


    /**
     * graphicPackages ()
     * Compute graphic (x11) packages
     * @return list(string)	list of rpm packages needed
     */
    define list<string> graphicPackages ()
    {
	list<string> packages = [];

	// don't setup graphics if running via serial console
	if (!Linuxrc::serial_console ())
	{
	    import "Display";
	    packages = (list<string>) Display::pacs_to_install;
	}

	y2milestone ("X11 Packages to install: %1", packages);
	return packages;
    }


    /**
     * Compute special packages
     * @return list(string)
     */
    define list<string> modePackages ()
    {
	list<string> packages = [];

	if (Linuxrc::vnc ())
	{
	    packages = add (packages, "tightvnc");
	    packages = add (packages, "yast2-qt");
	    packages = add (packages, "xorg-x11");
	}

	if (Linuxrc::braille ())
	{
	    packages = add (packages, "sbl");
	}

	return packages;
    }


    /**
     * Compute special java packages
     * @return list(string)
     */
    define list<string> javaPackages ()
    {
	if (!Arch::alpha ())
	    return [];

	list<string> packages = [];

	list cpus = (list) SCR::Read (.probe.cpu);
	string model = cpus[0, "model"]:"EV4";
	string cputype = substring (model, 2, 1);

	if ((cputype == "6") || (cputype == "7") || (cputype == "8"))
	{
	    packages = ["cpml_ev6"];
	}
	else
	{
	    packages = ["cpml_ev5"];
	}
	return packages;
    }


    /**
     * Compute language dependant packages
     * @return list(string)
     */
    define list<string> languagePackages ()
    {
	list<string> packages = [];
	list<string> locales = Pkg::GetAdditionalLocales();
	locales = prepend (locales, Pkg::GetLocale());
	string transpac = "yast2-trans-";

	foreach (string loc, locales, {
	    if (Pkg::IsAvailable (transpac + loc))
		packages = add (packages, transpac + loc);
	    else if (Pkg::IsAvailable (transpac + substring (loc, 0, 2)))
		packages = add (packages, transpac + substring (loc, 0, 2));
	    else
		y2warning ("No locale package found for %1", loc);
	});
	y2milestone ("Language packages: %1", packages);
	return packages;
    }


    /**
     * Compute board (vendor) dependant packages
     * @return list(string)
     */
    define list<string> boardPackages ()
    {
	list<string> packages = [];

	list <map <string, any> > probe = (list <map <string, any> >)SCR::Read (.probe.system);
	packages = (list<string>)probe[0,"requires"]:[];
	y2milestone ("Board/Vendor specific packages: %1", packages);

	return packages;
    }


    /*
     * Additional kernel packages from control file
     * @return list<string> Additional Kernel packages
     */
    define list<string> ComputeAdditionalKernelPackages ()
    {
	string final_kernel = Kernel::GetFinalKernel ();
        integer pos = findfirstof(final_kernel, "-");
        string extension = substring(final_kernel, pos, size(final_kernel));
        list<string> akp = [];
        if (extension!="")
        {
            if (size(ProductFeatures::kernel_packages) > 0 && 
                    ProductFeatures::kernel_packages != nil)
            {
                akp = maplist(string p , ProductFeatures::kernel_packages, ``{
                        return (p + "-" + extension);
                        });
            }
        }
        return akp;
    }

    /*-----------------------------------------------------------------------
     * GLOBAL FUNCTIONS
     *-----------------------------------------------------------------------*/


    global define list<string> ComputeSystemSelectionList ()
    {
	list<string> selection_list = [];
        // also add the 'laptop' selection if PCMCIA detected
        if (Arch::has_pcmcia ())
        {
            selection_list  =  Pkg::GetSelections( `available, "laptop");
        }

        if (size(ProductFeatures::addon_selections) > 0 &&
                ProductFeatures::addon_selections!=nil)
        {
            selection_list=(list<string>)union(selection_list, 
                    ProductFeatures::addon_selections );
        }
        return selection_list;
    }

    /**
     * Build and return list of packages which depends on the
     * the current target system and the preselected packages
     * (architecture, X11....)
     * @return list<string> packages
     */
    global define list<string> ComputeSystemPackageList ()
    {
	list<string> install_list =  architecturePackages ();

	install_list = (list<string>) union (install_list, modePackages ());
	install_list = (list<string>) union (install_list,
	                                     Storage::AddPackageList());

	// Kernel is added in autoinstPackages () if autoinst is enabled
	if (!Mode::update () || !Mode::autoinst ())
	{
            list <string> kernel_pkgs = Kernel::ComputePackages ();
            list <string> kernel_pkgs_additional = ComputeAdditionalKernelPackages();
	    install_list = (list <string>) union (install_list, kernel_pkgs);
            if (size(kernel_pkgs_additional) > 0 && kernel_pkgs_additional != nil)
            {
	        install_list = (list <string>) union (install_list, kernel_pkgs_additional);
            }
        }

	if (Pkg::IsSelected("xorg-x11"))
	{
	    install_list = (list<string>) union (install_list, graphicPackages ());
	}

	if (Pkg::IsSelected("java"))
	{
	    install_list = (list<string>) union (install_list, javaPackages ());
	}

	install_list = (list<string>) union (install_list, languagePackages ());

	install_list = (list<string>) union (install_list, boardPackages ());

	// and the most flexible enhancement for other products
        // NOTE: not really flexible, because it requires the client
        // in the instsys, instead use <kernel-packages> in the control file.
	if (ProductFeatures::packages_transmogrify != "")
	{
            list<string> tmp_list = (list<string>)
		WFM::CallFunction (ProductFeatures::packages_transmogrify,
				   [ install_list ]);

            // Make sure we did not get a nil from calling the client, i.e.
            // if the client does not exist at all..
            if (tmp_list != nil)
            {
                install_list = tmp_list;
            }
	}

        if (size(ProductFeatures::packages) > 0 && ProductFeatures::packages != nil )
        {
            y2milestone("Adding packages from control file: %1", ProductFeatures::packages );
            install_list = (list<string>) union (install_list, ProductFeatures::packages );
        }

	install_list = toset (install_list);
	y2milestone ("auto-adding packages: %1", install_list);
	return install_list;
    }

    /**
     * SetBaseSelection (string base name)
     * @param string base selection name
     * @return boolean
     */
    global define boolean SetBaseSelection (string basename)
    {
	list base_selections = Pkg::GetSelections (`selected, "base");
	string current_base_selection = base_selections[0]:"";
	if (current_base_selection != "")
	{
	    Pkg::ClearSelection (current_base_selection);
	}
	Pkg::SetSelection (basename);
	Pkg::ActivateSelections ();
	Pkg::PkgSolve(false);
    }

    /**
     * Init()
     * Initialize package module and package agent
     * @return integer: 0 == ok, 1 == retry, -1 == error
     *
     */
    global define void Init( boolean forceInit )
    {
	if (init_called)
	{
	    y2warning ("Packages::Init() already called");
	    return;
	}

	//
	// Initialize package manager
	//
	pm_init_blocker = nil;
	y2milestone ("Packages::Init(%1)", forceInit);
	// usual mountpoint for the source medium
	string base_url = "";

	if ( Mode::test () )
	{
	    // Fake values for testing purposes
	    base_url = "dir:///dist/next-i386";
	}
	else
	{
	    base_url = InstURL::installInf2Url ("");
	}

	// check if url contains "@" -> might be username/password, hide from logging
	string log_url = InstURL::HidePassword(base_url);
	y2milestone ("Initialize Package Manager: %1", log_url);

	// Set languages for packagemanager. Always set the UI language. Set
	// language for additional packages only in Stage::initial ().

	Pkg::SetLocale (Language::language);

	if (Stage::initial ())
	{
	    Pkg::SetAdditionalLocales ([Language::language]);
	    Pkg::SourceStartCache (true);			// dummy, so further calls to Pkg::Pkg* don't restart it

	    Pkg::SourceSetRamCache (false);
	    integer initial_source = Pkg::SourceCreate (base_url, "");
	    if (initial_source == nil)
	    {
		y2error ("No source on '%1'", log_url);
		// error in proposal, %1 is URL
		pm_init_blocker = sformat (_("No source found at '%1'."),
		    log_url);
		return;
	    }

            /* Check for Service Pack */
            boolean servicepack_available = false;
            if ((integer)WFM::Read(.local.size, servicepack_metadata) > 0)
            {
                y2milestone("Service Pack data available");
                servicepack_available = true;
            }

            // check for yast/order
	    string orderfile = Pkg::SourceProvideFile (initial_source, 1, "yast/order");
	    list<list<string> > orderlist = [];
	    map<integer, string > instordermap = $[];	// instorder : product dir, ...

	    if (orderfile != nil)			// splitted source installation
	    {
		y2milestone ("orderfile '%1'", orderfile);
		orderlist = SourceManager::ParseOrderFile (orderfile);
		if (orderlist == nil)
		{
		    y2error ("Can't read '%1'", orderfile);
		    // error in proposal, %1 is filename
		    pm_init_blocker = sformat (
			// error in the proposal
			_("Cannot read '%1' from the installation media."),
			orderfile);
		    return;
		}

		string instorderfile = Pkg::SourceProvideFile (initial_source, 1, "yast/instorder");
		if (instorderfile != nil)
                {
		    instordermap = SourceManager::ParseInstOrderFile (instorderfile);
                }
            }
            else if (servicepack_available)
            {
                /**
                 * No order file, but service pack is available, so we hack
                 * our own order/instorder files
                 */
	        orderlist = [["/"]];
                instordermap[1] = "/";
            }

            if (size(orderlist) > 0 )
            {
		// setup slidedir
                // FIXME: Is that needed here?
		map productmap = Pkg::SourceProductData (initial_source);
		string datadir = productmap["datadir"]:"suse";
		string slidedir = metadir + "/" + datadir + "/setup/slide";
                WFM::Execute (.local.mkdir, slidedir);
		SlideShow::SetSlideDir (slidedir);

		// setup meta data dir

		WFM::Execute (.local.mkdir, metadir);
		boolean all_sources_ok = true;
                // copy all data to ramdisk
		list<list<string> > new_orderlist = filter (list<string> sourcelist, orderlist, {		
                        // just in case more spaces are used as delimiter,
                        // remove all remaining spaces from the list
                        sourcelist = filter(string src, sourcelist,
                            ``(src!=""));

			string descDir = sourcelist[0]:"";
			string pkgdir = sourcelist[1]:"/";
                        boolean status = SourceManager::CreateLocalMetaData(initial_source, 
                            metadir, 
                            slidedir,
                            descDir , 
                            pkgdir );
                        return status;
		});
                if (new_orderlist!= orderlist)
                {
                    all_sources_ok = false;
                }
                orderlist = new_orderlist;

		// ok, all data copied from the install media, release and delete it

		Pkg::SourceDelete (initial_source);
		Pkg::SourceSetRamCache (true);

		metadir_used = true;

		// cancel initialization if some of the source is missing
		if (! all_sources_ok)
		{
		    y2error ("Unable to initialize all installation sources");
		    pm_init_blocker
			// error in proposal
			= _("Unable to initialize all installation sources.");
		    return;
		}

                if (servicepack_available)
                {
                    string spdir = metadir + "/Service-Pack/CD1";
                    WFM::Execute (.local.mkdir, spdir);
                    y2milestone ("Filling %1", spdir);
                    WFM::Execute(.local.bash, "tar -zxvf " +
                            servicepack_metadata + " -C " + spdir);

                    
                    string sp_url = "dir://"+metadir+"/Service-Pack/CD1";
                    orderlist = add(orderlist, ["/", sp_url]);
                    instordermap =  add (instordermap, size(instordermap) + 1, sp_url);

                }

		// now restart the whole stuff from the metadata in the ramdisk and create
		// the *real* sources

                y2milestone("orderlist: %1", orderlist );
		foreach (list<string> sourcelist, orderlist, {
			string descrdir = sourcelist[0]:"";
			string pkgdir = sourcelist[1]:"";

			string meta_url = "dir://"+metadir+"/" + descrdir;
			integer srcid = Pkg::SourceCreate (meta_url, "");		// create and enable source
			y2milestone ("srcid %1, temp_url '%2'", srcid, meta_url);
			if (srcid != nil)
			{
			    theSources = add (theSources, srcid);
			    theSourceDirectories = add (theSourceDirectories, descrdir);
                            // if second column of yast/order contains ":"
			    if (find (pkgdir, ":") > 0)					
			    {
				base_url = pkgdir;	// then take as URL
			    }
			    else
			    {
				base_url = InstURL::installInf2Url (pkgdir);		// else take as sub-dir
			    }
			    Pkg::SourceChangeUrl (srcid, base_url);		// rewrite URL for *real* source

			    // now find this source in the instorder map and fill
			    // the global 'theSourceOrder'
			    foreach (integer key, string value, instordermap, {
				if (value == descrdir)
				{
				    theSourceOrder[key] = srcid;
				}
			    });
			}
		});
		y2milestone ("theSourceOrder %1", theSourceOrder);
		Pkg::SourceInstallOrder (theSourceOrder);
	    }
	    else		// normal media installation
	    {
		y2milestone ("No order file, doing single source installation");
		theSources = [ initial_source ];
	    }

	    if (ProductFeatures::base_selection_choice == `fixed)
	    {
                Pkg::SetSelection (ProductFeatures::fixed_base_selection);
	    }
	}
	else	// cont or normal mode
	{
	    theSources = Pkg::SourceStartCache (true);
	    if (theSources == nil || size (theSources) <= 0)
	    {
		y2error ("Pkg::SourceStartCache failed");
		theSources = [];
	    }
	    else if ( Stage::cont ()    // rewrite URL if cd/dvd since ide-scsi might have changed it
		     && ((substring (base_url, 0, 2) == "cd")
			|| (substring (base_url, 0, 3) == "dvd")))
	    {
		foreach (integer source, theSources, {
		    map data = Pkg::SourceGeneralData (source);		// get source data
		    string url = data["url"]:"";
		    if ((substring (url, 0, 2) == "cd")			// source comes from cd/dvd
			|| (substring (url, 0, 3) == "dvd"))
		    {
                        string new_url = InstURL::RewriteCDUrl(url);
			y2milestone ("rewrite url: '%1'->'%2'", url, InstURL::HidePassword(new_url));
			Pkg::SourceChangeUrl (source, new_url);
		    }
		});
	    }
	}

	y2milestone ("theSources %1", theSources);
	y2milestone ("theSourceDirectories %1", theSourceDirectories);
	if (size (theSources) >= 0)
	{
	    init_called = true;
	}
	else
	{
	    import "PackageCallbacks";

	    // an error message
	    string errortext = sformat (_("Error while initializing package descriptions.
Check the log file %1 for more details."), Directory::logdir + "/y2log") +
		"\n" + Pkg::LastError();

	    // FIXME somewhere get correct current_label and wanted_label
	    string result = PackageCallbacks::MediaChange (errortext, base_url, "",
							   0, "", 1, "", false);
	}
    }


    /**
     * Warn about suboptimal distribution
     */
    define void add_information_about_suboptimal_distribution (list& output)
    {
	// this depends on the kernel
	if (Arch::i386 ())
	{
	    string tmp = (string) SCR::Read(.proc.cpuinfo.value."0"."flags");
	    list flags = (size (tmp) > 0) ? splitstring (tmp, " ") : [];

	    // this depends on the cpu (lm = long mode)
	    if (contains (flags, "lm"))
	    {
		// warning text, part 1
		output = add (output, _("Your computer is a 64-bit x86-64 system.") + " " +
		// warning text, part 2
			      _("However, you are trying to install a 32-bit distribution."));
	    }
	}
    }


    define void SetSelectionAndComputeSystemPackages (string selection)
    {
        // set the new selection
        boolean ret = Pkg::SetSelection (selection);

        y2milestone ( "Selecting '%1' returns: %2", selection, ret );

        ret = Pkg::ActivateSelections();
        y2milestone ("Pkg::ActivateSelections() returns %1", ret);

        // add additional (internal) packages, like kernel etc.
        Pkg::DoProvide (Packages::ComputeSystemPackageList());
        Pkg::PkgSolve(false);

        list selected_selections = Pkg::GetSelections( `selected, "base");
        selected_selections = union ( selected_selections, Pkg::GetSelections( `selected, "") );
        y2milestone( "INSTALLING software selection: %1", selected_selections );
    }

define boolean SelectDesktop () {
    // get all available base selections
    // checked in help below !
    list<string> available_desktop_selections = Pkg::GetSelections( `available, "base");
    y2milestone ("available_desktop_selections %1", available_desktop_selections);

    if (!contains (available_desktop_selections, "Gnome"))
    {
	y2error( "No desktop selections available for Gnome" );
	return false;
    }
    if (!contains (available_desktop_selections, "Kde"))
    {
	y2error( "No desktop selections available Kde" );
	return false;
    }

    // get the currently selected base selection
    list current_desktop_selections = Pkg::GetSelections( `selected, "base");
    y2milestone ("current_desktop_selections %1", current_desktop_selections);

    // evaluate descriptions
    map<string, string> selection_description = $[];
    foreach( string selection, available_desktop_selections, {
	map selection_data = Pkg::SelectionData (selection);
	if (selection_data != nil)
	{
	    selection_description[selection] = selection_data["summary"]:("'"+selection+"'");
	}
    });

    // Construct a box with radiobuttons for each software base configuration
    term selection_box = `RadioButtonGroup(`id(`selection_box),
					    `VBox(
						  `Left(`RadioButton(`id("Gnome"), `opt(`notify), selection_description["Gnome"]:"not defined",
							contains (current_desktop_selections,"Gnome"))),
						  `VSpacing(1),
						  `Left(`RadioButton(`id("Kde"), `opt(`notify), selection_description["Kde"]:"not defined",
								     contains (current_desktop_selections,"Kde")))
						  )
					    );


    term ask_desktop_dialog = `HBox(
                                    `HStretch(),
				    `VBox (
                                        `VWeight(30,`VStretch()),
					// translators: dialog text
					`Left(`Label (_("The &product; includes two different
desktop environments.
Select the environment to use by default
from the list below. If you do not
know which environment is preferred by your company,
ask your system administrator.
"))),
					`VSpacing(1),
                                        `VWeight(10,`VStretch()),
					`HBox(
					      `HSpacing(10),
					       selection_box
                                         ),
                                       `VWeight(60,`VStretch())
                                       ),
                                   `HStretch()
				    );


    //Wizard::OpenAcceptDialog();
    Wizard::OpenAcceptStepsDialog();
    string wizard_mode = Mode::test () ? "installation" : Mode::mode ();

    // UI::WizardCommand() can safely be called even if the respective UI
    // doesn't support the wizard widget, but for optimization it makes sense
    // to do expensive operations only when it is available.
    list<map> stage_mode = [$["stage": "initial" , "mode": wizard_mode ],
                            $["stage": "continue", "mode": wizard_mode ],
                            $["stage": "normal", "mode": wizard_mode ]];
    ProductControl::AddWizardSteps(stage_mode);
    UI::WizardCommand(`SetCurrentStep( ProductControl::CurrentWizardStep ));


    // translators: dialog title
    Wizard::SetContents ( _("Select Your Default Desktop"), ask_desktop_dialog,
            desktop_help_text, false, true);
    UI::ChangeWidget(`id(`accept), `Enabled, false );
    UI::SetFocus(`id(`abort));

    any ret = nil;
    string desktop_selection = "";
    repeat
    {
        ret = UI::UserInput();
        // get the newly selected base configuration
        desktop_selection = (string) UI::QueryWidget( `id(`selection_box),`CurrentButton );

        if (ret == `cancel)
        {
            if (Popup::ConfirmAbort (`painless))
            {
                Pkg::RestoreState(false);
                Wizard::CloseDialog();
                return false;
            }
            else
            {
                ret = `again;
            }
        }

        if (ret != `cancel && ret != `accept && ret != `again)
	{
	    UI::ChangeWidget(`id(`accept), `Enabled, true );
	}
    } until (ret == `cancel || ret == `accept);

    if (ret == `accept)
    {
	y2milestone ("Setting selection to %1", desktop_selection);
	SetSelectionAndComputeSystemPackages (desktop_selection);
    }
    // Flag that this screen has been finished successfully
    Wizard::CloseDialog();
    desktop_dialog_shown = true;
    return true;
}


    global string CountSizeToBeInstalled () {
        integer sz = 0;
	if (! Mode::installation ())
	{
	    list<string> packages = Pkg::GetPackages (`selected, true);
	    foreach (string p, packages, {
		sz = sz + Pkg::PkgSize (p);
	    });
	}
	else // in case of fresh installation, disk is initially empty
	{
	    map<string,list> du = Pkg::TargetGetDU ();
	    foreach (string mp, list usages, du, {
		sz = sz + usages[2]:0 - usages[1]:0;
	    });
	}
	y2milestone ("Total size of packages to install: %1", sz);
        return String::FormatSizeWithPrecision (sz, 1, true);
    }


    /**
     * Make a proposal for package selection
     * @param force reset
     * @param re-initialize
     * @return map Map for the API proposal
     */
    global define map Proposal( boolean force_reset, boolean reinit )
    {

	map ret = $[];

	// popup label
	UI::OpenDialog(`opt(`decorated ),`Label(_("Evaluating package selection...")));

	y2milestone ("Packages::Proposal: force_reset %1, reinit %2, lang '%3'",
		     force_reset, reinit, Language::language);

	if ( force_reset )
	{
	    Kernel::ProbeKernel();
	    Pkg::PkgReset ();
	    Packages::Init( true );	// reintialize AND reset package selection too
	}
	else if ( reinit )
	{
	    Packages::Init( false );	// reinitialize but keep former selection (if possible)
	}
	else
	{
	    Packages::Init( !init_called ); // reset completely only if necessary
	}

	UI::CloseDialog();

	if (pm_init_blocker != nil)
	{
	    return $[
		"warning" : pm_init_blocker,
		"warning_level" : `blocker,
	    ];
	}

	// start with base selections

	y2milestone ("checking selected selection");

	list base_selections = Pkg::GetSelections (`selected, "base");
	string selected_base_selection = base_selections[0]:"";

	list<string> selected_addon_selections = Pkg::GetSelections (`selected, "");
	y2milestone ("selected base is %1, addon is %2", selected_base_selection, selected_addon_selections);

	// when coming from the update branch to the install branch, we might already have
	// a choosen_base_selection but no selection in the package manager
	// this will trigger the redo_proposal below

	if (selected_base_selection == "")
	    choosen_base_selection = "";

	// re-set space calculation
	y2milestone ("Resetting space calculation");
	SpaceCalculation::GetPartitionInfo();
	boolean disk_space_sufficient = SpaceCalculation::CheckDiskSize();
	y2milestone ("Disk space sufficient: %1", disk_space_sufficient);

	// remember if we must re-do the proposal.
	// If not, there's no need to activate any selections !
	boolean redo_proposal = (choosen_base_selection == "") && !Mode::autoinst ();

	if (ProductFeatures::base_selection_choice == `fixed)
	{
	    if (! disk_space_sufficient)
	    {
		// warning in the summary
		ret = add( ret, "warning", _("Disk Space Exhausted") );
		ret = add( ret, "warning_level", `blocker );
	    }
	}
	else if (ProductFeatures::base_selection_choice == `auto)
	{
	    if (redo_proposal || ! disk_space_sufficient)
	    {
		// make a new proposal or change a proposal,
		// if it does not fit on the disk anymore

		if ( ! redo_proposal )
		{
		    // the selected set of software packages does not fit on the harddisk
		    // error message
		    ret = add( ret, "warning", _("Disk space exhausted -- changing software selection") );
		    ret = add( ret, "warning_level", `error );
		    redo_proposal = true; //
		}

		// get all base selections, and sort them by reverse order (highest ordered first)
		list available_base_selections = sort (string x, string y, Pkg::GetSelections (`available, "base"),
						       ``{
							   map xmap = Pkg::SelectionData(x);
							   map ymap = Pkg::SelectionData(y);
							   return (xmap["order"]:"" > ymap["order"]:"");
						       }
						       );
		y2milestone ("available_base_selections %1", available_base_selections);

		if (size (available_base_selections) == 0)
		{
		    y2error ("No base selections available");
		    // Can't find any software data, probably a installation media error
		    // error message
		    return $[ "warning" : _("Cannot read package data from installation media. Media error?"),
			      "warning_level" : `blocker ];
		}

		boolean fit = false;
		integer counter = 0;

		if (ProductFeatures::software_proposal == "desktop")
		{
		    if (!desktop_dialog_shown)
		    {
			if (!SelectDesktop ())
                        {
			    ret = add( ret, "warning_level", `fatal);
			}
			base_selections = Pkg::GetSelections (`selected, "base");

			y2milestone ("Desktop base selection: %1", base_selections);
			selected_base_selection = base_selections[0]:"";

			SetBaseSelection (selected_base_selection);
			if (SpaceCalculation::CheckDiskSize())
			{
			    fit = true;
			}
			else
			{
			    // error popup
			    Popup::Error(_("Not enough disk space.  Remove some packages in the single selection."));
			}
		    }
		}

		while (!fit
		       && counter < size(available_base_selections) )
		{
		    selected_base_selection = available_base_selections[counter]:"";

		    y2milestone ("Checking space for '%1'", selected_base_selection);
		    SetBaseSelection (selected_base_selection);

		    if ( !SpaceCalculation::CheckDiskSize() )
		    {
			// does not fit --> trying next
			counter = counter +1;
		    }
		    else
		    {
			fit = true;
		    }
		}
		if (!fit)
		{
		    y2warning ("No selection fits");
		    Pkg::ClearSelection (selected_base_selection);
		    // can't find a set of software packages which fits on the harddisk
		    // error message
		    ret = add (ret, "warning" , _("Not enough disk space even for a minimal installation!"));
		    ret = add (ret, "warning_level" , `blocker);
		    ret = add (ret, "raw_proposal", []);
		    return ret;
		}

		y2warning ("Get recommendations for %1", selected_base_selection);

		map base_data = Pkg::SelectionData (selected_base_selection);
		if (base_data == nil)
		{
		    base_data = $[];
		}
		selected_addon_selections = base_data["recommends"]:[];

		// also add the 'laptop' selection if PCMCIA detected
		if (Arch::has_pcmcia ())
		{
		    selected_addon_selections = (list<string>)union (selected_addon_selections, Pkg::GetSelections( `available, "laptop"));
                }
                if (size(ProductFeatures::addon_selections) > 0 &&
                        ProductFeatures::addon_selections!=nil)
                {
                    selected_addon_selections=(list<string>)union(selected_addon_selections, 
                            ProductFeatures::addon_selections );
                }
	    }
	}
	else
	{
	    y2error ("unknown value %1 for ProductFeatures::base_selection_choice",
		     ProductFeatures::base_selection_choice);
	}

	choosen_base_selection = selected_base_selection;

	// generate user readable output for proposal
	list output = [];

        string dp = (string) SCR::Read(.content.DISTPRODUCT);
        if (dp==nil)
            dp = "";

	if (ProductFeatures::inform_about_suboptimal_distribution &&
                Arch::i386 () && issubstring(dp, "DVD"))
	    add_information_about_suboptimal_distribution (output);

	map base_data = Pkg::SelectionData (selected_base_selection);
	if (base_data == nil)
	{
	    y2error ("No data for selection '%1'", selected_base_selection);
	    base_data = $[];
	}
	y2milestone ("Base %1: %2", selected_base_selection, base_data);
	output = add (output, base_data["summary"]:"?");

	// add on
	foreach(string sel, selected_addon_selections, {
	    map addon_data = Pkg::SelectionData (sel);
	    if ((addon_data != nil)				// skip unknown selections
		&& (addon_data["category"]:"" != "base")
		&& addon_data["visible"]:false)
	    {
		if ( Pkg::SetSelection( sel ) )
		    output = add ( output, "+  " + addon_data["summary"]:sel);
		else
		    y2error( "Couldn't add selection %1", sel );
	    }
	});

	y2milestone ("proposal %1", output);

	// part of summary, %1 is size of packages (in MB or GB)
	output = add (output, sformat (_("Size of Packages to Install: %1"),
	    CountSizeToBeInstalled ()));

	// check only disk space, if the selected software selection
	// fits on the disk
	if ( !SpaceCalculation::CheckDiskSize() )
	{
//	    ret = $[ "warning" : _("Software selection does not fit on disk"),
//		   warning_level : `blocker ];
	    y2warning ("Software selection does not fit on disk");
	}

	ret = add ( ret, "raw_proposal", output );

	// we just proposed a new base selection, activate it !

	if (redo_proposal)
	{
	    y2milestone ("activating selections");
	    boolean act_sel = Pkg::ActivateSelections ();

	    // add additional (internal) packages, like kernel etc.
	    map <string, any> res = Pkg::DoProvide (ComputeSystemPackageList());
	    if (size (res) > 0)
	    {
		foreach (string s, any a, res, {
		    y2warning ("Pkg::DoProvide failed for %1: %2", s, a);
		});
	    }

	    Pkg::PkgSolve (false);

	    y2milestone ("activating: %1, returning %2", act_sel, ret);
	}
	return ret;
    }

/* EOF */
}
