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

namespace Addison_Wesley.Codebook.Filesystem
{
   public class FolderUtil
   {

      /// <summary>
      /// Deklarace funkce SHFileOperation z API Windows
      /// </summary>
      [DllImport("shell32.dll", CharSet = CharSet.Auto)]
      static extern int SHFileOperation(ref SHFILEOPSTRUCT fileOp);

      /* Konstanty pro SHFileOperation (z ShellAPI.h) */
      // kopírování souboru nebo adresáře
      private const int FO_COPY = 0x0002;
      // Umožní odvolání
      private const int FOF_ALLOWUNDO = 0x0040;
      // Bez dotazu k uživateli
      private const int FOF_NOCONFIRMATION = 0x0010;

      /// <summary>
      /// Struktura pro definici operace se souborem
      /// </summary>
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
      public 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>
      /// Kopíruje adresář pomocí sunkce z API 
      /// </summary>
      /// <param name="sourceFolderPath">Cesta ke zdrojovému adresáři</param>
      /// <param name="destFolderPath">Cesta k cílovému adresáři</param>
      /// <param name="confirmOverwrites">Udává, zda má být přepsání adresáře nebo 
      /// souboru potvrzeno uživatelem</param>
      /// <exception cref="IOException">Vznikne, když neexistuje adresář
      /// nadřízený cílovému adresáři, když zdrojový adresář
      /// neexistuje nebo když při kopírování dojde k jedné z (bohužel 
      /// nedokumentovaných) chyb</exception>
      /// <remarks>
      /// <para>
      /// Při použití této metody je třeba dát pozor, 
      /// aby zadání cílového adresáře pro funkci SHFileOperation, 
      /// představovalo adresář, který přijme kopírovaný adresář (jako podadresář). 
      /// Jestliže např. chcete kopírovat adresář C:\Codebook do adresáře G:\Backup 
      /// jako podadresář 'Codebook', musíte jako zdrojový zadat
      /// adresář 'C:\Codebook' a jako cílový 'G:\Backup'.
      /// </para>
      /// </remarks>
      public static void CopyFolder(string sourceFolderPath,
         string destFolderPath, bool confirmOverwrites)
      {
         // Test, zda cílový adresář existuje, 
         // abychom se vyvarovali problemu, že SHFileOperation při 
         // kopírování na neexistující disk proběhne bez chyby,
         // a další možné chybě, kterou funkce
         // SHFileOperation oklásí, když adresář neexistuje.
         if (Directory.Exists(destFolderPath) == false)
         {
            // Neexistuje nadřízený adresář cíle: Vyvoláme výjimku
            throw new IOException("Cílový adresář " + destFolderPath +
               " neexistuje");
         }

         // Test, zda existujue zdrojový adresář
         if (Directory.Exists(sourceFolderPath) == false)
         {
            throw new IOException("Zdrojový adresář " + sourceFolderPath +
               " neexistuje");
         }

         // Struktura pro informace o souboru
         SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT();

         // Definice (pod-)funkce (ShFileOperation může také mazat, přemísťovat
         // a přejmenovávat soubory)
         fileOp.wFunc = FO_COPY;

         // Definoce zdroje a cíle. Pitom je třeba různé soubory nebo
         // adresíže oddělit znakem s hodnotou 0. Na konec musí přijít dodatečný 
         // nulový znak 
         fileOp.pFrom = sourceFolderPath + "\x0\x0";
         fileOp.pTo = destFolderPath + "\x0\x0";

         // nastavení příznaků, aby bylo možno vrátit operaci a
         // - podle hodnoty argumentu confirmOverwrites - se program 
         // pči přepisování adresáře neptal uživatele
         if (confirmOverwrites)
         {
            fileOp.fFlags = FOF_ALLOWUNDO;
         }
         else
         {
            fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
         }

         // zavoláme ShFileOperation a předáme jí strukturu
         int result = SHFileOperation(ref fileOp);

         // Vyhodnocení výsledku. SHFileOperation vrátí
         // 0, když nedojde k chybě, jinak vrátí
         // (bohužel nedokumentovanou) hodnotu jinou než 0.
         if (result != 0)
         {
            throw new IOException("Chyba " + result +
               " při kopírování adresáře '" +
               sourceFolderPath + "' do adresáře '" +
               destFolderPath + "'");
         }
      }
   }
}