/*
 *************************************************************
 *
 *     YaST2      SuSE Labs                        -o)
 *     --------------------                        /\\
 *                                                _\_v
 *           www.suse.de / www.suse.com
 * ----------------------------------------------------------
 *
 * Author:        Michael Hager <mike@suse.de>
 *
 * Description:   utility for create_target, for parttition handling
 *
 *
 *************************************************************

 $Id: propose_part_util.ycp 20155 2004-11-03 16:35:04Z fehr $


*/
{
  import "Arch";
  import "Mode";
  import "Partitions";
  import "Storage";

  textdomain "storage";


  //////////////////////////////////////////////
  // Insert in the existing partition:
  //  "resize"     : true,	// do resize this partition
  //  "new_size"   : 1000,	// if new size is 1000 MB
  //  "ori_length" : 722,	// if ori_length of partition before resize was 722 cyl

define list resize_partition( list<map> partitions, integer nr,
			      integer win_length,  // in cyl
			      integer win_size,    // in byte
			      integer win_max_length ) // in cyl
    ``{

    map win_part = filter( map c_part, partitions,
			   ``(c_part["prop_reuse_nr"]:-1==nr &&
			      !c_part["delete"]:false))[0]:$[];

    list other_parts = filter( map c_part, partitions,
			       ``( c_part["prop_reuse_nr"]:-1!=nr ||
			           c_part["delete"]:false ));

    list region = win_part["region"]:[];

      // save the original length of windows partition, if it is not already saved
    if( win_part["ori_length"]:-1 == -1 )
	{
	win_part["ori_length"] = region[1]:0;
	}

      win_part["resize"] = true;
      win_part["new_size"] = win_size / (1024*1024);
      region      = [ region[0]:0, win_length ];
      win_part["region"] = region;
      win_part["win_max_length"] = win_max_length;

      if ( win_part == $[] || win_part == nil || nr == -1 || win_length == -1)
      {
	  // ERROR
	  y2error("Resize %1 %2 %3 %4", nr, win_length, win_size, win_part );
	  hiterror = true;
	  return( partitions );
      }
      else
      {
         // merge changed prtition into list:
         return( add( other_parts, win_part ));
      }
  };




  //////////////////////////////////////////////
  // Setup a partition for a slot
  // - delete an existing partition
  //
  // therefore we edit an entry in the targetmap
  // like this
  // $[
  //   "delete":true,
  //   "fsid":131,
  //   "nr":1,		 
  //   "format":true
  //   "region":[0, 17],
  //    ..

define list delete_partition( list<map> partitions, integer nr )
  ``{

      map part = filter( map c_part, partitions,
			 ``( c_part["prop_reuse_nr"]:-1 == nr &&
			     !c_part["delete"]:false))[0]:$[];

      list<map> other_parts = filter( map c_part, partitions,
				      ``( c_part["prop_reuse_nr"]:-1 != nr ||
					  c_part["delete"]:false));

      part = add( part, "mount", "");
      part = add( part, "delete", true);


      if ( part == $[] || part == nil || nr == -1)
      {
	  // ERROR
	  y2error("delete partition %1 %2 ", nr, part );
	  hiterror = true;
	  return( partitions );
      }


      ///////////////////////////////////////////////
      // renumber logical values
      partitions      = add( other_parts, part );
      integer real_nr = part["nr"]:99;

      if ( part["type"]:`unknown == `logical )
      {
	  partitions = maplist( map parti, partitions , ``(
	  {
	      // only the valid ones
	      if ( !parti["delete"]:false &&  parti["nr"]:0 > real_nr )
	      {
		  return( add( parti, "nr",  parti["nr"]:1-1  ));
	      }
	      else
	      {
		  return( parti );
	      }
	  }));
      }


      if ( part == $[] || part == nil || nr == -1)
      {
	  // ERROR
	  y2error("delete partition %1 %2 ", nr, part );
	  hiterror = true;
	  return( partitions );
      }
      else
      {
	  // merge changed prtition into list:
	  return( partitions );
      }
  };


  //////////////////////////////////////////////
  // Setup a partition for a slot
  // - reuse an existing partition
  //
  // therefore we edit an entry in the targetmap
  // like this
  // $[
  //   "fsid":131,
  //   "mount":"/",
  //   "nr":1,
  //   "format":true
  //   "region":[0, 17],
  //   "type":`primary
  //   "used_fs": `ext2

define list reuse_partition( list<map> partitions, integer nr, string mount )
  ``{
      map part = filter( map c_part, partitions,
			 ``( c_part["prop_reuse_nr"]:-1 == nr &&
			     !c_part["delete"]:false))[0]:$[];

      list other_parts = filter( map c_part, partitions,
				 ``( c_part["prop_reuse_nr"]:-1 != nr ||
				     c_part["delete"]:false));

      ////////////////////////////////////////////////
      // suggest the default filesystem,

      if ( mount != "swap" && mount != "" )
	  {
	  if( mount == Partitions::BootMount() )
	      {
	      part["used_fs"] = Partitions::DefaultBootFs();
	      }
	  else
	      {
	      part["used_fs"] = Partitions::default_fs;
	      }
	  }

      part["mount"] = mount;
      part["format"] = true;
      part["fstopt"] = FileSystems::DefaultFstabOptions( part );

      if ( part == $[] || part == nil || nr == -1 )
      {
	  // ERROR
	  y2error("reuse partition %1 %2 %3 ", nr, mount, part );
	  hiterror = true;
	  return( partitions );
      }
      else
      {
	  // merge changed partition into list:
	  return( add( other_parts, part ));
      }
  };




  //////////////////////////////////////////////
  // Setup a partition for a slot
  // that means
  // - create a partition in a slot
  //
  // therefore we create an entry in the targetmap
  // $[
  //   "create":true,		    // mode win and free but not if reuse
  //   "fsid":131,
  //   "mount":"/",
  //   "nr":1,
  //   "format":true
  //   "region":[0, 17],
  //   "type":`primary
  //   "used_fs": `ext2

define list create_partition( list<map>    partitions, integer start_cyl,
			      integer end_cyl, 
			      integer nr,  // ignore if logical!
			      string  mount,
			      string  extended,
			      integer max_primary )
    ``{
    symbol fs     = Partitions::default_fs;  
    integer fsid  = Partitions::fsid_native;

    if( mount == Partitions::BootMount() )
	{
	fs     = Partitions::DefaultBootFs();
	fsid   = Partitions::FsidBoot();
	}
    if ( mount == "swap" )
	{
	fs     = `swap;
	fsid   = Partitions::fsid_swap;
	}

    string fstype = Partitions::FsIdToString( fsid );

    map part = $[];

    if( extended == "extended" )
	{
	//////////////////////////////////////////////////
	// mode win or free: we have to create a new partition
	part = $[
		  "create" : true,
		  "fsid"   : Partitions::fsid_extended_win,
		  "nr"     : nr,
		  // short description for extended partition
		  "fstype" : 
		      Partitions::FsIdToString( Partitions::fsid_extended_win ),
		  "region" : [start_cyl, end_cyl-start_cyl+1 ],
		  "type"   : `extended,
		  "mount"  : mount,
		];
	}
    else
	{
	symbol type = `primary;
	
	if ( nr > max_primary )
	    {
	    // due to the fact, we have deleted one partition, "nr" is only 
	    // a first guess now lets see wich real number we have to create

	    type = `logical;

	    list<integer> base = [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,64];

	    list<integer> dev_nb = maplist( map part, 
	                           filter( map pa, partitions, 
				           ``(!pa["delete"]:false )), 
				   ``(part["nr"]:0 ));
	    list result = filter( integer base_nr, base, 
	                          ``( find( integer nr, dev_nb, 
				           ``(nr == base_nr)) == nil));

	    nr = sort( result )[0]:0;
	    y2debug( "New logical nr: %1", nr );
	    }

	//////////////////////////////////////////////////
	// mode win or free: we have to create a new partition
	part = $[
		     "create" : true,
		     "fsid"   : fsid,
		     "nr"     : nr,
		     "region" : [start_cyl, end_cyl-start_cyl+1 ],
		     "type"   : type,
		     "fstype" : fstype,
		     "used_fs": fs,
		     "mount"  : mount,
		     "format" : true
	    ];
	}

    part["fstopt"] = FileSystems::DefaultFstabOptions( part );
    if( part == $[] || part == nil || start_cyl == -1 || end_cyl == -1 || 
        nr == -1   )
	{
	// ERROR
	y2error( "create partition %1 %2 %3 %4 %5", nr, mount, start_cyl,
	         end_cyl, part );
	hiterror = true;
	return( partitions );
	}
    else
	{
	return( add( partitions, part ));
	}
    };



  ///////////////////////////////////////////////////
  // Add a possible partition model to the hit list
  /*
   mode is    reuse, free or win_resize
   type is    for debugging only
   I always need a main slot called slot!

   --------------------------------
   three kinds:
   - <nothing>    : slot becomes root partion and thats it
   - boot_split   : slot will be splited in two partitions
                    a boot slot and a root slot
   - have         : i need a boot slot and its not done by splitting the main slot
   --------------------------------

   slot:     the main slot
   boot_slot the boot_slot if have mode

   extd:     "extd" or "no_extd" if I need one

   boot_split means: main_slot is splited to create boot_partition

   root_split: flag if I can create my root_partition in a newly created
               extended. Then I can create easiely a swap partition ...
	       If I have a reusable swap partition I will NOT split 

   ------------------------------------------------
   generating the partition numbers
   boot_nr     = interface
   nr          = interface
   slotnr      = lookup( slot, "nr")
   bootslotnr  = lookup( boot_slot, "nr")

   a) .. d) see propose_create_target::HitToTargetMap algorith



   ### SPLIT ############   boot_part           root_part     extd
   ---------------------------------------------------------------
   reuse slot edt           c) 6                c) 5          c) slotnr
   reuse slot no edt        c) boot_nr          c) slotnr     --
			     		        
   free slot edt            c) 6                c) 5          c) nr   == win_resize
   free slot no edt         c) boot_nr          c) nr         --      == win_resize

   
   ### HAVE #############      boot_part        root_part     extd
   ---------------------------------------------------------------
   ru    boot-ru  no_extd   a) boot-slotnr      b) slotnr     --
   free  boot-ru  no_extd   a) boot-slotnr      b) nr         --
   ru    free     no_extd   a) boot_nr          b) slotnr     --
   free  free     no_extd   a) boot_nr          b) nr         --

   ru    boot-ru  extd      a) boot-slotnr      d) 5          d) slotnr
   free  boot-ru  extd	    a) boot-slotnr      d) 5          d) nr
   ru    free     extd	    a) boot_nr(extd?)   d) 5          d) slotnr
   free  free     extd	    a) boot_nr(extd?)   d) 5          d) nr



   NOTH ################                        root_part    extd
   ---------------------------------------------------------------
   reuse slot edt            	              	d) 5          d) slotnr
   reuse slot no edt                            b) slotnr     --
			     
   free slot edt                              	d) 5          d) nr    == win_resize
   free slot no edt                           	b) nr         --       == win_resize

   ------------------------------------------------------------------------

   !!! have-mode + no_root_split + extd is not supported!
       cause: root_split == create extd

   if boot_split -> root_split is implizit true


   */

define void AddHit( string mode, map slot, string type, map boot_slot,
		    integer nr, string extd, string boot_split,
		    string need_boot, integer boot_nr, string root_split,
		    map swap_slot,        // $[] or a valid one
		    map boot_resue_slot,  // $[] or a valid one
		    integer poss_swap_nr,     // -1 or a valid swap nr
		    integer free_primary )
  ``{
      map hit = $[ "mode"    	: mode,
		 "slot"      	: slot,
		 "type"      	: type,           // "2p-nE-2pri-split"
		 "boot_slot" 	: boot_slot,
		 "swap_slot" 	: swap_slot,      
		 "boot_r_slot" 	: boot_resue_slot,
                 "nr"		: nr,
		 "extd"		: extd == "extd",
		 "boot_split"	: boot_split == "boot_split",
		 "need_boot"	: need_boot  == "need_boot",
		 "boot_nr"	: boot_nr,
		 "root_split"   : root_split == "root_split",
		 "swap_slot_nr" : poss_swap_nr,
		 "free_primary" : free_primary
      ];
      y2milestone( "hit %1", hit );

      hitlist = add( hitlist, hit);
  };



    /*---------------------------------------------------------------------
     * Dumppartitiontabel for debug
     *----------------------------------------------------------------------
     */

define void DumpPartitionList ( map<string,map> targets)
    ``{
	foreach ( string disk_dev_name, map disk, targets, ``{
	    integer cyl_count  = disk["cyl_count"]:0;
	    integer cyl_size   = disk["cyl_size"]:1000000;
	    string  vendor     = disk["vendor"]:"";
	    string  model      = disk["model"]:"";

	    y2debug( "OUT: %1 has %2cyl, cylsize: %3 KB", disk_dev_name, cyl_count,  cyl_size/1000 );

	    if ( !disk["is_lvm_vg"]:false && find( disk_dev_name, "/dev/evms" )!=0 )
		{
		foreach ( map partition, disk["partitions"]:[], ``{
		    string fstype = partition["fstype"]:"";
		    if ( !partition["delete"]:false )
			{
			string id = "";
			if( partition["nr"]:0 < 10 )
			    {
			    id = sformat( "0%2.%1", disk_dev_name, partition["nr"]:0);
			    }
			else
			    {
			    id = sformat( "%2.%1", disk_dev_name, partition["nr"]:0);
			    }

			string   part_dev_name   = "";
			string   part_id         = "";

			if ( !Arch::s390 () )
			    {
			    part_dev_name = Storage::GetDeviceName( disk_dev_name, partition["nr"]:0 );
			    }
			else
			    {
			    // s390:
			    part_dev_name = substring( disk_dev_name, 5);
			    part_id = sformat( " (%1)", disk["bios_id"]:"");
			    }


			////////////////////////////////////////////////////////////
			// Format other Colums:

			integer  start_cyl       = partition["region",0]:0;
			integer  nb_cyl          = partition["region",1]:0;
			symbol   type            = partition["type"]:`unknown;
			boolean  format          = partition["format"]:false;
			boolean  crypt_fs        = partition["loop_fs"]:false &&
			                           partition["crypt"]:""!="none";
			symbol   used_fs         = partition["used_fs"]:`unknown;
			string   mount_point     = partition["mount"]:"";
			string   filesystem_name = "";

			filesystem_name = partition["fstype"]:"";

			string   format_flag      = "";
			if ( format )   format_flag = "F";
			if ( crypt_fs ) format_flag = "C" + format_flag;

			string bytes_of_part = "";
			bytes_of_part = ByteToHumanString(( nb_cyl ) * cyl_size);

			term a = `item(`id(id));

			string sta_str = ToLen(4, sformat("%1", start_cyl));
			string end_str = ToLen(4, sformat("%1", start_cyl + nb_cyl - 1) );
			y2debug( "OUT: %1 %2 %3 %4 %5", part_dev_name, part_id, bytes_of_part, sta_str, end_str);
		    }}
			  );
	    }
	});
    };


}


