using System;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;

namespace Addison_Wesley.Codebook.Filesystem
{
   /// <summary>
   /// Obsahuje informace o nepodařeném kopírování
   /// </summary>
   public class CopyFault
   {
      /// <summary>
      /// Udává, zda jde o souor
      /// </summary>
      public bool IsFile;

      /// <summary>
      /// Zdrojová cesta
      /// </summary>
      public string Source;

      /// <summary>
      /// Cílová cesta
      /// </summary>
      public string Destination;

      /// <summary>
      /// Vzniklé chyby
      /// </summary>
      public string Error;

      /// <summary>
      /// Konstruktor
      /// </summary>
      /// <param name="isFile">Udává, zda se jedná o soubor</param>
      /// <param name="source">Zdrojová cesta</param>
      /// <param name="destination">Cílová cesta</param>
      /// <param name="error">Vzniklé chyby</param>
      internal CopyFault(bool isFile, string source, string destination,
         string error)
      {
         this.IsFile = isFile;
         this.Source = source;
         this.Destination = destination;
         this.Error = error;
      }
   }

   /// <summary>
   /// Seznam objektů typu CopyFault s chybami při kopírování
   /// </summary>
   public class CopyFaults : List<CopyFault>
   {
   }

   /// <summary>
   /// Třída s metodou pro flexibilní kopírování adresáře
   /// </summary>
   public class FolderUtil
   {
      /* Vlastnosti, které se použijí při přepisování 
       * všech souborů */
      private static bool overwriteAllFiles;
      private static bool alreadyAskedForOverwriteAllFiles;

      /* Kopíruje jeden adresář */
      public static CopyFaults CopyFolder(string sourceFolderPath,
         string destFolderPath)
      {
         // Vytvoření objektu typu CopyFaults
         CopyFaults copyFaults = new CopyFaults();

         // Nastavení příznaků pro přepsání souboru 
         overwriteAllFiles = false;
         alreadyAskedForOverwriteAllFiles = false;

         // Volání rekurzivní metody pro kopírování podadresáře a souborů
         FolderUtil.copySubFoldersAndFiles(new DirectoryInfo(sourceFolderPath),
            sourceFolderPath, destFolderPath, copyFaults);

         // Vrácení výsledku
         return copyFaults;
      }

      /// <summary>
      /// (Rekurzivně) zkopíruje  všechny podadresáře a soubory daného adrsáře do jiného adresáře
      /// </summary>
      /// <param name="folder">odkaz na objekt typu DirectoryInfo, který reprezentuje daný adresář</param>
      /// <param name="mainsourceFolderPath">Cesta k hlavnímu zdrojovému adresáři</param>
      /// <param name="maindestFolderPath">Cesta k hlavnímu cílovému adresáři</param>
      /// <param name="copyFaults">Odkaz na seznam objektů typu CopyFaults,
      /// do něhož se zapisují všechny chyby, k nimž při kopírování došlo</param>
      private static void copySubFoldersAndFiles(DirectoryInfo folder,
       string mainsourceFolderPath, string maindestFolderPath,
       CopyFaults copyFaults)
      {
         // Cílový adresář
         try
         {
            // Zjistíme jméno cílového adresáře
            string destFolderPath = folder.FullName.Replace(mainsourceFolderPath,
               maindestFolderPath);

            // Vytvoření cílového adresáře
            Directory.CreateDirectory(destFolderPath);
         }
         catch (IOException ex)
         {
            // Dokumentace chyb v seznamu objektů typu CopyFaults
            copyFaults.Add(new CopyFault(false, mainsourceFolderPath,
               maindestFolderPath, ex.Message));
         }

         // Projdeme všechny podadresáře daného adresáře
         DirectoryInfo[] subFolders = folder.GetDirectories();
         for (int i = 0; i < subFolders.Length; i++)
         {
            // Zjištění cesty cílového adresáře, v němž bude cesta
            // zdrojového adresáře nahrazena cestou cílového adresáře
            string destSubFolderName = subFolders[i].FullName.Replace(
               mainsourceFolderPath, maindestFolderPath);

            // Rekurzivn volání funkce, aby se další vytvořily podadresáře
            FolderUtil.copySubFoldersAndFiles(
               subFolders[i], mainsourceFolderPath,
               maindestFolderPath, copyFaults);
         }

         // Zjištění souborů obsažených v adresáři
         FileInfo[] files = folder.GetFiles();

         // Projdeme všechny soubory
         for (int i = 0; i < files.Length; i++)
         {
            // Zjištění jména cílového souboru, v něm bude cesta
            // ke zdrojovému adresáři nahrazena cestou k cílovému adresáři
            string destFileName = files[i].FullName.Replace(
               mainsourceFolderPath, maindestFolderPath);

            // Nastavení příznaku, který určí, zda se má soubor kopírovat
            bool performCopyOperation;
            performCopyOperation = true;

            // Test, zda soubor již existuje
            if (File.Exists(destFileName))
            {
               // Dotaz, zda se má soubor přepsat, pokud
               // to uživatel předem nenastavil pro všechny 
               // adresáře a soubory 
               if (overwriteAllFiles == false)
               {
                  switch (MessageBox.Show("Soubor '" + destFileName +
                     "' již existuje.\r\n\r\n" +
                     "Chcete tento soubor přepsat?",
                     Application.ProductName, MessageBoxButtons.YesNoCancel,
                     MessageBoxIcon.Question))
                  {
                     case DialogResult.Yes:
                        // Dotaz, zda se mají přpsat všechny soubory,
                        // pokud k tomu nedošlo již dříve
                        if (alreadyAskedForOverwriteAllFiles == false)
                        {
                           switch (MessageBox.Show("Chcete přepsat všechny  " +
                              "existující soubory v cílovém adresáři?",
                              Application.ProductName,
                              MessageBoxButtons.YesNoCancel,
                              MessageBoxIcon.Question))
                           {
                              case DialogResult.Yes:
                                 // Nastavení příznaku, který řídí přepisování
                                 overwriteAllFiles = true;
                                 break;

                              case DialogResult.Cancel:
                                 // Uživatel to přerušil: Vyvoláme výjimku,
                                 // neboť adresář nelze kompletně zkopírovat
                                 throw new IOException("Zrušeno uživatelem");
                           }

                           // Určení, že se již není třeba ptát
                           alreadyAskedForOverwriteAllFiles = true;
                        }
                        break;

                     case DialogResult.No:
                        // Určení, že se aktuální soubor 
                        // nemá kopírovat
                        performCopyOperation = false;
                        break;

                     case DialogResult.Cancel:
                        // Přerušeno uživatelem: Vyvoláme výjimku, neboť
                        // adresář nelze zkopírovat celý
                        throw new IOException(
                           "Zrušeno uživatelem");
                  }
               }
            }

            // Kopírování souboru, když má tato operace proběhnout
            if (performCopyOperation)
            {
               try
               {
                  File.Copy(files[i].FullName, destFileName,
                     true);
               }
               catch (Exception ex)
               {
                  // Dokumentace chyby v seznamu instancí typu CopyFaults
                  copyFaults.Add(new CopyFault(true, files[i].FullName,
                     destFileName, ex.Message));
               }
            }
            else
            {
               // Soubor nemá být přepsán: Dokumentace chyby v seznamu
               // instancí typu CopyFaults
               copyFaults.Add(new CopyFault(true, files[i].FullName,
                  destFileName,
                  "Chybí souhlas uživatele s přepsáním"));
            }
         }
      }
   }
}
