/**
 * Module: 		inst_target_selection.ycp
 *
 * Authors: 		Klaus Kaempf (kkaempf@suse.de)
 *
 * Purpose: 		This module selects the harddisk(s) for installation.
 *			-Harddisk recognition
 *			-Selecting the harddisk for the installation by
 *			 the user ( if possible harddisks > 1 ).
 *			-Write selected harddisk(s) with SetPartDisk into 
 *                       Storage
 *                      "target_is":<devicename> (key to "targets" map)
 *			if custom, set "target_is":"CUSTOM"
 *
 * $Id: inst_target_selection.ycp 22053 2005-03-01 10:36:31Z fehr $
 */
{
    textdomain "storage";

    import "Arch";
    import "Mode";
    import "Stage";
    import "Popup";
    import "Partitions";
    import "Wizard";
    import "Storage";

    boolean test_mode	= Mode::test (); 
    boolean demo_mode	= Mode::test ();

    y2debug( "mode: %1", test_mode );

    //////////////////////////////////////////////////////
    // look what inst_part_propose has said:

    y2milestone( "GetPartMode %1", Storage::GetPartMode() );

    if( Storage::GetPartMode() == "PROP_MODIFY" ||
        Storage::GetPartMode() == "SUGGESTION" )
        return Storage::GetExitKey();


    //////////////////////////////////////////////////////
    // if we get here in update mode it's a BUG

    if( Mode::update () ) {
	return `cancel;
    }

    /*
     *----------------------------------------------------------------------
     * CheckPartitionTableSanity
     *
     * - if an extended partition exists:
     *   check, if there are n logical drives, whether they start from xxx5
     *   up to xxx5 + xxx(n-1)
     *----------------------------------------------------------------------
     *
     */
    
define boolean CheckPartitionTableSanity( map<string,map> targetMap )
    ``{
    boolean     logical_drive_is_missing = false;
    boolean     too_many_primary         = false;
    boolean     logical_without_extended = false;
    string      ldim_dev                 = "";
    string      err_dev                  = "";
    
    foreach(string dev, map disk, targetMap,
	``{
	string type = substring(dev, 5, 2);
	
	boolean extended_exists      = false;
	
	list<map> primary_list = 
	    filter( map part, disk["partitions"]:[],
		    ``( (part["type"]:`unknown == `primary) && 
		        !part["delete"]:false ));
	list<map> extended_list = 
	    filter( map part, disk["partitions"]:[],
		    ``( (part["type"]:`unknown == `extended) && 
		        !part["delete"]:false ));
	list<map> logical_list = 
	    filter( map part, disk["partitions"]:[],
		    ``( (part["type"]:`unknown == `logical) && 
		        !part["delete"]:false ));

	integer maximal_primaries = disk["max_primary"]:4;
	if( (size(extended_list) + size(primary_list)) > maximal_primaries )
	    {
	    too_many_primary = true;
	    err_dev          = dev;
	    y2error("Error Sanity: Extended List %1", extended_list ); 
	    y2error("Error Sanity: Primary List %1",  primary_list ); 
	    }

	if( (size(extended_list) == 0)  && (size(logical_list) != 0) )
	    {
	    logical_without_extended = true;
	    err_dev                  = dev;
	    y2error("Error Sanity: Extended List %1", extended_list ); 
	    y2error("Error Sanity: Primary List %1",  primary_list ); 
	    }

	// this will only trigger for FAT partitions
	if( size(extended_list) > 0 )
	    {
	    integer nb_logical_drives  = size( logical_list );
	    list lst_logical_drives = 
		maplist( map part, logical_list, ``(part["nr"]:0));

	    integer n = 5;

	    // example:
	    // - hda5 an hda6 ->  nb_logical_drives = 2
	    // - while( n <= 6 )  # 4 + nb_logical_drives)
	    //   - check
	    
	    while( n <= ( 4 + nb_logical_drives ))
		{
		if( contains( lst_logical_drives, n ))
		    {
		    // To make the new interpreter happy
		    n = n + 0;
		    }
		else
		    {
		    // Oops
		    logical_drive_is_missing = true;
		    ldim_dev                 = dev;
		    y2error("Error Sanity: Extended List %1 %2", lst_logical_drives, n ); 
		    }
		       
		n = n + 1;
		}
	    }
	});

    return( true );
    };

/////////////////////////////////////////////////////////////////////
// MAIN:
/////////////////////////////////////////////////////////////////////

    map<string,map> targetMap = Storage::GetTargetMap();

    if( test_mode && (size(targetMap) == 0) ) 
        {
	if( demo_mode ) 
	    {
	    y2warning("***** Demo mode active - using fake demo values *****");
	    targetMap = (map<string,map>)SCR::Read(.target.yast2, "demo_target_map.ycp");
	    }
	else 
	    {// ! demo_mode
	    y2warning("***** Test mode active - using fake values *****");
	    targetMap = (map<string,map>)SCR::Read(.target.yast2, "test_target_map.ycp");
	    }
	Storage::SetTargetMap( targetMap );
	}
    
    // Check the partition table for correctness
  
    boolean targetMap_isSane = CheckPartitionTableSanity( targetMap );
    if ( !targetMap_isSane ) return `back;

    term contents = `Dummy();

    boolean custom_val = (Storage::GetPartMode() == "CUSTOM");
    y2milestone( "custom_val %1", custom_val );

    if( size(targetMap) > 0) 
	{
	if (test_mode) 
	    y2milestone( "found targetMap\n%1\n target_is '%2'", targetMap, 
	                 Storage::GetPartDisk());

	// loop over targetMap and build radio buttons for selection
	// dont use foreach here since we need a counter (as a shortcut)
	// anyway

	term buttonbox = `VBox();

	integer dskcnt = size( filter( string d, map e, targetMap,
	                               ``(Storage::IsRealDisk( d, e ))));
	y2milestone( "dskcnt %1", dskcnt );

	if( dskcnt>10 )
	    {
	    integer i = 1;
	    list disklist = [];
	    foreach( string tname, map tdata, targetMap, 
		``{
		if( Storage::IsRealDisk( tname, tdata ))
		    {
		    disklist = add( disklist, 
				    `item(`id(tname), 
					  sformat( "%1: %2", i, tname )));
		    i = i + 1;
		    }
		});
	    buttonbox = add( buttonbox,
			     `SelectionBox(`id(`disklist), `opt(`notify),
			                   _("Available &Disks"),
					   disklist ));
	    }
	else
	    {
	    integer i = 1;
	    foreach( string tname, map tdata, targetMap, 
		``{
		if( Storage::IsRealDisk( tname, tdata ))
		    {
		    string tlinename = tdata["name"]:"?";
		    string tline = "&" + i + ":    " + tlinename;
		    boolean sel = Storage::GetPartDisk()==tname && 
				  !custom_val;
		    buttonbox = add( buttonbox, 
				     `Left(`RadioButton(`id(tname), tline, sel )));
		    i = i + 1;
		    }
		});
	    }

	buttonbox = add(buttonbox, `VSpacing(0.8) );
	// Check box for expert partitioning mode rather than
	// just selecting one of the hard disks and use
	// a standard partitioning scheme
	buttonbox = 
	    add(buttonbox, `Left(`RadioButton(`id("CUSTOM"), `opt(`notify),
	                         // label text
				 _("&Custom partitioning -- for experts"),
				 custom_val )));
    
	// This dialog selects the target disk for the installation.
	// Below this label, all targets are listed that can be used as 
	// installation target

	                   // heading text
	contents = `Frame( _("Choose a hard disk"),
			   `RadioButtonGroup(`id(`options),
					     `VBox( `VSpacing(0.4),
						    `HSquash(buttonbox),
						    `VSpacing(0.4)
						  )
					    )
			 );
	}
    else 
	{
	if (test_mode) y2milestone("NO targetMap");
	// normally the target is located on hard disks. Here no hard disks 
	// can be found. YaST2 cannot install - now the fallback YaST must be 
	// used
	contents = `Label (_("No disks found, please use the update CD for installation."));
	}


    // There are several hard disks found. Linux is completely installed on
    // one hard disk - this selection is done here
    // "Preparing Hard Disk - Step 1" is the description of the dialog what to 
    // do while the following locale is the help description
    // help part 1 of 3
    string helptext = _("<p>
All hard disks automatically detected on your system
are shown here. Select the hard disk on which to install &product;.
</p>
");
    // help part 2 of 3
    helptext = helptext + _("<p>
You may select later which part of the disk is used for &product;.
</p>
");
    // help part 3 of 3
    helptext = helptext + _("
<p>
The <b>custom partitioning</b> option for experts allows full
control over partitioning the hard disks and assigning
partitions to mount points when installing &product;.
<br>
</p>
");


    // first step of hd prepare, select a single disk or "expert" partitioning
    Wizard::SetContents( _("Preparing Hard Disk -- Step 1"),
			 contents, helptext, (boolean)WFM::Args(0), 
			 (boolean)WFM::Args(1));
    if ( Stage::initial () )
	Wizard::SetTitleIcon( "idetune" );
    
    if( UI::WidgetExists( `id(`disklist)) && !custom_val )
	{
	UI::ChangeWidget( `id(`disklist), `CurrentItem, Storage::GetPartDisk());
	}
    any ret = nil;

    // Event handling

    any option = nil;

    symbol sym = `none;

    repeat
	{
	ret = Wizard::UserInput();


	y2milestone( "ret %1", ret );

	if( is( ret, string) && (string)ret == "CUSTOM" )
	    {
	    //UI::ChangeWidget( `id(`disklist), `CurrentItem, "" );
	    custom_val = !custom_val;
	    }

	sym = `none;
	if( is( ret, symbol))
	    {
	    sym = (symbol)ret;
	    }

	if( sym == `disklist )
	    {
	    y2milestone( "set CUSTOM false" );
	    custom_val = false;
	    UI::ChangeWidget( `id("CUSTOM"), `Value, false );
	    }

	if( sym == `abort && Popup::ReallyAbort(true) )
	    return `abort;
	
	if( sym == `next ) 
	    {
	    option = UI::QueryWidget(`id(`options), `CurrentButton);
	    y2milestone( "option %1", option );
	    if( option == nil ) 
		{
		string disk = "";
		if( UI::WidgetExists( `id(`disklist)) && !custom_val )
		    {
		    disk = (string)UI::QueryWidget(`id(`disklist), `CurrentItem);
		    if( disk == nil ) disk="";
		    }
		if( find( disk, "/dev/" ) == 0 )
		    option = disk;
		else
		    {
		    // there is a selection from that one option has to be 
		    // chosen - at the moment no option is chosen
		    Popup::Message(_("Select one of the options to continue."));
		    sym = `again;
		    }
		}
	    if( option!=nil && substring((string)option,0,5) == "/dev/" )
		{
		if( targetMap[(string)option,"readonly"]:false )
		    {
		    Popup::Error( Partitions::RdonlyText( (string)option, true ));
		    sym = `again;
		    }
		else
		    {
		    Storage::SetPartMode( "USE_DISK" );
		    y2milestone( "PartMode Disk old %1 name %2", 
				 Storage::GetPartDisk(), option );
		    Storage::DisposeTargetBackup("disk");
		    Storage::CreateTargetBackup("disk");
		    Storage::ResetOndiskTarget();
		    map<string,map> tg = Storage::GetTargetMap();
		    tg = Storage::AddMountPointsForWin(tg);
		    Storage::SetTargetMap( tg );
		    Storage::SetPartDisk( (string)option );
		    Storage::SetCustomDisplay( false );
		    Storage::SetDoResize( "NO" );
		    }
		} // if (option)
	    else if( option!=nil )
		{
		y2milestone( "PartMode %1 %2", Storage::GetPartMode(), 
			     Storage::GetPartDisk() );
		Storage::DisposeTargetBackup("disk");
		Storage::CreateTargetBackup("disk");
		if( Storage::GetPartMode() != "USE_DISK" )
		    {
		    Storage::ResetOndiskTarget();
		    }
		// map tg = Storage::GetTargetMap();
		// when creating your own setup you probably do not want to have
		// these windows mount point (/windows/C, /windows/D) as default
		// tg = Storage::AddMountPointsForWin(tg);
		// Storage::SetTargetMap( tg );
		Storage::SetCustomDisplay( true );
		}

	    } // if (ret == next)
	
	} until ( sym == `next || sym == `back || sym == `cancel );
    if( sym != `next )
	{
	Storage::SetPartMode( "SUGGESTION" );
	Storage::SetPartProposalActive( true );
	}
    Storage::SaveExitKey( sym );
    return sym;
}
