/*


$Id: autopart.ycp 22624 2005-03-16 15:14:29Z nashif $
*/
{
    textdomain "autoinst";

    import "FileSystems";

    include "partitioning/partition_defines.ycp";



    map get_gap_info( map disk, map pd, boolean add_exist_linux );
    map add_cylinder_info( map conf, map gap );
    map get_perfect_list( list ps, map g );
    map process_partition_data( string dev, map solution );
    map find_matching_disk( list<string> disks, map target, map conf );
    map try_resize_windows( map disk );
    map remove_possible_partitions( map disk, map conf );
    map distribute_space( integer rest, list weights, list added, list ps );
    void add_part_recursive( list ps, map g );
    map normalize_gaps( list ps, map g );
    integer do_weighting( list ps, map g );

    symbol cur_mode = `free;
    integer cur_weight = -10000;
    map cur_gap = $[];


    list GetNoneLinuxPartitions(string device) {
        list ret = [];
        foreach ( string dev, map disk, Storage::GetTargetMap(), ``{
                if( Storage::IsRealDisk( dev, disk )  && dev == device)
                {
                list<map> l = (list<map>) filter( map p, disk["partitions"]:[],
                    ``(!p["delete"]:false &&
                        !p["format"]:false &&
                        !Partitions::IsLinuxPartition(p["fsid"]:0)) );

                l = filter(map p, l, ``(!contains( [`xfs, `ext2, `ext3, `jfs, `reiser],
                            p["used_fs"]:`unknown)));
                l = filter(map p, l,
                    ``(!FileSystems::IsSystemMp( p["mount"]:"", false )));
                if( size(l)>0 )
                {
                    list<integer> ln =  maplist( map p, l, ``(p["nr"]:0));
                    ret = union( ret, ln );
                }
                }
                });
        y2milestone( "GetNoneLinuxPartitions ret=%1", ret );
        return( ret );
    }


    list<integer> GetAllPartitions(string device) {
        list<integer> ret = [];
        foreach ( string dev, map disk, Storage::GetTargetMap(), ``{
                if( Storage::IsRealDisk( dev, disk )  && dev == device)
                {
                list<integer> l = maplist( map p, disk["partitions"]:[],
                    ``(p["nr"]:0));

                ret = (list<integer>)union( ret, l );
                }
                });
        y2milestone( "All Partitions ret=%1", ret );
        return( ret );
    }

    /**
     * Read partition data from XML control file
     * @return map flexible propsal map
     */
    define list<map> preprocess_partition_config(list<map> xmlflex) {
	
	y2debug("xml input: %1", xmlflex );

	list<map>partitioning = maplist(map d, xmlflex, ``{
	    
	    foreach( string key, [ "keep_partition_id", "keep_partition_num"], ``{
		list num = [];
		list<string> nlist = splitstring(d[key]:"", ",");
		foreach( string n, nlist, ``{ num = union( num, [tointeger(n)] );});
		d[key] = num;
	    });

	    list fsys = [];
	    list<string> nlist = splitstring( d["keep_partition_fsys"]:"" , "," );
	    foreach( string n, nlist,
		     ``{
		symbol fs = FileSystems::FsToSymbol(n);
		if( fs != `none )
		{
		    fsys = union( fsys, [ fs ] );
		}
	    });
	    d["keep_partition_fsys"] = fsys;


            list<map> user_partitions = d["partitions"]:[];
            if (size(user_partitions) == 0)
            {
                y2milestone("no partitions specified, creating default scheme");
                map root = $[];
                root["mount"] = "/";
                root["size"] = "max";
                map swap = $[];
                swap["mount"] = "swap";
                swap["size"] = "auto";
                user_partitions=add(user_partitions, swap);
                user_partitions=add(user_partitions, root);
                
            }
            
	    list partitions = [];
	    foreach(map partition, user_partitions,
		    ``{
		if (haskey(partition, "maxsize"))
		{
		    partition["max"] = kmgt_str_to_byte(partition["maxsize"]:"");
		} 
		    
		if (partition["size"]:"" != "")
		{
		    string s = partition["size"]:"";
		    if( tolower(s) == "auto" )
		    {
			partition["size"] = -1;
		    }
		    else if( tolower(s) == "max" )
		    {
			partition["size"] = 0;
		    }
		    else
		    {
			partition["size"] = kmgt_str_to_byte( s );
		    }
		}
		    
		if( partition["size"]:0 == -1 && partition["mount"]:"" == "swap" )
		{
		    partition["size"] = 1024*1024*Partitions::SwapSizeMb(0);
		}
	    
		if( partition["mount"]:"" == Partitions::BootMount() )
		{
		    if( partition["size"]:0 == -1 )
			partition["size"] = Partitions::MinimalNeededBootsize();

		    if( partition["filesystem"]:`none == `none )
			partition["filesystem"] = Partitions::DefaultBootFs();

		    if( partition["filesystem_id"]:0 == 0 )
			partition["filesystem_id"] = Partitions::FsidBoot();

		    //partition["max_cyl"] = Partitions::BootCyl();
		}
		
		if( partition["size"]:0 == -1 )
		    partition["size"] = 0;

		if (partition["lvm_group"]:""!="")
	    	    partition["filesystem_id"] =  Partitions::fsid_lvm;
		else if (partition["raid_name"]:""!="")
		    partition["filesystem_id"] =  Partitions::fsid_raid;

		partition["fsid"] = partition["filesystem_id"]:Partitions::fsid_native;
		
		y2debug("partition: %1", partition);
	        partitions = add( partitions, partition );
	    });
	
	    if (!d["is_lvm_vg"]:false) {
		d["partitions"] = partitions;
	    }
	    return (d);
	});
	
	y2milestone( "conf: %1", partitioning );
	return( partitioning );
    }
    

    /**
     * Read partition data from XML control file
     * @return map flexible propsal map
     */
    define map read_partition_xml_config(map automap)
	``{

	map xmlflex = automap;
	y2debug("xml input: %1", xmlflex );

	map conf = $[];
	conf["prefer_remove"] = xmlflex["prefer_remove"]:true;
	conf["remove_special_partitions"] =
	    xmlflex["remove_special_partitions"]:false;
	
	conf["keep_partition_id"] = [];
	conf["keep_partition_num"] = [];
	conf["keep_partition_fsys"] = [];

	foreach( string key, [ "keep_partition_id", "keep_partition_num"], ``{
	    list num = [];
	    list<string> nlist = splitstring(xmlflex[key]:"", ",");
	    foreach( string n, nlist, ``{ num = union( num, [tointeger(n)] );});
	    conf[key] = num;
	});

	list fsys = [];
	list<string> nlist = splitstring( xmlflex["keep_partition_fsys"]:"" , "," );
	foreach( string n, nlist,
		 ``{
	    symbol fs = FileSystems::FsToSymbol(n);
	    if( fs != `none )
	    {
		fsys = union( fsys, [ fs ] );
	    }
	});
	conf["keep_partition_fsys"] = fsys;
	list partitions = [];
	foreach(map p, xmlflex["partitions"]:[],
		``{
	    map partition = $[];

	    if (p["disk"]:0 != 0)
	    {
		partition["disk"] = p["disk"]:0;
	    }
	    if (p["id"]:0 != 0)
	    {
		partition["id"] = p["id"]:0;
	    }

	    if (p["fstopt"]:"" != "")
	    {
		partition["fstopt"] = p["fstopt"]:"";
	    }

	    if (p["formatopt"]:"" != "")
	    {
		partition["fopt"] = p["formatopt"]:"";
	    }

	    partition["increasable"] = p["increasable"]:false;

	    if (p["mount"]:"" != "")
	    {
		partition["mount"] = p["mount"]:"";
	    }

	    if (p["percent"]:-1 != -1)
	    {
		partition["pct"] = p["percent"]:100;
	    }

	    if (p["label"]:"" != "")
	    {
		partition["label"] = p["label"]:"";
	    }

	    if (p["maxsize"]:"" != "")
	    {
		partition["maxsize"] = kmgt_str_to_byte(p["maxsize"]:"");
	    }

	    if (p["fsys"]:"" != "")
	    {
		symbol fs = FileSystems::FsToSymbol(p["fsys"]:"");
		if( fs != `none )
		{
		    partition["fsys"] = fs;
		}
	    }

	    if (p["size"]:"" != "")
	    {
		string s = p["size"]:"";
		if( tolower(s) == "auto" )
		{
		    partition["size"] = -1;
		}
		else if( tolower(s) == "max" )
		{
		    partition["size"] = 0;
		}
		else
		{
		    partition["size"] = kmgt_str_to_byte( s );
		}

	    }

	    if( partition["size"]:0 == -1 && partition["mount"]:"" == "swap" )
	    {
		partition["size"] = 1024*1024*Partitions::SwapSizeMb(0);
	    }
	    if( partition["mount"]:"" == Partitions::BootMount() )
	    {
		if( partition["size"]:0 == -1 )
		    partition["size"] = Partitions::MinimalNeededBootsize();

		if( partition["fsys"]:`none == `none )
		    partition["fsys"] = Partitions::DefaultBootFs();

		if( partition["id"]:0 == 0 )
		    partition["id"] = Partitions::FsidBoot();

		partition["max_cyl"] = Partitions::BootCyl();
	    }

	    if( partition["size"]:0 == -1 )
		partition["size"] = 0;

	    y2debug("partition: %1", partition);
	    if( size(partition["mount"]:"")>0 || partition["id"]:0 > 0 )
	    {
		partitions = add( partitions, partition );
	    }


	});
	conf["partitions"] = partitions;
	if( size(partitions)==0 )
	    conf = $[];
	else
	    conf["partitions"] = partitions;

	y2milestone( "conf %1", conf );
	return( conf );
    }


    define map try_add_boot( map conf, map disk ) {
	boolean boot = 
	    size(filter( map e, conf["partitions"]:[], 
			 ``(e["mount"]:""==Partitions::BootMount()))) > 0;
	boolean root = size(filter( map e, conf["partitions"]:[], 
				    ``(e["mount"]:""=="/"))) > 0;
	map tc = (map) eval(conf);
	if( !boot && root &&
	    (disk["cyl_count"]:0 > Partitions::BootCyl() ||
	     Arch::ia64 () || Arch::ppc () || Arch::sparc ()) )
	{
	    map pb = $[];
	    pb["mount"] = Partitions::BootMount();
	    pb["size"] = Partitions::MinimalNeededBootsize();
	    pb["fsys"] = Partitions::DefaultBootFs();
	    pb["id"] = Partitions::FsidBoot();
	    pb["auto_added"] = true;
	    pb["max_cyl"] = Partitions::BootCyl();
	    tc["partitions"] = add( tc["partitions"]:[], pb );
	    y2milestone( "boot added automagically pb %1", pb );
	}
	return( tc );
    }

/*
  define map do_flexible_disk( string dev, map disk )
  ``{
  y2milestone( "dev %1", dev );
  map ret = $[];
  ret["ok"] = false;
  map conf = read_partition_config( pinfo_name() );
  map solutions = $[];

  if( size(conf)>0 && Storage::IsRealDisk( dev, disk ))
  {
  y2milestone( "processing disk %1", dev );
  map tc = try_add_boot( conf, disk );
  cur_mode = `free;
  map gap = get_gap_info( disk, false );
  tc = add_cylinder_info( tc, gap );
  map sol = get_perfect_list( tc["partitions"]:[], gap );
  if( size(sol)>0 )
  {
  sol["disk"] = eval(disk);
  ret["ok"] = true;
  ret["disk"] = process_partition_data( dev, sol );
  }
  }
  y2milestone( "ret %1", ret["ok"]:false );
  if( ret["ok"]:false )
  {
  y2milestone( "disk %1", ret["disk"]:[] );
  }
  return( ret );
  }

*/
    define map do_flexible( map<string,map> targetmap, map automap )
	``{
	map ret = $[];
	ret["ok"] = false;
	map conf = $[];
	conf = read_partition_xml_config(automap);
	list<map> solutions = [];

	if( size(conf)>0 )
	{
	    // return list of available devices
	    list<string> ddev = maplist( string k, map e, 
					 filter( string l, map f, targetmap, 
						 ``(Storage::IsRealDisk( l, f ))), ``(k));
	    // ["/dev/hda", "/dev/hdb"]
	    y2milestone( "available disks (ddev) %1", ddev );
	    map tc = $[];
	    map<integer,any> dtmp = $[];
	    foreach( map p, conf["partitions"]:[], 
		     ``{
		integer dprio = p["disk"]:0;
		if( haskey( dtmp, dprio ))
		{
		    dtmp[dprio] = add( dtmp[dprio]:[], p );
		}
		else
		{
		    dtmp[dprio] = [ p ];
		}
	    });


	    // $[0:[$[..], $[...]], 1:[$[..], $[...]],2:[$[..], $[...]]]
	    
	    y2milestone( "disks and their partitions (dtmp) %1", dtmp );
	    list dlist = maplist( integer k, any e, dtmp, ``(e) );

            // [ [$[..], $[...]], [$[..], $[...]],.... ]

	    y2milestone( "list of partition lists (dlist) %1", dlist );
	    if( size(dlist)>size(ddev) )
	    {
		y2milestone("Some partitions are still not assigned to a disk");
		integer idx = size(ddev);
		while( idx<size(dlist) )
		{
		    dlist[size(ddev)-1] = union( dlist[size(ddev)-1]:[], 
						 dlist[idx]:[] );
		    idx = idx+1;
		}
		while( size(dlist)>size(ddev) )
		{
		    dlist = remove( dlist, size(ddev) );
		}
		y2milestone( "dlist %1", dlist );
	    }
	    list save_dlist = (list) eval(dlist);
	    repeat
		{
		    integer count = 0;
		    repeat
			{
			    list<string> td = eval(ddev);
			    integer idx = 0;
			    y2milestone( "start while count %1", count );
			    while( idx<size(dlist) && count<size(dlist) )
			    {
				y2milestone( "in while idx %1", idx );
				tc = (map) eval(conf);
				tc["partitions"] = eval( dlist[idx]:[] );
				map md = find_matching_disk( td, targetmap, tc );
				y2milestone( "size(md) %1", size(md) );
				if( size(md)>0 )
				{
				    solutions = add( solutions, md );
				    td = filter( string e, td, ``(e!=md["device"]:""));
				    y2milestone( "new td %1", td );
				    idx = idx+1;
				}
				else
				{
				    y2milestone( "no solution" );
				    idx = size(dlist);
				    td = eval(ddev);
				    solutions = [];
				    count = count + 1;
				    if( size(dlist)>1 )
				    {
					list tfi = dlist[0]:[];
					dlist = remove( dlist, 0 );
					dlist = add( dlist, tfi );
					y2milestone( "new rotated dlist %1", dlist );
				    }
				}
			    }
			}
		    until( size(solutions)>0 || count>=size(dlist) );
		    if( size(solutions)==0 && size(dlist)>1 )
		    {
			dlist = (list) eval(save_dlist);
			dlist[size(dlist)-2] = union( dlist[size(dlist)-2]:[],
						      dlist[size(dlist)-1]:[] );
			dlist = remove( dlist, size(dlist)-1 );
			y2milestone( "new truncated dlist %1", dlist );
			save_dlist = (list)eval(dlist);
		    }
		}
	    until( size(solutions)>0 || size(dlist)<=1 );
	    if( size(solutions)==0 &&
		(size(conf["keep_partition_fsys"]:[])>0 ||
		 size(conf["keep_partition_id"]:[])>0 ||
		 size(conf["keep_partition_num"]:[])>0 ||
		 !conf["prefer_remove"]:false))
	    {
		y2milestone( "desperate mode" );
		tc = (map) eval(conf);
		cur_mode = `desparate;
		tc["keep_partition_fsys"] = [];
		tc["keep_partition_id"] = [];
		tc["keep_partition_num"] = [];
		tc["prefer_remove"] = true;
		map md = find_matching_disk( ddev, targetmap, tc );
		if( size(md)>0 )
		{
		    solutions = add( solutions, md );
		}
	    }
	    if( size(solutions)>0 )
	    {
		foreach( map e, solutions,
			 ``{
		    string disk = e["device"]:"";
		    targetmap[disk] = process_partition_data( disk, e );
		    y2milestone( "solution disk %1 %2", disk, targetmap[disk]:$[] );
		});
		ret["ok"] = true;
		ret["targetmap"] = Storage::SpecialBootHandling( targetmap );
	    }
	}
	return( ret );
    }


    /**
     * Find matching disk
     */
    define map find_matching_disk( list<string> disks, map target, map conf )
	``{
	map<string,map> solutions = $[];

	cur_weight = -100000;
	cur_gap = $[];
	foreach( string k, disks,
		 ``{
	    map e = target[k]:$[];
	    map pd = conf;
	    y2milestone( "processing disk %1", k );
	    y2milestone( "parts %1", conf["partitions"]:[] );
	    map tc = try_add_boot( conf, e );
	    cur_mode = `free;
	    if( !tc["prefer_remove"]:false )
	    {
		map gap = get_gap_info( e, pd , false );
		tc = add_cylinder_info( tc, gap );
		map l = get_perfect_list( tc["partitions"]:[], gap );
		if( size(l)>0 )
		{
		    solutions[k] = eval(l);
		    solutions[k,"disk"] = eval(e);
		}
		cur_mode = `reuse;
		map egap = get_gap_info( e, pd, true );
		if( size(egap["gap"]:[]) > size(gap["gap"]:[]) )
		{
		    tc = add_cylinder_info( tc, egap );
		    l = get_perfect_list( tc["partitions"]:[], egap );
		    if( size(l)>0 && 
			(!haskey(solutions,k) || 
			 (haskey( l, "weight" ) && 
			  l["weigth"]:0 > solutions[k,"weigth"]:0 )))
		    {
			y2milestone( "solution reuse existing" );
			solutions[k] = eval(l);
			solutions[k,"disk"] = eval(e);
		    }
		}
		cur_mode = `resize;
		map rw = try_resize_windows( e );
		if( size( filter( map p, rw["partitions"]:[],
				  ``(haskey(p, "winfo")) ))>0 )
		{
		    egap = get_gap_info( rw, pd, true );
		    tc = add_cylinder_info( tc, egap );
		    l = get_perfect_list( tc["partitions"]:[], egap );
		    if( size(l)>0 && 
			(!haskey(solutions,k) ||
			 (haskey( l, "weight" ) && 
			  l["weigth"]:0 > solutions[k,"weigth"]:0 )))
		    {
			y2milestone( "solution resizing windows" );
			solutions[k] = eval(l);
			solutions[k,"disk"] = eval(rw);
		    }
		}
	    }
	    else
	    {
		cur_mode = `free;
		map rp = remove_possible_partitions( e, tc );
		map gap = get_gap_info( rp, pd, false );
		tc = add_cylinder_info( tc, gap );
		map l = get_perfect_list( tc["partitions"]:[], gap );
		if( size(l)>0 )
		{
		    solutions[k] = eval(l);
		    solutions[k,"disk"] = eval(rp);
		}
	    }
	});
	map ret = $[];
	if( size(solutions)>0 )
	{
	    foreach( string k, map e, solutions,
		     ``{
		y2milestone( "disk %1 weight %2", k, e["weight"]:0 );
	    });
	    list<string> disks = maplist( string k, map e, solutions, ``(k) );
	    disks = sort( string a, string b, disks, 
			  ``(solutions[a,"weight"]:0>solutions[b,"weight"]:0));
	    y2milestone( "sorted disks %1", disks );
	    ret = solutions[disks[0]:""]:$[];
	    ret["device"] = disks[0]:"";
	}
	return( ret );
    }


    
    /**
     * Process partition data
     */
    define map process_partition_data( string dev, map solution )
	``{
	map disk = solution["disk"]:$[];
	list<map> partitions = [];
	string value = "";
	map mapvalue = $[];
	boolean remove_boot = false;
	if( size( filter( map e, solution["partitions"]:[], 
			  ``(e["mount"]:""==Partitions::BootMount() && 
			     e["auto_added"]:false)))>0 )
	{
	    foreach( map e, solution["solution","gap"]:[],
		     ``{
		foreach( list a, e["added"]:[],
			 ``{
		    integer pindex = a[0]:0;
		    if( solution["partitions",pindex,"mount"]:"" == "/" &&
			disk["cyl_count"]:0 > Partitions::BootCyl() &&
			e["end"]:0 <= Partitions::BootCyl() )
		    {
			remove_boot = true;
		    }
		});
	    });
	}
	integer index = 0;
	if( remove_boot )
	{
	    foreach( map e, solution["solution","gap"]:[],
		     ``{
		list nlist = [];
		foreach( list a, e["added"]:[],
			 ``{
		    integer pindex = a[0]:0;
		    if( solution["partitions",pindex,"mount"]:"" == 
			Partitions::BootMount() )
		    {
			integer rest = a[2]:0;
			y2milestone( "remove unneeded %3 %1 cyl %2", 
				     e["added"]:[], rest, Partitions::BootMount() );
			list<list> nlist = filter( list l, e["added"]:[], ``(l[0]:0!=pindex));
			if( size(nlist)>0 && !e["exists"]:false )
			{
			    list weight = maplist( list l, nlist, ``(l[2]:0) );
			    map r = $[];
			    r = distribute_space( rest, weight, nlist, 
						  solution["partitions"]:[] );
			    nlist = eval(r["added"]:[]);
			    solution["solution","gap",index,"cylinders"] = 
				e["cylinders"]:0 - r["diff"]:0;
			}
			solution["solution","gap",index,"added"] = eval(nlist);
			y2milestone( "remove unneeded %2 %1", e["added"]:[],
				     Partitions::BootMount() );
		    }
		    pindex = pindex+1;
		});
		index = index + 1;
	    });
	}
	index = 0;
	foreach( map e, solution["solution","gap"]:[],
		 ``{
	    if( !e["exists"]:false && e["cylinders"]:0>0 )
	    {
		integer increase = 0;
		list<integer> weight = 
		    maplist( list l, e["added"]:[], 
			     ``(solution["partitions",l[0]:0,"grow"]:false ? 1 : 0) );
		if( find( integer l, weight, ``(l>0) ) != nil ) 
		{
		    map r = $[];
		    r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
					  solution["partitions"]:[] );
		    solution["solution","gap",index,"added"] = eval(r["added"]:[]);
		    solution["solution","gap",index,"cylinders"] = 
			e["cylinders"]:0 - r["diff"]:0;
		    y2milestone( "increase increasable p %1 cyl %2", 
				 solution["solution","gap",index,"added"]:[],
				 solution["solution","gap",index,"cylinders"]:0 );
		}
	    }
	    index = index + 1;
	});
	foreach( map e, solution["solution","gap"]:[],
		 ``{
	    if( e["exists"]:false )
	    {
		integer index = 0;
		integer pindex = e["added",0,0]:0;
		string mount = solution["partitions",pindex,"mount"]:"";
		integer fsid = Partitions::fsid_native;
		if( mount == "swap" )
		{
		    fsid = Partitions::fsid_swap;
		}
		if( solution["partitions",pindex,"id"]:0 != 0 )
		{
		    fsid = solution["partitions",pindex,"id"]:0;
		}
		foreach( map p, disk["partitions"]:[],
			 ``{
		    if( !p["delete"]:false && p["nr"]:0 == e["added",0,1]:0 )
		    {
			p["format"] = solution["partitions",pindex,"format"]:true;
			p["mount"] = mount;
			if (e["reuse"]:false)			    
			    p["used_fs"] = solution["partitions",pindex,"filesystem"]:p["detected_fs"]:`reiser;
			else
			    p["used_fs"] = solution["partitions",pindex,"filesystem"]:`reiser;
			
			value = solution["partitions",pindex,"fstopt"]:"";
			if( size(value)>0 )
			{
			    p["fstopt"] = value;
			}
			else
			{
			    p["fstopt"] = FileSystems::DefaultFstabOptions( p );
			}
			mapvalue = solution["partitions",pindex,"fs_options"]:$[];
			if( size(mapvalue)>0 )
			{
			    p["fs_options"] = mapvalue;
			}
			value = solution["partitions",pindex,"label"]:"";
			if( size(value)>0 )
			{
			    p["label"] = value;
			}
		
			
			if (solution["partitions",pindex,"loop_fs"]:false
			    || solution["partitions",pindex,"crypt_fs"]:false)
			{
			    p["loop_fs"]  =    	solution["partitions",pindex,"crypt_fs"]:false;
			    p["crypt_key"] =   	Storage::AddCryptToClassified( solution["partitions",pindex,"crypt_key"]:"");
			    p["crypt"] =	solution["partitions",pindex,"crypt"]:"twofish256";
			}
			
			if( p["fsid"]:0 != fsid )
			{
			    p["change_fsid"] = true;
			    p["ori_fsid"] = p["fsid"]:0;
			    p["fsid"] = fsid;
			}
			if (solution["partitions",pindex,"lvm_group"]:"" != "")
			{
			    Storage::SetLvmUse( true );

			    p["used_fs"]	=  `unknown;
			    p["fsid"]		=  Partitions::fsid_lvm;
			    p["format"]		=  false;
			    p["lvm_group"]	=  solution["partitions",pindex,"lvm_group"]:"";
			    p["mount"]		=  "";
			    p["fstype"]		=  "Linux LVM";
			}
			else if (solution["partitions",pindex,"raid_name"]:"" != "")
			{			   
			    p["used_fs"]	= `unknown;
			    p["fsid"]	= Partitions::fsid_raid;
			    p["format"]	= false;
			    p["raid_name"]	= solution["partitions",pindex,"raid_name"]:"";
			    p["raid_type"]   = solution["partitions",pindex,"raid_type"]:"raid";
			    p["mount"]	= "";
			    p["fstype"]	= "Linux RAID";
			}
			
			disk["partitions",index] = p;
			y2milestone( "reuse auto partition %1", p );
		    }
		    index = index + 1;
		});
	    }
	    else
	    {
		list region = [ e["start"]:0, e["end"]:0-e["start"]:0+1 ];
		map part = $[];
	
		if( e["extended"]:false && e["created"]:0 > 0 && size(e["created_primary"]:[]) == 0)
		{
		    part["create"] = true;
		    part["nr"] = e["created"]:0;
		    part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
		    part["region"] = eval(region);
		    part["type"] = `extended;
		    part["fsid"] = Partitions::fsid_extended_win;
		    part["fstype"] = Partitions::FsIdToString( part["fsid"]:0 );
		    y2milestone( "extended auto partition %1", part );
		    partitions = add( partitions, eval(part));
		}
		foreach( list a, e["added"]:[],
			 ``{
		    part = $[];
		    integer pindex = a[0]:0;
		    string mount = solution["partitions",pindex,"mount"]:"";
		    integer fsid = Partitions::fsid_native;
		    part["format"] =  solution["partitions",pindex,"format"]:true;
		    if( mount == "swap" )
		    {
			fsid = Partitions::fsid_swap;
		    }
		    if( solution["partitions",pindex,"filesystem_id"]:0 != 0 )
		    {
			fsid = solution["partitions",pindex,"filesystem_id"]:0;
			if( !haskey( solution["partitions",pindex]:$[], "filesystem" ))
			{
			    part["format"] = false;
			}
			y2milestone( "partition id %1 format %2 part %3", fsid, 
				     part["format"]:false, 
				     solution["partitions",pindex]:$[] );
		    }
		    part["create"] = true;
		    part["nr"] = a[1]:0;
		    part["device"] = Storage::GetDeviceName( dev, part["nr"]:0 );
		    region[1] = a[2]:0;
		    part["region"] = eval(region);
		    region[0] = region[0]:0 + region[1]:0;
		    part["type"] = `primary;
		    if( e["extended"]:false )
		    {
			part["type"] = `logical;
		    }
		    
		    if(contains(e["created_primary"]:[],  a[1]:0 ))
		    {
			part["type"] = `primary;
		    }
			 
		    part["mount"] = mount;
		    part["used_fs"] = 
			solution["partitions",pindex,"filesystem"]:`reiser;		    
		    value = solution["partitions",pindex,"fstopt"]:"";
		    if( size(value)>0 )
		    {
			part["fstopt"] = value;
		    }
		    else
		    {
			part["fstopt"] = FileSystems::DefaultFstabOptions( part );
		    }
		    
		    mapvalue = solution["partitions",pindex,"fs_options"]:$[];
		    if( size(mapvalue)>0 )
		    {
			part["fs_options"] = mapvalue;
		    }
		    
		    if (solution["partitions",pindex,"loop_fs"]:false
			|| solution["partitions",pindex,"crypt_fs"]:false)
		    {
			part["loop_fs"]  =    	solution["partitions",pindex,"crypt_fs"]:false;
			part["crypt_key"] =   	Storage::AddCryptToClassified( solution["partitions",pindex,"crypt_key"]:"");
			part["crypt"] =	solution["partitions",pindex,"crypt"]:"twofish256";
		    }
		    
		    value = solution["partitions",pindex,"label"]:"";
		    if( size(value)>0 )
		    {
			part["label"] = value;
		    }
		    part["fsid"] = fsid;
		    part["fstype"] = Partitions::FsIdToString( fsid );
		    if (solution["partitions",pindex,"lvm_group"]:"" != "")
		    {
			Storage::SetLvmUse( true );

			part["used_fs"]	=  `unknown;
			part["fsid"]		=  Partitions::fsid_lvm;
			part["format"]		=  false;
			part["lvm_group"]	=  solution["partitions",pindex,"lvm_group"]:"";
			part["mount"]		=  "";
			part["fstype"]		=  "Linux LVM";
		    }
		    else if (solution["partitions",pindex,"raid_name"]:"" != "")
		    {			   
			part["used_fs"]	= `unknown;
			part["fsid"]	= Partitions::fsid_raid;
			part["format"]	= false;
			part["raid_name"]	= solution["partitions",pindex,"raid_name"]:"";
			part["raid_type"]   = solution["partitions",pindex,"raid_type"]:"raid";
			part["mount"]	= "";
			part["fstype"]	= "Linux RAID";
		    }
		    
		    y2milestone( "auto partition %1", part );
		    partitions = add( partitions, eval(part));

		    if (a[1]:0 + 1 == e["created"]:0 &&  e["extended"]:false )
		    {
			part = $[];
			list ext_region = [ region[0]:0, e["end"]:0-region[0]:0+1 ];
			part["create"] = true;
			part["nr"] = e["created"]:0;
			part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
			part["region"] = ext_region;
			part["type"] = `extended;
			part["fsid"] = Partitions::fsid_extended_win;
			part["fstype"] = Partitions::FsIdToString( part["fsid"]:0 );
			y2milestone( "extended auto partition %1", part );
			partitions = add( partitions, eval(part));					
		    }
		});
		partitions = sort( map a, map b, partitions, ``(a["nr"]:0<b["nr"]:0));
	    }
	});
	disk["partitions"] = union( disk["partitions"]:[], partitions );
	y2milestone( "disk %1", disk );
	return( disk );
    }

    define map find_matching_partition_size(map gap, integer nr)
	{
	    list mg = filter(map g, gap["gap"]:[], ``(g["nr"]:-1 == nr && g["reuse"]:false));
	    y2milestone("usepart partition: %1", mg[0]:$[]);
	    return mg[0]:$[];
	}

    /**
     * Add Cylinder Information
     */
    define map add_cylinder_info( map conf, map gap )
	``{
	integer big_cyl = 4 * 1024 * 1024 * 1024; 
	integer cyl_size = gap["cyl_size"]:1;
	/*
	  // FIXME: Why is sorting needed here?
	conf["partitions"] = 
	    sort( map a, map b, conf["partitions"]:[],
		  ``({
		      if( a["max_cyl"]:big_cyl != b["max_cyl"]:big_cyl )
			  return( a["max_cyl"]:big_cyl < b["max_cyl"]:big_cyl );
		      else
			  return( a["size"]:0 > b["size"]:0 );
		  }));
	*/
	y2milestone( "parts %1", conf["partitions"]:[] );
	integer sum = 0;
	conf["partitions"] = maplist( map p, conf["partitions"]:[],
				      ``{
					  sum = sum + p["pct"]:0;
					  p["cylinders"] = (p["size"]:0+cyl_size-1)/cyl_size;
					  
					  // p["cylinders"] = (p["size"]:0)/cyl_size;

					  map mg = find_matching_partition_size(gap,p["usepart"]:0);
					  if (mg != $[])
					  {
					      p["cylinders"] = mg["cylinders"]:0;
					      p["size"] = mg["size"]:0;
					      if (p["usepart"]:0< gap["max_primary"]:0)
					      {
						  p["partition_type"] = "primary";						  
					      }
					  }
					  
					  if( p["cylinders"]:0 == 0 )
					  {
					      p["cylinders"] = 1;
					  }
					  return( p );
				      });
	y2milestone( "sum %1", sum );
	y2milestone( "parts %1", conf["partitions"]:[] );
	if( sum>100 )
	{
	    integer rest = sum - 100;
	    conf["partitions"] = maplist( map p, conf["partitions"]:[],
					  ``{
					      if( haskey( p, "pct" ) )
					      {
						  integer pct = p["pct"]:0;
						  integer diff = ((rest * pct) + sum/2) / sum;
						  sum = sum - pct;
						  rest = rest - diff;
						  p["pct"] = pct - diff;
					      }
					      return( p );
					  });
	}	
	conf["partitions"] = maplist( map p, conf["partitions"]:[],
				      ``{
					  if( haskey( p, "pct" ) )
					  {
					      integer cyl = gap["sum"]:0 / 100 * p["pct"]:0;
					      cyl = (cyl+cyl_size/2) / cyl_size;
					      if( cyl == 0 )
					      {
						  cyl = 1;
					      }
					      p["want_cyl"] = cyl;
					  }
					  if( p["max"]:0 > 0 )
					  {
					      integer cyl = (p["max"]:0+cyl_size-1) / cyl_size;
					      p["size_max_cyl"] = cyl;
					      if( p["want_cyl"]:0 > cyl )
					      {
						  p["want_cyl"] = cyl;
					      }
					  }					  
					  return( p );
				      });
	y2milestone( "parts %1", conf["partitions"]:[] );
	return( conf );
    }

    
    /**
     * Compute perfect partition list
     * @param ps Partition List from control file
     * @param g Calculated Gaps
     * @return map Best partition list using the gaps
     */
    define map get_perfect_list( list ps, map g ) {
	y2milestone( "requested partitions  %1", ps );
	y2milestone( "calculated gaps %1", g );

        /**
          * If gaps are available
          * AND (
          * extended partitions are possible and there are
          * primaries left and number of requested partitions(+1) is less than all available
          * primaries and logical slots
          * OR
          * extended is not possible and number of requested partitions is less than all
          * available primaries and logical slots )
          */
	if( size(g["gap"]:[])>0 &&
	    ((g["extended_possible"]:false && 
	      size(g["free_pnr"]:[])>0 &&
	      size(ps)+1 <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[])) ||
	     (!g["extended_possible"]:false &&
	      size(ps) <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[]))) )
	{
	    map lg = (map) eval(g);

            // prepare local gap var
	    lg["gap"] = maplist( map e, lg["gap"]:[], 
				 ``{ 
				     e["orig_cyl"] = e["cylinders"]:0;
				     e["added"] = [];
				     return( e );
				 });
	    lg["procpart"] = 0;
	    
	    list lp = (list) eval(ps);
	    
	    if( g["extended_possible"]:false &&
		size(ps)+1>=size(g["free_pnr"]:[]) &&
		size(filter(map up, (list<map>)ps, ``(
                            up["partition_type"]:"none" == "primary" || 
                            contains(lg["free_pnr"]:[], up["partition_nr"]:0) )))  == 0 
		)
	    {
		y2milestone( "creating extended" );
		integer index = 0;
		foreach( map e, lg["gap"]:[], 
			 ``{
		    if( !e["exists"]:false )
		    {
			map gap = (map) eval(lg);
			gap["gap",index,"created"] = gap["free_pnr",0]:1;
			gap["free_pnr"] = remove( gap["free_pnr"]:[1], 0 );
			gap["gap",index,"extended"] = true;
			add_part_recursive( ps, gap );
		    }
		    index = index+1;
		});
	    }
	    else
	    {
		y2milestone( "not creating extended now" );		
		add_part_recursive( ps, lg );
	    }
	}
	map ret = $[];
	if( size(cur_gap)>0 )
	{
	    ret["weight"] = cur_weight;
	    ret["solution"] = eval(cur_gap);
	    ret["partitions"] = eval(ps);
	}
	y2milestone( "ret weight %1", ret["weight"]:-1000000 );
	y2milestone( "ret solution %1", ret["solution","gap"]:[] );
	return( ret );
    }


    
    /**
     * Recursive Adding of partitions
     * @param ps Partition list from control file
     * @param g Calculated gaps
     * @return void
     */
    define void add_part_recursive( list ps, map g )
	``{
	y2milestone( "partition index %1", g["procpart"]:0 );
	y2milestone( "partitions %1", ps );
	y2milestone( "gap %1", g );

	map lg = (map) eval(g);
	integer gindex = 0;
	integer pindex = lg["procpart"]:0;
	map part = ps[pindex]:$[];
	lg["procpart"] = pindex + 1;
	y2milestone( "working on partition %1", part );
	foreach( map e, lg["gap"]:[], 
		 ``{
	    y2milestone( "start: gap section  %1", e );
	    
	    if( part["max_cyl"]:0 <= e["end"]:0 &&	
		part["cylinders"]:0 <= e["cylinders"]:0 &&
		(!e["extended"]:false && size(lg["free_pnr"]:[])>0 ||
		 e["extended"]:false && size(lg["ext_pnr"]:[])>0)
		)
	    {
		map llg = (map) eval(lg);

		list addl = [ pindex ];
		
                // number of needed primaries by user
		integer sp = size(filter(map up, (list<map>)ps,
					 ``(up["partition_type"]:"none"  == "primary" ||
                                             contains(llg["free_pnr"]:[], up["partition_nr"]:0))));

                y2milestone("number of primaries requested: %1", sp );
		integer cp = 0;
		foreach(map gg, llg["gap"]:[], ``{
		    cp = cp + size(gg["created_primary"]:[]);
		});

		boolean ex = false;
                y2milestone("number of created primaries: %1", cp );
	
		// Extended
		if( llg["extended_possible"]:false &&
		    !e["extended"]:false &&
		    size(ps)+1>=size(llg["free_pnr"]:[]) && // maybe not needed
		    cp == sp  &&   sp  > 0    )
		{
		    y2milestone( "creating extended" );
		   		   
		    map gap = (map) eval(lg);
		    llg["gap",gindex,"created"] = llg["free_pnr",0]:1;
		    llg["free_pnr"] = remove( llg["free_pnr"]:[1], 0 );
		    llg["gap",gindex,"extended"] = true;
		    // reset
		    llg["procpart"] = pindex ;
		    ex = true;
		
		}
		// Logical
		else if( e["extended"]:false ) // ||
                    /*
			 ( ps[pindex,"partition_type"]:"none" != "primary" &&
			   ps[pindex,"partition_type"]:"none" != "extended")
			 )
                         */
		{
		    if (!e["reuse"]:false)
		    {	
			addl = add( addl, llg["ext_pnr",0]:5 );
			llg["ext_pnr"] = remove( llg["ext_pnr"]:[0], 0 );
		    } else {
			addl = add( addl, e["nr"]:0 );
		    }
		}
		// Primary
		else
		{
		    if (e["exists"]:false)
		    {
			addl = add( addl, e["nr"]:0 );
			llg["gap",gindex,"created_primary"] = add(llg["gap",gindex,"created_primary"]:[],
								  e["nr"]:0 );
		    } else {
			addl = add( addl, llg["free_pnr",0]:1 );
			llg["gap",gindex,"created_primary"] = add(llg["gap",gindex,"created_primary"]:[],
								  llg["free_pnr",0]:1 );
			llg["free_pnr"] = remove( llg["free_pnr"]:[0], 0 );
		    }

		}
		
		if (!ex)
		{
		    llg["gap",gindex,"added"] = 
			add( llg["gap",gindex,"added"]:[], addl );
		}

                if (!ex) {
                    if( e["exists"]:false )
                    {
                        llg["gap",gindex,"cylinders"] = 0;
                    }
                    else
                    {
                        llg["gap",gindex,"cylinders"] = 
                            llg["gap",gindex,"cylinders"]:0 - part["cylinders"]:0;
                    }
                }
		
		if( pindex+1 < size(ps) || ex )
		{
		    add_part_recursive( ps, llg );
		}
		else
		{
		    map ng = normalize_gaps(ps, llg);
		    integer val = do_weighting( ps, ng );
		    y2milestone( "val %1 cur_weight %2 size %3", val, cur_weight, size(cur_gap));
		    if( val > cur_weight || size(cur_gap)==0 )
		    {
			cur_weight = val;
			cur_gap = (map)eval(ng);
		    }
		}
	    }
	    gindex = gindex+1;
	});
	
    };



    /**
     * Normalize Gaps
     */
    define map normalize_gaps( list ps, map g )
	``{
	y2milestone( "normalize_gaps: gap %1", g );
	integer gindex = 0;
	integer pindex = 0;
	foreach( map e, g["gap"]:[],
		 ``{
	    y2milestone( "gap section %1", e );
	    if( e["exists"]:false )
	    {
		if( size(e["added"]:[])>0 && size(e["added",0]:[])==2 )
		{
		    e["added",0] = add( e["added",0]:[], e["orig_cyl"]:1 );
		}
	    }
	    else
	    {
		integer rest = e["cylinders"]:0;
		integer needed = 0;
		integer tidx = 0;
		foreach( list p, e["added"]:[],
			 ``{
		    tidx = p[0]:0;
		    if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
		    {
			needed = needed + ps[tidx,"want_cyl"]:0 -
			    ps[tidx,"cylinders"]:0;
		    }
		});
		y2milestone( "needed %1 rest %2", needed, rest );
		if( needed > rest )
		{
		    list tr = [];
		    list weight = 
			maplist( list l, e["added"]:[], 
				 ``({
				     integer idx = l[0]:0;
				     integer d = ps[idx,"want_cyl"]:0 - 
			                 ps[idx,"cylinders"]:0;
				     if( d>0 )
				     {
					 l = add( l, ps[idx,"cylinders"]:0 );
				     }
				     tr = add( tr, l );
				     return( d>0 ? d : 0 );
				 }));
		    y2milestone( "tr %1", tr );
		    map r = $[];
		    r = distribute_space( rest, weight, tr, ps );
		    g["gap",gindex,"added"] = eval(r["added"]:[]);
		    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		    y2milestone( "partly satisfy %1 cyl %2", g["gap",gindex,"added"]:[],
				 g["gap",gindex,"cylinders"]:0 );
		}
		else
		{
		    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - needed;
		}

		pindex = 0;
		foreach( list p, g["gap",gindex,"added"]:[], 
			 ``{
		    if( size(p)<3 )
		    {
			tidx = p[0]:0;
			if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
			{
			    p = add( p, ps[tidx,"want_cyl"]:0 );
			}
			else
			{
			    p = add( p, ps[tidx,"cylinders"]:0 );
			}
			g["gap",gindex,"added",pindex] = p;
			y2milestone( "satisfy p %1 cyl %2", p, e["cylinders"]:0 );
		    }
		    pindex = pindex+1;
		});
		y2milestone( "added %1", g["gap",gindex,"added"]:[] );
	    }
	    gindex = gindex + 1;
	});
	gindex = 0;
	foreach( map e, g["gap"]:[],
		 ``{
	    if( !e["exists"]:false && e["cylinders"]:0>0 )
	    {
		list<integer> weight = maplist( list l, e["added"]:[], 
						``(ps[l[0]:0,"size"]:0==0 ? 1 : 0) );
		if( find( integer l, weight, ``(l>0) ) != nil ) 
		{
		    map r = $[];
		    r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
					  ps );
		    g["gap",gindex,"added"] = eval(r["added"]:[]);
		    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		    y2milestone( "increase max p %1 cyl %2", g["gap",gindex,"added"]:[], 
				 g["gap",gindex,"cylinders"]:0 );
		}
	    }
	    gindex = gindex + 1;
	});
	gindex = 0;
	foreach( map e, g["gap"]:[],
		 ``{
	    if( !e["exists"]:false && e["cylinders"]:0>0 && 
		e["cylinders"]:0 < g["disk_cyl"]:0/20 )
	    {
		list weight = maplist( list l, e["added"]:[], ``(l[2]:0) );
		map r = $[];
		r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], ps );
		g["gap",gindex,"added"] = eval(r["added"]:[]);
		g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		y2milestone( "close small gap p %1 cyl %2", g["gap",gindex,"added"]:[], 
			     g["gap",gindex,"cylinders"]:0 );
	    }
	    gindex = gindex + 1;
	});
	y2milestone( "gap %1", g );
	return( g );
    };


    /**
     * Distribute Spaces
     */
    define map distribute_space( integer rest, list weights, list added, list ps )
	``{
	integer diff_sum = 0;
	integer sum = 0;
	integer index = 0;
	integer pindex = 0;
	y2milestone( "rest %1 weights %2 added %3", rest, weights, added );
	foreach( list p, (list<list>)added,
		 ``{
	    pindex = p[0]:0;
	    if( ps[pindex,"size_max_cyl"]:0==0 || ps[pindex,"grow"]:false ||
		ps[pindex,"size_max_cyl"]:0 > p[2]:0 )
	    {
		sum = sum + weights[index]:0;
	    }
	    index = index+1;
	});
	index = 0;
	y2milestone( "sum %1 rest %2 added %3", sum, rest, added );
	foreach( list p, (list<list>)added,
		 ``{
	    pindex = p[0]:0;
	    if( size(p)==3 && sum>0 &&
		(ps[pindex,"size_max_cyl"]:0==0 || ps[pindex,"grow"]:false ||
		 ps[pindex,"size_max_cyl"]:0 > p[2]:0) )
	    {
		integer diff = ((rest*weights[index]:0) + sum/2) / sum;
		if( ps[pindex,"size_max_cyl"]:0>0 && 
		    !ps[pindex,"grow"]:false &&
		    diff > ps[pindex,"size_max_cyl"]:0-p[2]:0 )
		{
		    diff = ps[pindex,"size_max_cyl"]:0-p[2]:0;
		}
		sum = sum - weights[index]:0;
		rest = rest - diff;
		added[index,2] = added[index,2]:0 + diff;
		diff_sum = diff_sum + diff;
		y2milestone( "sum %1 rest %2 diff %3 added %4", sum, rest, diff, 
			     added[index]:[] );
	    }
	    index = index+1;
	});
	map ret = $[ "added":added, "diff" : diff_sum ];
	y2milestone( "ret (distribute_space) %1", ret );
	return( ret );
    }


    /**
     * Calculate plan weights
     */
    define integer do_weighting( list ps, map g )
	``{
	y2milestone( "gap %1", g["gap"]:[] );
	integer ret = 0;
	integer index = 0;
	if( cur_mode == `free )
	{
	    ret = 0;
	}
	if( cur_mode == `reuse )
	{
	    ret = ret - 100;
	}
	else if( cur_mode == `resize )
	{
	    ret = ret - 1000;
	}
	else if( cur_mode == `desparate )
	{
	    ret = ret - 1000000;
	}
	y2milestone( "weight after mode ret %1", ret );
	foreach( map e, g["gap"]:[],
		 ``{
	    y2milestone( "added %1", e["added"]:[] );
	    if( !e["exists"]:false && e["cylinders"]:0 > 0 )
	    {
		ret = ret - 5;
		if( e["cylinders"]:0 < g["disk_cyl"]:0/20 )
		{
		    ret = ret - 10;
		}
		y2milestone("weight (cyl) %1", ret );
	    }
	    y2milestone( "weight after gaps %1", ret );
	    foreach( list p, e["added"]:[], 
		     ``{
		index = p[0]:0;
		if( e["exists"]:false && ps[index,"mount"]:""=="swap" && 
		    e["swap"]:false )
		{
		    ret = ret + 100;
		    y2milestone( "weight after swap reuse %1", ret );
		}
		if( ps[index,"want_cyl"]:0>0 )
		{
		    integer diff = ps[index,"want_cyl"]:0 - p[2]:0;
		    integer normdiff = diff * 100 / p[2]:0;
		    if( diff < 0 )
		    {
			normdiff = -normdiff;
		    }
		    else if( diff > 0 )
		    {
			normdiff = normdiff / 10;
		    }
		    ret = ret - normdiff;
		    ret = ret + ps[index,"want_cyl"]:0*g["cyl_size"]:1 / 
			(100 * 1024 * 1024);
		    y2milestone( "after pct parts %1", ret );
		}
		if( ps[index,"size"]:0==0 )
		{
		    ret = ret + p[2]:0 * g["cyl_size"]:1 / (50 * 1024 * 1024);
		    y2milestone( "after maximizes parts %1", ret );
		}
		if( ps[index,"size_max_cyl"]:0 > 0 && 
		    ps[index,"size_max_cyl"]:0 < p[2]:0 )
		{
		    integer diff = p[2]:0 - ps[index,"size_max_cyl"]:0;
		    integer normdiff = diff * 100 / ps[index,"size_max_cyl"]:0;
		    ret = ret - normdiff;
		    y2milestone( "after maximal size %1", ret );
		}
	    });
	    /*
	    if( e["cylinders"]:0 > 0 )
	    {
		y2milestone("ret (before rounding): %1", ret);
		ret = ret - (e["cylinders"]:0 * g["cyl_size"]:1) / (1024*1024*1024);
		y2milestone("weight (after rounding): %1", ret);
	    }
	    */
	});
	y2milestone( "weight:  %1", ret );
	return( ret );
    };

    /**
     * Remove partitions
     * @param disk disk data
     * @param pm partitioning as in the control file.
     * @return map the new partition map with removed partitions
     */
    define map remove_possible_partitions( map disk , map pm)
    {
        boolean remove_special_partitions = pm["remove_special_partitions"]:false;
        list keep_partition_num = pm["keep_partition_num"]:[];
        list keep_partition_id = pm["keep_partition_id"]:[];
        list keep_partition_fsys = pm["keep_partition_fsys"]:[];

	// Special partitions
        list nodelpart = [ 0x12, 0xde, 257 ];

	// Handle <usepart> which is analog to create=false and partition_nr>0
	foreach(map p, pm["partitions"]:[], ``{
	    if (p["usepart"]:0 != 0 )
		keep_partition_num=add(keep_partition_num, p["usepart"]:0 );
	});

        map ret = (map)disk;
        ret["partitions"] = maplist( map p, ret["partitions"]:[],
                ``{
                integer fsid = p["fsid"]:0;
                if( (remove_special_partitions ||
                        !contains( nodelpart, fsid ) ) &&
                    p["type"]:`primary != `extended &&
                    !contains( keep_partition_num, p["nr"]:0 ) &&
                    !contains( keep_partition_id, fsid ) &&
                    !contains( keep_partition_fsys, p["used_fs"]:`none ))
                {
                    p["delete"] = true;
                    if (haskey(p, "raid_name")) {
                        p["old_raid_name"] = p["raid_name"]:"";
                        p=remove(p, "raid_name");
                    }
                }
                return( p );
                });
        integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");

        // delete extended if no logical remain
        if( size( filter( map p, ret["partitions"]:[],
                        ``(p["type"]:`primary == `extended)))>0 &&
                size( filter( map p, ret["partitions"]:[],
                        ``(p["nr"]:0>max_prim && !p["delete"]:false) ))==0 )
        {
            ret["partitions"] = maplist( map p, ret["partitions"]:[],
                    ``{
                    if( p["type"]:`primary == `extended )
                    {
                    p["delete"] = true;
                    }
                    return( p );
                    });
        }
	y2milestone("after removal: %1", ret);
        return( ret );
    }


    
/*
    define map remove_possible_partitions( map disk, map conf )
	``{
	list nodelpart = [ 0x12, 0xde, 257 ];
	map ret = (map)eval(disk);
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
				     ``{
					 integer fsid = p["fsid"]:0;
					 if( (conf["remove_special_partitions"]:false ||
					      !contains( nodelpart, fsid ) ) &&
					     p["type"]:`primary != `extended &&
					     !contains( conf["keep_partition_num"]:[], p["nr"]:0 ) &&
					     !contains( conf["keep_partition_id"]:[], fsid ) &&
					     !contains( conf["keep_partition_fsys"]:[], p["used_fs"]:`none ))
					 {
					     p["delete"] = true;
					 }
					 return( p );
				     });
	integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");
	if( size( filter( map p, ret["partitions"]:[], 
			  ``(p["type"]:`primary == `extended)))>0 &&
	    size( filter( map p, ret["partitions"]:[], 
			  ``(p["nr"]:0>max_prim && !p["delete"]:false) ))==0 )
	{
	    ret["partitions"] = maplist( map p, ret["partitions"]:[],
					 ``{
					     if( p["type"]:`primary == `extended )
					     {
						 p["delete"] = true;
					     }
					     return( p );
					 });
	}
	return( ret );
    };
*/


    /**
     * Try resizing windows partition
     *
     */
    define map try_resize_windows( map disk )
	``{
	integer cyl_size = disk["cyl_size"]:1;
	map win = $[];
	map ret = (map)eval(disk);

	ret["partitions"] = maplist( map p, ret["partitions"]:[], ``{
	    integer fsid = p["fsid"]:0;
	    if( Partitions::IsDosPartition( fsid ) ) 
	    {
		integer psize = (p["region",0]:0 + p["region",1]:1 - 1) * cyl_size;
		win = FileSystems::GetFreeSpace( p["device"]:"", psize, `fat32, 
						 false );
		y2milestone( "win=%1", win );
		if( win != nil && psize > 300*1024*1024 )
		{
		    p["winfo"] = win;
		    p["region",1] = (win["new_size"]:0 + cyl_size - 1) / cyl_size;
		    y2milestone( "win part %1", p );
		}
	    }
	    return( p );
	});
	return( ret );
    };


    /**
     * Collect gap information
     *
     */
    define list<map> get_gaps( integer start, integer end,
			       map pd,
			       list<map> part, 
			       boolean add_exist_linux )
	``{
	y2milestone("partitions: %1", pd["partitions"]:[] );
	list<integer> usepart_p =  maplist(map pa, pd["partitions"]:[],
							``{
							    return pa["usepart"]:0;
							});
	list<integer> reuse = filter(integer i, usepart_p, ``(i!=0));
	
	y2milestone("reuse: %1", reuse);
	
	y2milestone( "start %1 end %2 add_exist %3", start, end, add_exist_linux );
	list<map> ret = [];
	map entry = $[];
	foreach( map p, part, 
		 ``{	    	    
	    integer s = p["region",0]:0;
	    integer e = s + p["region",1]:1 - 1;
	    entry = $[];
	    y2milestone("Getting gap from part: %1", p );
	    y2milestone("start %1 s %2 e %3", start, s, e );
	    if( start < s )
	    {
		entry["start"] = start;
		entry["end"] = s-1;
		ret = add( ret, eval(entry) );
	    }
	    if( add_exist_linux && 
		(p["fsid"]:0==Partitions::fsid_native || 
		 p["fsid"]:0==Partitions::fsid_swap) )
	    {
		entry["swap"] = p["fsid"]:0==Partitions::fsid_swap;
		entry["start"] = s;
		entry["end"] = e;
		entry["exists"] = true;
		entry["nr"] = p["nr"]:0;
		ret = add( ret, entry );		
	    }
	    
	    if (contains(reuse,  p["nr"]:0))
	    {
		// This partition is to be used as specified in the control file
		entry["swap"] = p["fsid"]:0==Partitions::fsid_swap;
		entry["start"] = s;
		entry["end"] = e;
		entry["exists"] = true;
		entry["reuse"] = true;
		entry["nr"] = p["nr"]:0;
		ret = add( ret, entry );		
	    }
	    
	    start = e+1;
	});
	if( start < end )
	{
	    entry = $[];
	    entry["start"] = start;
	    entry["end"] = end;
	    ret = add( ret, entry );
	}
	y2milestone( "ret %1", ret );
	return( ret );
    }

    
    /**
     * Collect gap information
     * 
     */
    define map get_gap_info( map disk, map pd, boolean add_exist_linux )
	``{
	map ret = $[];
	list<map> gap = [];
	list<map> plist = filter( map p, disk["partitions"]:[], ``(!p["delete"]:false) );
	plist = sort( map a, map b, plist, ``(a["region",0]:0<b["region",0]:0) );
	list exist_pnr = sort( maplist( map e, plist, ``(e["nr"]:0) ));
	integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");
	boolean has_ext = Partitions::HasExtended( disk["label"]:"msdos" );

	// check if we support extended partitions
	if( has_ext )
	{
	    // see if disk has an extended already
	    map ext = filter( map p, plist,
			      ``(p["type"]:`primary == `extended))[0]:$[];
	    ret["extended_possible"] = size(ext)==0;
	    if( size(ext)>0 )
	    {
		gap = get_gaps( ext["region",0]:0, 
				ext["region",0]:0 + ext["region",1]:1-1,
				pd,
				filter( map p, plist, ``(p["nr"]:0>max_prim)),
				add_exist_linux );
		gap = maplist( map e, gap, 
			       ``{
				   e["extended"]=true;
				   return e;
			       });
		plist = filter( map p, plist, ``(p["nr"]:0<=max_prim));
	    }
	}
	else
	{
	    ret["extended_possible"] = false;
	}

	gap = (list<map>)union( gap, 
				get_gaps( 0, disk["cyl_count"]:1-1, pd, plist, add_exist_linux ));
	integer av_size = 0;
	gap = maplist( map e, gap,
		       ``{
			   e["cylinders"] = e["end"]:0 - e["start"]:0 + 1;
			   e["size"] = e["cylinders"]:0 * disk["cyl_size"]:1;
			   av_size = av_size + e["size"]:0;
			   return( e );
		       });
	gap = maplist( map e, gap,
		       ``{
			   e["sizepct"] = (e["size"]:0 * 201 / 2) / av_size;
			   if( e["sizepct"]:0 == 0 )
			   {
			       e["sizepct"] = 1;
			   }
			   return( e );
		       });
	ret["cyl_size"] = disk["cyl_size"]:1;
	ret["disk_cyl"] = disk["cyl_count"]:1;
	ret["max_primary"] = disk["max_primary"]:0;
	ret["sum"] = av_size;
	integer max_pnr = max_prim;
	integer pnr = 1;
	list free_pnr = [];
	y2milestone( "exist_pnr %1", exist_pnr );
	while( pnr<=max_pnr )
	{
	    if( !contains( exist_pnr, pnr ) )
	    {
		free_pnr = add( free_pnr, pnr );
	    }
	    pnr = pnr + 1;
	}
	ret["free_pnr"] = free_pnr;
	list<integer> ext_pnr = [ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
				  25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,
				  45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63];
	integer max_logical = disk["max_logical"]:15;
	if( max_logical<63 )
	{
	    ext_pnr = filter( integer i, ext_pnr, ``(i<=max_logical));
	}
	if( !ret["extended_possible"]:false )
	{
	    if( !has_ext )
	    {
		ext_pnr = [];
	    }
	    else
	    {
		integer maxext = exist_pnr[size(exist_pnr)-1]:4;
		pnr = 5;
		while( pnr<=maxext )
		{
		    ext_pnr = remove( ext_pnr, 0 );
		    pnr = pnr+1;
		}
	    }
	}
	ret["ext_pnr"] = ext_pnr;
	
	ret["gap"] = sort(map a, map b, gap, ``(a["start"]:0<b["start"]:0));
	y2milestone( "ret %1", ret );
	return( ret );
    }

}

