/*
 *************************************************************
 *
 *     YaST2      SuSE Labs                        -o)
 *     --------------------                        /\\
 *                                                _\_v
 *           www.suse.de / www.suse.com
 * ----------------------------------------------------------
 *
 * Author:        Michael Hager <mike@suse.de>
 *
 * Description:   Take a hit, and build the targetmap
 *
 *
 *************************************************************

 $Id: propose_create_target.ycp 22502 2005-03-14 12:55:21Z fehr $


*/
{

import "Mode";
import "Storage";
import "Partitions";

  ////////////////////////////////////////////////////////
  // returns the end_cyl for a swap
  // returns -1 if an error occurs or no swap is possible
  
define integer GetSwapSize( integer start_cyl, integer end_cyl, 
                            integer cyl_size)
    ``{
    integer swap_mb = 
	Partitions::SwapSizeMb( (end_cyl-start_cyl+1) * cyl_size / (1024*1024));
    return( start_cyl + (swap_mb*1024*1024)/cyl_size );
    };

define map HitToTargetMap( map<string,map> targetMap, map<string,any> hit )
    ``{
    ////////////////////////////////////
    // 1. create boot partition if required
    //    by "split"     split base-slot
    //    or by "have" : use a slot
    // 2. create root partition
    //
    // 3. swap if possible

    y2milestone( "hit:%1", hit );
    
    string  mode 	   = hit["mode"]:"free";
    map     slot           = hit["slot"]:$[];
    map     boot_slot      = hit["boot_slot"]:$[];
    map     swap_slot      = hit["swap_slot"]:$[];
    map     disk_info      = hit["disk_info"]:$[];
    string  p_or_l         = hit["p_or_l"]:"primary";
    string  type           = hit["type"]:"";            
    string  slot_type      = slot["type"]:"";            
    string  slot_dev       = slot["dev"]:"";            
    string  boot_slot_type = boot_slot["type"]:"";            
    integer nr             = hit["nr"]:-1;
    integer boot_nr        = hit["boot_nr"]:-1;
    integer slot_nr        = slot["nr"]:-1;
    integer boot_slot_nr   = boot_slot["nr"]:-1;
    integer swap_slot_nr   = swap_slot["nr"]:-1;
    integer add_slot_nr    = hit["swap_slot_nr"]:-1;

    integer slot_start_cyl       = slot["start_cyl"]:-1;
    integer slot_end_cyl         = slot["end_cyl"]:-1;
    integer slot_win_size        = slot["win_size"]:10000000000;
    integer slot_win_length      = slot["win_length"]:-1;
    integer slot_win_max_length  = slot["win_max_length"]:-1;
    integer boot_slot_win_size   = boot_slot["win_size"]:10000000000;
    integer boot_slot_win_length = boot_slot["win_length"]:-1;
    integer boot_slot_start_cyl  = boot_slot["start_cyl"]:-1;
    integer boot_slot_end_cyl    = boot_slot["end_cyl"]:-1;

    
    map disk        = targetMap[slot_dev]:$[];
    list<map> partitions = disk["partitions"]:[];

    boolean need_boot_part = hit["need_boot"]:false;
    boolean need_extended  = hit["extd"]:false;
    boolean boot_by_split  = hit["boot_split"]:false;
    boolean root_by_split  = hit["root_split"]:false;

    integer cyl_size       = disk["cyl_size"]:1;
	
    min_nb_boot_cyl = (Partitions::MinimalNeededBootsize() / cyl_size) + 1;

    //
    map boot_win_part = $[];   // resized windows partition, if mode == win
    map boot_part     = $[];   // partition of base slot
    map win_part      = $[];   // resized windows partition, if mode == win
    map del_part      = $[];
    map part          = $[];   // partition of base slot
    map ext_part      = $[];

    boolean have_old_swap_part = ( swap_slot != $[] );

    hitlist  = [];
    hiterror = false;

    // we need this prop_reuse_nr, bececause we have entered slot_nr and 
    // therefore partition numbers but, if I delete a logical part. this 
    // number may change, and we loose our handle. We can not use the 
    // "orig_nb" because sometimes we will create a proposal on an already 
    // changed partitioning:
    // therefore the base is always the targetMap with its definition.
    
    partitions = maplist( map part, partitions, 
                          ``( add( part, "prop_reuse_nr", part["nr"]:0))); 

			  
    /////////////////////////////////////////////////////
    // Create BOOT Partition ... (but not the split mode)
    // HAVE-mode
    // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

    y2milestone( "need_boot_part:%1 boot_by_split:%2", need_boot_part, 
                 boot_by_split );
    if( need_boot_part && !boot_by_split )
	{
	if( boot_slot_type == "win_resize")
	    {
	    partitions = (list<map>)resize_partition( partitions,  boot_slot_nr, 
	                                   boot_slot_win_length, 
					   boot_slot_win_size, 
					   boot_slot_win_length );
	    }

	if( boot_slot_type == "linux" )
	    {
	    if( boot_slot_end_cyl > Partitions::BootCyl() )
		{
		partitions = (list<map>)delete_partition( partitions, boot_slot_nr );

		partitions = (list<map>)create_partition( partitions, boot_slot_start_cyl,
					       boot_slot_start_cyl+min_nb_boot_cyl-1,
					       boot_slot_nr, 
					       Partitions::BootMount(),
					       "no-extended", 
					       disk["max_primary"]:4 );
		}
	    else
		{
		partitions = (list<map>)reuse_partition( partitions, boot_slot_nr, 
		                              Partitions::BootMount() );
		}
	    }
	else
	    {
	    if( boot_slot_end_cyl < Partitions::BootCyl() )
		{
		partitions = (list<map>)create_partition( partitions, boot_slot_start_cyl,
					       boot_slot_end_cyl, boot_nr,
					       Partitions::BootMount(), 
					       "no-extended",
					       disk["max_primary"]:4 );
		}
	    else
		{
		partitions = (list<map>)create_partition( partitions, boot_slot_start_cyl,
					       boot_slot_start_cyl+min_nb_boot_cyl-1,
					       boot_nr, Partitions::BootMount(),
					       "no-extended",
					       disk["max_primary"]:4 );
		}
	    }
	}  


    /////////////////////////////////////////////////////
    // Create ROOT Partition ... (but not the split mode)
    // no extended! see root_split_mode
    // HAVE + NOTH-mode withOUT root-split
    // bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

    if( !boot_by_split && !root_by_split )
	{
	if( slot_type == "win_resize")     
	    partitions = (list<map>)resize_partition( partitions, slot_nr, slot_win_length,
	                                   slot_win_size, slot_win_max_length );

	if( slot_type == "linux" )
	    {
	    partitions = (list<map>)reuse_partition( partitions, slot_nr, "/" );
	    }
	else
	    {
	    partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					   slot_end_cyl, nr, "/",
					   "no-extended",
					   disk["max_primary"]:4 );
	    }
	}	
    

    /////////////////////////////////////////////////////
    // BOOT && ROOT  Partition if  split mode
    // SPLIT-mode
    // cccccccccccccccccccccccccccccccccccccccccccccccc

    if( need_boot_part && boot_by_split )
	{
	integer rootnr = 5;

	if( slot_type == "win_resize")
	    {
	    partitions = (list<map>)resize_partition( partitions, slot_nr, slot_win_length,
	                                   slot_win_size, slot_win_max_length );
	    }
	if( slot_type == "linux" )
	    {
	    partitions = (list<map>)delete_partition( partitions, slot_nr );
	    }

	if( need_extended )
	    {
	    partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					   slot_end_cyl,
					   slot_type == "linux" ? slot_nr: nr,
					   "", "extended", 
					   disk["max_primary"]:4 );
	    }
	else
	    {
	    rootnr = (slot_type == "linux" ? slot_nr: nr);
	    }

	y2milestone( "rootnr:%1 boot_nr:%2", rootnr, boot_nr );
	if( boot_nr==-1 )
	    {
	    boot_nr = rootnr;
	    if( need_extended )
		{
		rootnr = rootnr+1;
		}
	    else
		{
		rootnr = hit["nr"]:0;
		}
	    }
	y2milestone( "rootnr:%1 boot_nr:%2", rootnr, boot_nr );

	partitions = (list<map>)create_partition( partitions, slot_start_cyl,
				       slot_start_cyl + min_nb_boot_cyl-1,
				       need_extended ? 6:boot_nr, 
				       Partitions::BootMount(),
				       "no-extended", disk["max_primary"]:4 );

	integer new_start_cyl = slot_start_cyl + min_nb_boot_cyl;
	integer swap_end = GetSwapSize( new_start_cyl, slot_end_cyl, cyl_size);

	y2milestone( "swap_end %1 slot_start_cyl %2 rootnr %3", swap_end,
	             slot_start_cyl, rootnr );

	if( !have_old_swap_part && swap_end != -1 && 
	    swap_end > slot_start_cyl && rootnr > disk["max_primary"]:4 &&
	    ((((slot_end_cyl+1)-swap_end+1) * cyl_size) > 
		Partitions::minimal_needed_disksize))
	    {
	    // swap in extended
	    partitions = (list<map>)create_partition( partitions, new_start_cyl, swap_end,
					   rootnr, "swap", "no-extended",
					   disk["max_primary"]:4 );

	    partitions = (list<map>)create_partition( partitions, swap_end+1, slot_end_cyl,
					   rootnr+1, "/", "no-extended",
					   disk["max_primary"]:4 );
	    }
	else
	    {
	    partitions = (list<map>)create_partition( partitions,
					   slot_start_cyl+min_nb_boot_cyl,
					   slot_end_cyl, rootnr, "/",
					   "no-extended", 
					   disk["max_primary"]:4 );	      
	    }
	}

    /////////////////////////////////////////////////////
    // ROOT by split
    // HAVE + NOTH-mode with root-split
    // (if I have a reusable swap partition. I donot need
    // to split root
    //dddddddddddddddddddddddddddddddddddddddddddddddddddddd

    if( (!need_boot_part && root_by_split) ||
	(need_boot_part && root_by_split && !boot_by_split))
	{
	if( slot_type == "win_resize")
	    {
	    partitions = (list<map>)resize_partition( partitions, slot_nr, slot_win_length,
	                                   slot_win_size, slot_win_max_length );
	    }

	if( !have_old_swap_part )
	    {
	    ////////////////////////
	    // Do the rootsplit ...
	    if( slot_type == "linux" )
		{
		partitions = (list<map>)delete_partition( partitions, slot_nr );
		}

	    if( need_extended )
		{
		partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					       slot_end_cyl,
					       slot_type=="linux" ? slot_nr:nr,
					       "", "extended", 
					       disk["max_primary"]:4 );
		}

	    integer swap_end = GetSwapSize( slot_start_cyl, slot_end_cyl, 
	                                    cyl_size);

	    if( swap_end != -1 && swap_end > slot_start_cyl && need_extended &&
		((slot_end_cyl+1-swap_end+1) * cyl_size) > 
		    Partitions::minimal_needed_disksize )
		{
		// swap
		partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					       swap_end, 5, "swap",
					       "no-extended",
					       disk["max_primary"]:4 );
		partitions = (list<map>)create_partition( partitions, swap_end+1,
					       slot_end_cyl, 6, "/",
					       "no-extended",
					       disk["max_primary"]:4 );
		}
	    else
		{
		partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					       slot_end_cyl, 5, "/",
					       "no-extended", 
					       disk["max_primary"]:4 );
		}
	    }
	else
	    {
	    //////////////////////////////////////////////////////////////
	    // We don't need a root split ...
	    
	    if( slot_type == "linux" )
		{
		partitions = (list<map>)reuse_partition( partitions, slot_nr, "/" );
		}
	    else
		{
		partitions = (list<map>)create_partition( partitions, slot_start_cyl,
					       slot_end_cyl, nr, "/",
					       "no-extended", 
					       disk["max_primary"]:4 );
		}
	    }
	}

    if( have_old_swap_part )
	{
	partitions = (list<map>)reuse_partition( partitions, swap_slot_nr, "swap" );
	}

    if( hit["boot_r_slot"]:$[] != $[] && 
        size(filter(map p, partitions, 
	            ``(p["mount"]:""==Partitions::BootMount())))==0 )
	{
	map slot = hit["boot_r_slot"]:$[];
	partitions = (list<map>)reuse_partition( partitions, slot["nr"]:0, 
	                              Partitions::BootMount() );
	}

    y2debug( "NEW: add_slot_nr:%1", add_slot_nr );

    if( add_slot_nr != -1 && add_slot_nr<=Partitions::MaxLogical(slot_dev) &&
	size(filter(map p, partitions, ``(p["mount"]:""=="swap")))==0 )
	{
	list pt = filter(map p, partitions, 
			 ``(p["mount"]:""=="/" && !p["delete"]:false));
	if( size(pt)>0 )
	    {
	    map root = pt[0]:$[];
	    integer start = root["region",0]:0;
	    integer end = start + root["region",1]:0 - 1;
	    integer swap_end = GetSwapSize( start, end, cyl_size );

	    y2debug( "NEW: start:%1 end:%2 swap_end:%3 cyl_size:%4", 
		     start, end, swap_end, cyl_size );
	    if( swap_end != -1 &&  swap_end > start &&
		(end-swap_end+1)*cyl_size > Partitions::minimal_needed_disksize)
		{
		integer old_num = root["nr"]:0;
		y2debug( "NEW: old_num:%1", old_num );
		if( root["create"]:false == false )
		    {
		    partitions = (list<map>)delete_partition( partitions, old_num );
		    partitions = (list<map>)create_partition( partitions, start, swap_end, 
						   old_num, "swap", 
						   "no-extended", 
						   disk["max_primary"]:4  );
		    }
		else
		    {
		    partitions = filter( map p, partitions,
					 ``(!(p["nr"]:-1 == old_num &&
					      p["delete"]:false==false)));
		    partitions = (list<map>)create_partition( partitions, start, swap_end, 
						   old_num, "swap", 
						   "no-extended", 
						   disk["max_primary"]:4 );
		    }
		partitions = (list<map>)create_partition( partitions, swap_end+1, end, 
					       add_slot_nr, "/", 
					       "no-extended", 
					       disk["max_primary"]:4  );
		}
	    }
	}

    
    // change( disk, "partitions", partitions );
    partitions = maplist( map part, partitions, ``{
	part["device"] = Storage::GetDeviceName( slot_dev, part["nr"]:0 );
	return( part );
	});
    y2milestone( "NEW: partitions:%1", partitions );

    disk["partitions"] = partitions;
    targetMap[slot_dev] = disk;
    Storage::SetPartDisk( slot_dev );

    if( hiterror == true)
	{
	y2debug( "hit slot      :  %1", slot); 
	y2debug( "hit boot_slot :  %1", boot_slot); 
	y2debug( "hit was       :  %1", 
	          filter( string key, any value, hit, 
			  ``( key != "slot" && key != "boot_slot")));  
	return( nil );
	}
    else
	{
	y2debug( "hit slot      :  %1", slot); 
	y2debug( "hit boot_slot :  %1", boot_slot); 
	y2debug( "hit was       :  %1", 
	         filter( string key, any value, hit, 
			 ``( key != "slot" && key != "boot_slot")));  
	return( targetMap );
	}
    }



  /////////////////////////////////////////////////////////////////////
  // next step is find the best hit
  // 	hit rating for the best proposal:
  // 	all mode==free are better than win_reuse/reuse    
  // 	all mode==reuse better than win_reuse
  //
  //  one slot: if biggest slot <  1.5G  1p-pri == 1p-log      //win-mode!
  //  one slot: if biggest slot >= 1.5G  1p-pri <  1p-log
  //  
  //  if size is > 2G or size_have <= size_split * 1.1 
  //  -split is better than -have, because an own boot-slot will not be used
  //  really good.
  //  
  //  If I can make a swap partition:  size_ns = ns-proposal
  //  size_s
  //  450-800   size_ns >= size_s*1.2
  //  800-1.2   size_ns >= size_s*1.2 
  //  1.2-1.6   size_ns >= size_s*1.3
  //  1.6-2     size_ns >= size_s*1.5
  //  >2        use swap
  //
  
define map<string,any> GetTheBestHit( list<map> hitlist )
    ``{
    ////////////////////////////////////
    // build a score
    // first rough implemtaion
    // todo: implement the rules above 
    hitlist = maplist( map hit, hitlist, ``{
	integer score = 0;
	boolean root_split  = hit["root_split"]:false;
	boolean boot_split  = hit["boot_split"]:false;
	boolean need_ext    = hit["extd"]:false;
	map     slot        = hit["slot"]:$[];
	map     boot_slot   = hit["boot_slot"]:$[];
	map     swap_slot   = hit["swap_slot"]:$[];
	integer ssize       = slot["size"]:0;
	string  type        = slot["type"]:"";
	string  boot_type   = "split";
	integer swap_slot_nr = hit["swap_slot_nr"]:-1;
	integer free_primary = hit["free_primary"]:0;

	score = ssize / 1000000;


	// don resize two times if you don't have to
	boot_type = boot_slot["type"]:"split";
	if( boot_type == "win_resize" ) score = 900 + (score/100);

	// try to use free space
	if( type=="free")       score = 400 *  score;
	if( type=="linux")      score =  20 * score;
	if( type=="win_resize" && slot["ntfs"]:false ) score = score / 3;

	// prefer a solution that contains a swap partition
	if( swap_slot == $[] )
	    {
	    if( root_split ) score = score * 2;
	    else if( swap_slot_nr != -1 ) 
		{
		score = score * 2;
		if( free_primary < 3 )
		    {
		    score = score - 5;
		    }
		}

	    }
	else
	    {
	    // if I have swap : try to avoid split
	    if ( !boot_split ) score = score * 2;
	    }

	// prefer a solution that needs no extended partition
	if( need_ext ) score = score - 1;

	// give a penalty to hits with reuse of a way too large boot slot
	if( !Arch::ia64 () && boot_slot["type"]:"" == "linux" && 
	    boot_slot["size"]:0>200*1024*1024 )
	    {
	    score = score / 4;
	    }

	y2debug( "score=%1 type=%2 root_split=%3 boot_split=%4 free:%5",
		 score, type, root_split, boot_split, free_primary );

	return( add( hit, "score", score ) );
	});
		       

    hitlist = sort(map slot_x, map slot_y, hitlist, 
                   ``(slot_x["score"]:0 > slot_y["score"]:0));

    if( size( hitlist ) > 0 )
	{
	y2milestone( "GetTheBestHit hit:%1", hitlist[0]:$[] );
	return(map<string,any>)( hitlist[0]:$[] );
	}
    else
	{
	return(map<string,any>)( $[] );
	}
    };

}
  
