/**
 * Module: 		inst_resize_ui.ycp
 *
 * Authors: 		Thomas Roelz <tom@suse.de>
 *			Stefan Hundhammer <sh@suse.de>
 *
 * Purpose: 		UI for partition resizing
 *
 *
 * $Id: inst_resize_ui.ycp 20155 2004-11-03 16:35:04Z fehr $
 */
{
    textdomain "storage";

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

    include "partitioning/partition_defines.ycp";
    include "partitioning/do_proposal_flexible.ycp";

  // Automatically return if resizing is not requested
  //
  string  do_resize = Storage::GetDoResize(); // minor number (e.g. "1" for /dev/sda1 or "NO")

  if( Storage::GetCustomDisplay() || do_resize == "NO" || 
      do_resize == "no" || do_resize == "" ) 
      return Storage::GetExitKey();
  
  /////////////////////////////////////////////////////////////////////////
  // START: Initialize
  /////////////////////////////////////////////////////////////////////////

  boolean test_mode	= Mode::test ();
  boolean demo_mode	= Mode::test ();
  map	  targets	= Storage::GetTargetMap();
  string  target_is     = Storage::GetPartDisk();

  // store 
  //
  string  win_device    = "";	// will be assigned later with e.g. /dev/sda1
  
  map	  target        = $[];
  list< map<string,any> >   partitions    = [];
  list< map<string,any> >   partitions_new = [];
  map<string,any>     win_partition = $[];
  list<integer>    region        = [];


  symbol  ret           = `next;
  any     local_ret     = nil;

  integer win_used	= -1;
  integer win_free	= -1;
  integer min_win_free	= -1;
  integer new_win_size	= -1;
  integer linux_size	= -1;
  integer cyl_size      = -1;
  integer linux_min	= 400;	// this is the base value for space calculations (minimum installation)

  if ( test_mode )	// not just in demo mode! no HW probe in test mode!
  {
      win_used		= 350;
      win_free		= 1500;
      min_win_free	= 50;
      linux_size	= 800;
      linux_min		= 400;
  }
  
  /////////////////////////////////////////////////////////////////////////
  // END: Initialize
  /////////////////////////////////////////////////////////////////////////

  // ############################################################
  // Quick hack for now: define global variables that are 
  // used by create_partitions() from module auto_part_create.
  //

  integer disk_size 	= 0;
  list	  full_region 	= [];
  integer size_of_boot 	= 0;
  integer size_of_swap 	= 0;
  integer first_logical_nr = 5;

  integer max_primary	= 0;
  integer max_partitions= 0;

  include "partitioning/auto_part_functions.ycp";
  include "partitioning/auto_part_create.ycp";
  
  //
  // End quick hack
  // ############################################################

  /////////////////////////////////////////////////////////////////////////
  // START: Functions
  /////////////////////////////////////////////////////////////////////////

  // Displays a popup with the message (can be dismissed with OK).
  // After that only `abort or `back is allowed
  // Every other user action ==> redisplay message
  // Parameter: message to be displayed
  // Return   : `back or `abort
  //
  define symbol allow_back_abort_only( string message ) 
      ``{
      symbol  ret = `next;
      
      // Enable back and next buttons independent of the settings
      // in installation.ycp so the user has a chance to see the
      // popup more than only once.
      //
      Wizard::EnableNextButton();
      Wizard::EnableBackButton();

      // To avoid an empty screen behind the popup display the
      // message also on the main window
      //
      term contents = `VBox(
		       `VStretch(),
		       `Left( `Label( message ) ),
		       `VStretch()
		       );
      
      Wizard::SetContents(
			    "",		// no header in this case
			    contents,
			    "",		// no help text in this case
			    (boolean)WFM::Args(0), (boolean)WFM::Args(1)
	 );
      
      repeat {
	  Popup::Message( message );	// Display the message

	  ret= (symbol)UI::UserInput();		// get user input

	  if ( ret == `abort )
	  {
	      if ( ! Popup::ReallyAbort(true) )
	      {
		  // user didn't want to abort ==> stay in loop
		  ret = `dummy;
	      }
	  }
	  else if ( ret == `back )
	  {
	      // reset resize flag
	      Storage::SetDoResize( "NO" );
	  }
      } until ( ret == `abort || ret == `back );

      return( ret );
      };

  // Displays a popup with the message (can be dismissed with OK).
  // After that only `abort is allowed
  // Every other user action ==> redisplay message
  // Parameter: message to be displayed
  // Return   : nothing
  //
  define void allow_abort_only( string message ) 
      ``{
      symbol  ret = `next;

      // Enable back and next buttons independent of the settings
      // in installation.ycp so the user has a chance to see the
      // popup more than only once.
      //
      Wizard::EnableNextButton();
      Wizard::EnableBackButton();

      // To avoid an empty screen behind the popup display the
      // message also on the main window
      //
      term contents = `VBox(
		       `VStretch(),
		       `Left( `Label( message ) ),
		       `VStretch()
		       );
      
      Wizard::SetContents(
			    "",		// no header in this case
			    contents,
			    "",		// no help text in this case
			    (boolean)WFM::Args(0), (boolean)WFM::Args(1)
	 );

      repeat {
	  Popup::Message( message );	// Display the message

	  ret= (symbol)UI::UserInput();		// get user input

	  if ( ret == `abort )
	  {
	      if ( ! Popup::ReallyAbort(false) )
	      {
		  // user didn't want to abort ==> stay in loop
		  ret = `dummy;
	      }
	  }
      } until ( ret == `abort );
  };

  // Displays an error message and waits for the user to press OK
  // Parameter : nothing
  // Return    : nothing
  //
  define void internal_error() ``{
      // An internal error has occured. Tell the user that the installation should
      // be  terminated now and that his hard disk has not been altered yet.
      string explanation =  _("An internal error has occurred.

You cannot shrink your Windows partition during
installation. Your hard disk has not been altered.

Abort the installation now and shrink your
Windows partition by other means.
");

      allow_abort_only( explanation );
  };

  // Calculate the free space within an extended partition that lies behind
  // a certain cylinder value (including this cylinder). 
  // Parameter : List of partitions containing the extended partition.
  //             Start cylinder within an extended partition.
  // Return    : OK    -   Number of cylinders within the extended partition behind
  //                       the given start cylinder (including it).
  //		 Error -   -1
  //
  define integer get_extended_free( list<map> partitions, integer start_cylinder ) 
      ``{

      // First get all extended partitions from the list.
      //
      list mother_part = filter( map pentry, partitions,
				 ``( pentry["type"]:`dummy == `extended ));

      // Next from this subset get the partition where the region includes
      // the given start cylinder.
      //
      mother_part = filter( map pentry, partitions,
			    ``{
				integer p_start  = pentry["region",0]:0;
				integer p_length = pentry["region",1]:1;

				if ( p_start                < start_cylinder &&
				     p_start + p_length - 1 > start_cylinder    ) return true;
				else						  return false;
			    });

      if ( size(mother_part) != 1 ) return -1;	// should be exactly one

      map ext_part = mother_part[0]:$[];	// the extended partition
      integer p_start  = ext_part["region",0]:0;
      integer p_length = ext_part["region",1]:0;

      integer ext_free = p_start + p_length - start_cylinder;

      return ext_free;
  };
  
  /////////////////////////////////////////////////////////////////////////
  // END: Functions
  /////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////
  // START: Preliminary action
  /////////////////////////////////////////////////////////////////////////

  if ( ! test_mode )
  {
      // get the selected target device from the target map
      //
      target = targets[target_is]:$[];

      if ( target == $[] )
      {
	  y2error( "Current device <%1> not found in targets.", target_is );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Current device <%1> found in targets.", target_is );

      // create full device name e.g. /dev/sda1
      //
      win_device = Storage::GetDeviceName( target_is, tointeger(do_resize) );

      // get the cylinder size of this device (used later for region calculation)
      //
      cyl_size = target["cyl_size"]:-1;

      if ( cyl_size == -1 )
      {
	  y2error( "Cylinder size not found in target_data struct." );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Cylinder size of target: <%1>", cyl_size );
	  
      // get the partition list from the target map
      //
      partitions = target["partitions"]:[];

      if ( partitions == [] )
      {
	  y2error( "Partition list not found in target." );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Partition list found in target." );
      
      // Now filter out all "create" and "resize" paritions, they will be re-created.
      // (this ensures a 'clean' partition list if this dialogue is re-entered.
      // This is actually obsolete since the invention of the targets-restore-mechanism
      // but left in for safeness reasons.
      //
      partitions = filter( map<string,any> pentry, partitions, 
                           ``( !pentry["create"]:false &&
			       !pentry["resize"]:false ));

      // reset all "delete" partitions, they will be re-created
      // (this ensures a 'clean' partition list if this dialogue is re-entered.
      // This is actually obsolete since the invention of the targets-restore-mechanism
      // but left in for safeness reasons.
      //
      partitions = maplist (map<string,any> pentry, partitions, ``(add (pentry, "delete", false)));

      y2milestone( "Old partition list after cleaning: <%1>", partitions );

      // Filter out the windows partition. It will be reinserted after being modified.
      // This is the start of the new partitions list.
      //
      partitions_new = filter( map<string,any> pentry, partitions,
			    ``(pentry["nr"]:-1 != tointeger(do_resize)));

      y2milestone( "New partition list without windows partition: <%1>", partitions_new );

      // get the windows partition from the old list 
      //
      list p_list = filter( map<string,any> pentry, partitions,
			    ``(pentry["nr"]:-1 == tointeger(do_resize)));

      if ( size( p_list ) != 1 )	// there should be only one
      {
	  y2error( "There was not exactly one partition with minor <%1> in the partition list <%2>",
		   do_resize, partitions );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else  y2milestone( "Partition minor <%1> found in partition list.", do_resize );

      // assign the windows partition map
      //
      win_partition = p_list[0]:$[];

      // check if this partition is the right one
      //
      if ( Storage::GetDeviceName( target_is, win_partition["nr"]:-1) != win_device )
      {
	  y2error( "Partition from the list <%1> is not the assigned windows device <%2>.",
		   Storage::GetDeviceName( target_is, win_partition["nr"]:-1 ),
		   win_device );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Found the Windows partition in the partition list." );

      // Check file system type of windows partition
      //
      if ( ! Partitions::IsDosPartition( win_partition["fsid"]:0 ) )
      {
	  y2error( "Windows partition <%1> has wrong file system type.", win_device );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Windows partition <%1> has valid file system type - OK.", win_device );

      // Get region from win partition
      //
      region = win_partition["region"]:[];

      if ( size( region ) != 2 )
      {
	  y2error( "Invalid region <%1> in Windows partition data struct.", region );
	  internal_error();
	  return( `abort );	// abort installation
      }
      else y2milestone( "Old region <%1> OK in Windows partition data struct.", region );

      // mount the partition to execute some checks
      //

      boolean mount_result = 
	  (boolean)SCR::Execute( .target.mount, 
	                         [ win_device, Installation::scr_destdir, 
				   Installation::mountlog ]);
      if ( !mount_result )
      {
	  y2error( "Current Windows device <%1> could not be mounted. Canceled", win_device );
	  local_ret = -1;
	  internal_error();
	  return( `abort );	// abort installation
      }
      else
	{
	    y2milestone( "Current Windows device <%1> mounted on %2.", win_device, Installation::scr_destdir );
	    local_ret = 0;
	}

      // get usage information for the partition via df
      //
      list<map> df_result = (list<map>)SCR::Read(.run.df);

      SCR::Execute(.target.umount, win_device);

      y2debug( ".run.df: %1", df_result );

      // filter out headline and other invalid entries
      df_result = filter( map part, df_result, 
                          ``( substring( part["spec"]:"", 0, 1 ) == "/" ));

      foreach( map part, df_result,
	  ``{
	  if( part["spec"]:"" == win_device )	// find right entry
	      {
	      // get the usage values
	      //
	      win_used = tointeger(part["used"]:"-1");
	      win_free = tointeger(part["free"]:"-1");

	      if( win_used != -1 && win_free != -1 )
		  {
		  win_used = win_used / 1024;	// MB
		  win_free = win_free / 1024;	// MB
		  
		  y2milestone( ".run.df: win_used: <%1> win_free:<%2>", win_used, win_free );
		  }
	      }
	  });

      if ( win_used == -1 || win_free == -1 )
	  {
	  y2error( "The sizes for device <%1> could not be examined in df_result <%2>. Canceled",
		   win_device, df_result );

	  internal_error();
	  return( `abort );	// abort installation
	  }
      else
	  {
	  // Apply some checks to determine if installing Linux is feasible at all.
	  //
	  boolean feasible = true;
	  
	  // First determine the amount of free Windows space that should be left
	  // on the device to assure the usability of the remaining Windows. This
	  // can only be a rough estimate. The user will see it on screen anyway.
	  //
	  min_win_free = win_used / 5;	// start with 20% of used Windows space

	  // If this is less than 200 MB grow it to 200 MB. Running Windows with
	  // less disk space is no fun.
	  //
	  if ( min_win_free < 200 ) min_win_free = 200;
	  
	  // If this is more than the free space on the device Windows is already
	  // overcrowded and Linux shouldn't be installed.
	  //
	  if ( min_win_free > win_free ) feasible = false;

	  // Now see if the so calculated Linux space is big enough
	  //
	  if ( feasible )
	      {
	      if ( win_free - min_win_free < linux_min )
		  {
		  feasible = false;
		  }
	      else
		  {
		  // Try to reserve 1.5 GB for linux (default installation).
		  // Otherwise get as much as possible
		  //
		  if ( win_free - min_win_free > 1500 ) 
		      linux_size = 1500;
		  else
		      linux_size = win_free - min_win_free;
		  }
	      }
	  if( ! feasible )
	      {
	      y2error( "Current Windows device <%1> has not enough room for Linux. Canceled", win_device );
	      y2error( "Space calculation: win_used: <%1> win_free: <%2> min_win_free: <%3> linux_min: <%4> linux_size: <%5>",
		       win_used, win_free, min_win_free, linux_min, linux_size );
	  
	      // The Windows partition has not enough free space for Linux. Tell the user the needed amount
	      // of free space and that he should terminate the installation now.
	      string explanation =  sformat( _("An error has occurred.

Your Windows partition does not have enough free space for
the minimum installation.

To install Linux anyway, boot Windows and uninstall some 
applications or delete some data.

You need at least %1 megabytes of free space on the
Windows device, including Windows workspace and
space for %2.
"), linux_min + min_win_free + 10, Product::name );	// 10 MB safety overhead
					      
	      return( allow_back_abort_only( explanation ) );
	  }
	  else
	  {
	      y2milestone( "Space calculation: win_used: <%1> win_free:<%2> min_win_free: <%3> linux_min: <%4> linux_size: <%5>",
		       win_used, win_free, min_win_free, linux_min, linux_size );
	  }
      }
  
      // Do a dosfsck on this partition to assure the file system is clean.
      // Do this only if not yet checked.
      //
      if ( ! Storage::GetWinDevice() )		// not yet checked
      {
	  // Inform the user that his Windows partition is being checked.
	  string explanation = _("The file system of your windows partition is being
checked for consistency.

Depending on the size of your Windows partition
and the amount of space used, this may take a while.

Please be patient.
");
      
	  UI::OpenDialog(`opt(`decorated), `VBox(`Label(explanation)));
	  string cmd = "/usr/sbin/parted -s " + target_is + " check " + do_resize;

	  y2milestone ("running: %1", cmd );
	  local_ret = SCR::Execute(.target.bash, cmd );
	  
	  UI::CloseDialog();
	  
	  if ( local_ret != 0 )
	  {
	      y2error( "Current Windows device <%1> had errors with parted check. Canceled", win_device );
	      
	      // The file system on the device is faulty. Tell the user he should correct those errors.
	      string explanation =  _("An error has occurred!

Your Windows partition has errors in the file system.

Please boot Windows and clear those errors by running
\"scandisk\" and \"defrag\".

If the problem occurs again next time, please shrink your
Windows partition by other means.
");
					      
	      return( allow_back_abort_only( explanation ) );
	  }
	  else	// OK
	  {
	      y2milestone( "Current Windows device <%1> was OK with parted check.", win_device );
	      Storage::SetWinDevice( true );
	  }
      }	// end of not yet checked
  }	// not test mode

  
  /////////////////////////////////////////////////////////////////////////
  // END: Preliminary action
  /////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////
  // START: GUI
  /////////////////////////////////////////////////////////////////////////

  boolean test_simple_ui = false;	// set to "true" to test non-graphical version

  // Unit for parition resizing - currently Megabytes
  string  unit = _("MB");;

  // Labels for bar graph. "%1" will be replace with a numeric value.
  string bargraph_label_win_used	= _("Windows\nused\n%1 ") + unit;
  // Labels for bar graph. "%1" will be replace with a numeric value.
  string bargraph_label_win_free	= _("Windows\nfree\n%1 ") + unit;
  // Labels for bar graph. "%1" will be replace with a numeric value.
  string bargraph_label_linux		= _("Linux\n%1 ") + unit;

  // Labels for input fields. "%1" will be replaced with the current unit (MB).
  string field_label_win_free		= sformat( _("Windows free (%1)"), unit);
  // Labels for input fields. "%1" will be replaced with the current unit (MB).
  string field_label_linux		= sformat( _("Linux (%1)"), unit);

  term contents = `Empty();



  // Help text for Windows partition resizing -
  // common part for both graphical mode (with bar graphs)
  // and non-graphical mode (text only).
  string helptext = _("<p>
Choose the new size for your Windows partition.
</p>");

  // help text (common to both modes), continued
  helptext = helptext + _("
<p>
The actual resizing will be performed only after you confirm all your
settings in the last installation dialog. Until then, your Windows
partition will remain untouched.
</p>");

  // help text (common to both modes), continued
  helptext = helptext + _("
<p>
To skip resizing your Windows partition, press
<b>Back</b>.
</p>
");


  if ( UI::HasSpecialWidget(`Slider   ) &&
       UI::HasSpecialWidget(`BarGraph ) &&
       ! test_simple_ui )
  {
      contents = `VBox(
		       `VStretch(),
		       // Headline above bar graph that displays current windows partition size
		       `Left( `Label( _("Now:")) ),
		       `BarGraph(
				  [ win_used, win_free ],
				  [
				   bargraph_label_win_used,
				   bargraph_label_win_free
				  ]
				  ),
		       `VStretch(),

		       // Headline above bar graph that displays future windows and linux partitions
		       `Left( `Label( _("After installation:") ) ),
		       `PartitionSplitter( `id(`linux_size),
					   win_used, win_free,
					   linux_size, linux_min, min_win_free,
					   bargraph_label_win_used,
					   bargraph_label_win_free,
					   bargraph_label_linux,
					   field_label_win_free,
					   field_label_linux
					   ),
		       `VStretch()
		       );


      // help text, continued - graphical mode only
      // this help text will be appended to the help text common to both modes.
      helptext = helptext + _("
<p>
The upper bar graph displays the current situation.
The lower bar graph displays the situation after the installation -- after
the partition resize.
</p>
");
      
      // help text (graphical mode), continued
      helptext = helptext + _("
<p>
Drag the slider or enter a numeric value in either
input field to adjust the suggested value.
</p>");
      
      // help text (graphical mode), continued
      helptext = helptext + _("
<p>
Within the space you reserve for Linux, partitions will automatically be
created as necessary.
</p>");
  }
  else	// no special widgets -> simple fallback UI
  {
      contents = `HVSquash(
			   `VBox(
				 `HBox(
				       // Label for used part of the Windows partition in non-graphical mode
				       `HWeight(3, `Right(`Label(_("Windows used:")))),
				       `HWeight(2, `Label(`opt(`outputField), sformat("%1", win_used ))),
				       `HWeight(3, `Left(`Label( unit )))
				       ),
				 `VSpacing(0.5),
				 `HBox(
				       // Label for free part of the Windows partition in non-graphical mode
				       `HWeight(3, `Right(`Label(_("Free:")))),
				       `HWeight(2, `Label(`opt(`outputField), sformat("%1", win_free ))),
				       `HWeight(3, `Left(`Label( unit )))
				       ),
				 `VSpacing(0.5),
				 `HBox(
				       // Edit field label for linux partition size in non-graphical mode
				       `HWeight(3, `Right(`Bottom(`Label(_("Linux:"))))),
				       `HWeight(2, `IntField( `id(`linux_size),
							      "",			// label (above)
							      linux_min,		// min
							      win_free - min_win_free,	// max
							      linux_size		// initial
							      )
						),
				       `HWeight(3, `Left(`Bottom(`Label( unit ))))
				       )
				 )
			   );

      // help text, continued - non-graphical mode only
      // this help text will be appended to the help text common to both modes.
      helptext = helptext + _("
<p>Enter a value for the size of your <b>Linux</b> installation.
The partitions will automatically be created within this range
as required for &product;.
</p>
");
      
      // help text (non-graphical mode), continued
      helptext = helptext + _("
<p>
<b>Windows used</b> is the size of the used part of your Windows partition.
</p>");

      // help text (non-graphical mode), continued
      helptext = helptext + _("
<p><b>Free</b> indicates the current free space (before the Linux
installation) on the partition.
</p>");
  }


  Wizard::SetContents( // Set the dialog header
		       _("Resizing the Windows partition"),
		       contents, helptext, (boolean)WFM::Args(0), 
		       (boolean)WFM::Args(1) );


  /////////////////////////////////////////////////////////////////////////
  // END: GUI
  /////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////
  // START: Main loop
  /////////////////////////////////////////////////////////////////////////

  repeat {
      ret = (symbol)Wizard::UserInput();

      if( ret == `abort && Popup::ReallyAbort(true) )
	  return `abort;

      if( ret == `next )
	  {
	  // Get the value the user adjusted. If s/he entered a value
	  // too big or too small this is automatically adjusted to the
	  // biggest/smallest value possible (by Qt).
	  //
	  linux_size   = (integer)UI::QueryWidget(`id(`linux_size), `Value);
	  new_win_size = win_used + win_free - linux_size;
	  
	  y2milestone( "Linux size: <%1> - New Win size: <%2>", 
	               linux_size, new_win_size);
	  }

      if( ret == `back )
	  {
	  // reset resize flag
	  Storage::SetDoResize( "NO" );
	  return `back;
	  }
      } until ( ret == `next || ret == `back || ret == `cancel );
  Storage::SaveExitKey( ret );

  /////////////////////////////////////////////////////////////////////////
  // END: Main loop
  /////////////////////////////////////////////////////////////////////////

  /////////////////////////////////////////////////////////////////////////
  // START: Final action
  /////////////////////////////////////////////////////////////////////////

  // Now update the target map to the new situation
  //
  if( !test_mode )
      {
      // adjust the partition entry in the target map to reflect the new size

      // add flag and new size to the windows partition
      //
      win_partition = add( win_partition, "resize",     true );
      win_partition = add( win_partition, "new_size",   new_win_size );

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

      // adjust the region list in the windows partition to reflect the new size
      //
      integer win_start    = region[0]:0;		// same as before resize
      integer new_length_i = PartedSizeToCly( (tofloat(new_win_size) * 1024.0 * 1024.0), cyl_size );

      region = [win_start, new_length_i];
      win_partition = add( win_partition, "region", region);

      y2milestone( "New region of Windows partition after resize: <%1>", region );
      
      // Insert the altered windows partition into the new cleaned partition list.
      //
      partitions_new = add( partitions_new, win_partition );

      y2milestone( "New partition list with altered windows partition: <%1>", partitions_new );

      // Create a pseudo partition for the rest of the available space (calculate this space).
      // There are two cases to be handled different:
      // 
      // 1. The Windows partition is a primary partition (i.e. is _not_ embedded into an extended)
      //    Here we can use the whole space up to the end of the disk.
      //    ATTENTION:
      //    This will ruthlessly consume the rest of the disk regardless of what there might be.
      //    This is OK at the moment because we allow only the last partition on the resizeable disk.
      //    If we ever want to be able to resize a Windows partition embedded between other
      //    partitions this must be replaced with a (much) more intelligent mechanism.
      //
      // 2. The Windows partition is a logical partition within an extended partition.
      //    Here we can only use the rest of the extended partition since it is (currently) not
      //    possible to resize the extended partition.
      //    ATTENTION:
      //    This will ruthlessly consume the rest of the ext. partition regardless of what there might be.
      //    This is OK at the moment because we allow only the last partition on the resizeable disk.
      //    If we ever want to be able to resize a Windows partition embedded between other
      //    partitions this must be replaced with a (much) more intelligent mechanism.
      //

      // The start of the pseudo partition is the same in both cases.
      //
      integer pseudo_start  = win_start + new_length_i;

      // The length of the pseudo partition must be ascertained in different ways.
      //
      integer pseudo_length = -1;	// init
      
      if ( win_partition["type"]:`dummy == `primary ) // Win partition is primary
      {
	  y2milestone( "Windows partition is primary" );
	  pseudo_length = target["cyl_count"]:0 - win_start - new_length_i;
      }
      else if ( win_partition["type"]:`dummy == `logical ) // Win partition is logical within extended
      {
	  y2milestone( "Windows partition is logical within extended" );
	  pseudo_length = get_extended_free( partitions_new, pseudo_start );

	  if ( pseudo_length == -1 )
	  {
	      y2error( "The length of the free space in the extended partition could not be determined." );
	      internal_error();
	      return( `abort );	// abort installation
	  }
      }
      else	// type of win partition could not be detected
      {
	  y2error( "The Type of the Windows partition could not be ascertained." );
	  internal_error();
	  return( `abort );	// abort installation
      }


      // Assemble new region to consumed by linux
      //
      region = [pseudo_start, pseudo_length];
      y2milestone( "Region of new pseudo partition after resize: <%1>", region );

      partitions_new = add (partitions_new, $[ "type":`free, "region": region]);

      y2milestone( "New partition list with pseudo partition: <%1>", partitions_new );

      // now let the automatic partitioner do its work
      //

      disk_size 	= target["cyl_count"]:1;
      full_region 	= [ 0, disk_size ];
      size_of_boot 	= Partitions::MinimalNeededBootsize();
      size_of_swap 	= 1024*1024*Partitions::SwapSizeMb(0);

      max_primary	= target["max_primary"]:0;
      max_partitions    = compute_max_partitions (target);

      if( has_flex_proposal() )
	{
	map tt = (map)eval(target);
	tt["partitions"] = filter( map p, partitions_new,
				   ``(p["type"]:`none!=`free));
	map flex = do_flexible_disk( target_is, tt );
	if(flex["ok"]:false)
	    {
	    partitions_new = flex["disk","partitions"]:[];
	    }
	else
	    {
	    Popup::Message( _("The available space is not sufficient for an installation.") );
	    ret = `cancel;
	    }
	}
      else
	{
	partitions_new = create_partitions( target, partitions_new, region );
	}
      
      y2milestone( "New partition list with installation partitions: <%1>", partitions_new );
      
      // partition list complete ==> replace in target
      //
      target = add( target, "partitions", partitions_new );

      // target up to date ==> replace in targets
      //
      targets = add( targets, target_is, target );

      // targets up to date == replace in Storage module
      //
      Storage::SetTargetMap( targets );
  }
  
  /////////////////////////////////////////////////////////////////////////
  // END: final action
  /////////////////////////////////////////////////////////////////////////

  return ret;
}
