using System;
using System.Runtime.InteropServices;
using System.IO;

namespace Addison_Wesley.Codebook.Filesystem
{
   public class FileUtil
   {
      /* Deklarace nezbytných funkcí, struktur a konstant z API */
      [DllImport("shell32.dll", CharSet = CharSet.Auto)]
      private static extern int SHFileOperation(ref SHFILEOPSTRUCT fileOp);

      private const int FO_MOVE = 0x0001;            // Přepsat
      private const int FOF_ALLOWUNDO = 0x0040;     // Umožní undo 
      private const int FOF_NOCONFIRMATION = 0x0010; // Bez dotazu

      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
      private struct SHFILEOPSTRUCT
      {
         public IntPtr hwnd;
         public int wFunc;
         public string pFrom;
         public string pTo;
         public short fFlags;
         [MarshalAs(UnmanagedType.Bool)]
         public bool fAnyOperationsAborted;
         public IntPtr hNameMappings;
         public string lpszProgressTitle;
      }

      /// <summary>
      /// Přesune soubor
      /// </summary>
      /// <param name="source">Cesta ke zdrojovému souboru resp. ke zdrojovému adresáři</param>
      /// <param name="dest">Cesta k cíli</param>
      /// <param name="confirmOverwrites">Udává, zda má být přepis souborů,
      /// které již v cílovém adresáři jsou, potvrzeno uživatelem</param>
      /// <returns>Vrátí true, když se přesun podařil</returns>
      /// <remarks>
      /// <para>
      /// Při přesouvání (a kopírování) složek se funkce SHFileOperation 
      /// chová poněkud nezvykle: Pokud ještě cílová složka s udanou 
      /// cílovou cestou neexistuje, přesune se zdrojová složka do složky, 
      /// která je v zadané cílové cestě uvedená jako nadřazená, a je 
      /// pojmenována jako poslední složka v cílové cestě. Toto je očekávané 
      /// chování. 
      /// Jestliže však již nějaká složka se zadanou cílovou cestou existuje, 
      /// přesune se zdrojová složka v této složce tak, že se stane 
      /// podsložkou. Jestliže například existuje složka 
      /// C:\Temp\DemoFolder a přesouváte složku C:\DemoFolder do 
      /// C:\Temp\DemoFolder, bude tato složka přesunuta do složky 
      /// C:\Temp\DemoFolder\DemoFolder. Jestliže C:\Temp\DemoFolder ještě 
      /// při přesouvání neexistuje, bude složka korektně přesunuta 
      /// do složky C:\Temp\DemoFolder. Proto byste měli dbát na to, 
      /// abyste při přesouvání složek jako cíl vždycky zadali cestu 
      /// ke složce, do níž se má přesouvaná složka zkopírovat (tj. 
      /// cestu k nadřazené složce). To bohužel vede k tomu, že složku 
      /// nemůžete při přesouvání přejmenovat. 
      /// </para>
      /// </remarks>
      public static bool MoveFileOrFolder(string source, string dest,
         bool confirmOverwrites)
      {
         // Vytvoření struktury pro informace o souboru
         SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT();

         // Definice (pod)funkce (ShFileOperation může také 
         // ruřit a kopírovat soubory a adresáře)
         fileOp.wFunc = FO_MOVE;

         // Definuje zdroj a cíl. Je-li zadáno více souborů nebo složek, 
         // musí se oddělit znaky 0.
         // Další 0 musí být na konci 
         fileOp.pFrom = source + "\x0\x0";
         fileOp.pTo = dest + "\x0\x0";

         // Nastaví příznaky, aby bylo možné stornování (což však, jak se 
         // zdá, při přesouvání zatím ještě není podporováno!)
         // a – podle argumentu confirmOverwrites – se nevyžaduje při 
         // přepisování složek k potvrzení od uživatele 
         if (confirmOverwrites)
         {
            fileOp.fFlags = FOF_ALLOWUNDO;
         }
         else
         {
            fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
         }

         // Volání SHFileOperation a předání struktury
         int result = SHFileOperation(ref fileOp);

         // Vrácená hodnota: úspěch nebo nezdar
         return (result == 0);
      }
   }
}
