/// <summary>
        /// Checks wheter the resource exists in the index and RemoteProperties
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private (bool existInIndex, bool existsRemote) CheckForElementExistance(ISyncElement element, IEnumerable <IRemoteProperty> remoteProperties,
                                                                                SyncIndexManager manager)
        {
            bool exstIndex  = GetIndexElement(manager, element) != null;
            bool exstRemote = GetProperty(remoteProperties, element) != null;

            return(exstIndex, exstRemote);
        }
        /// <summary>
        /// Returns the IndexElement matching the given ISyncElement
        /// </summary>
        /// <param name="manager"></param>
        /// <param name="element"></param>
        /// <returns></returns>
        private SyncIndexElementDTO GetIndexElement(SyncIndexManager manager, ISyncElement element)
        {
            IEnumerable <SyncIndexElementDTO> elements = manager.GetOrCreateIndexFile();

            if (elements != null)
            {
                return(elements.Where(x => x.ElementType == element.Type &&
                                      x.ReleativeFilePath == element.RelativePath
                                      ).FirstOrDefault());
            }
            return(null);
        }
        /// <summary>
        /// Handles the synchronisation of the given ISyncElement
        /// </summary>
        /// <param name="element"></param>
        /// <param name="client"></param>
        /// <param name="properties"></param>
        /// <param name="indexManager"></param>
        private void HandleISyncElement(CancellationToken cancleToken, ISyncElement element, ISyncClient client, IEnumerable <IRemoteProperty> properties,
                                        SyncIndexManager indexManager, ILogger logger, Configuration config)
        {
            if (cancleToken.IsCancellationRequested)
            {
                return;
            }
            //The general rules defining when to synchronize what are the same
            //for all ISyncClients and they follow the following design:
            //
            //1. Iterate all local folder and their files, check for updates on server:
            //  Both:
            //      1. when it does not exist in index and server -> upload
            //      2. when it exists only in index -> delete (has been deleted remotely)
            //      3. when exists on server but not index -> update index
            //  Files:
            //      1. When is exists in server and index -> check for updates
            //2. Add all files that are not in index to local copy
            //3. Remove all files that exist in index and server but not local from server -> deleted local

            (bool existInIndex, bool existsRemote)existance = CheckForElementExistance(element, properties, indexManager);

            if (!existance.existInIndex && !existance.existsRemote)
            {
                //Not in index and not in remote -> upload
                logger.Debug(element.Type + " " + element.RelativePath + " does not exist in local Index and remote, " +
                             "File will be added to both");
                Upload(cancleToken, element, client, indexManager);
                //Set the Method to ignore Updates -> Just handle children in case of Directory
                //Because directly checking for updates may cause exceptions because the server is
                //slow and does not return the correct properties on request yet
                HandleISyncElementChildrenOrUpdates(cancleToken, element, client, properties, indexManager, logger, config, true);
            }
            else if (existance.existInIndex && !existance.existsRemote)
            {
                //In index but not remote -> delete (has been deleted remotely)
                logger.Debug(element.Type + " " + element.RelativePath + " exists in index but has been removed remote. Deleting " + element.Type);
                DeleteLocalElement(element, indexManager);
            }
            else if (!existance.existInIndex && existance.existsRemote)
            {
                //Remote but not in index -> Add to index
                logger.Debug(element.Type + " " + element.RelativePath + " was missing in the index -> will be added.");
                //As it cannot be said if there was changes remote, no remote revision is added
                indexManager.AddOrUpdate(CreateIndexElement(element, null));
                HandleISyncElementChildrenOrUpdates(cancleToken, element, client, properties, indexManager, logger, config, false);
            }
            else if (existance.existInIndex && existance.existsRemote)
            {
                HandleISyncElementChildrenOrUpdates(cancleToken, element, client, properties, indexManager, logger, config, false);
            }
        }
 /// <summary>
 /// Deletes the given ISyncElement and removes it from index if successfull
 /// </summary>
 /// <param name="element"></param>
 /// <param name="indexManager"></param>
 private void DeleteLocalElement(ISyncElement element, SyncIndexManager indexManager)
 {
     if (element.Delete())
     {
         SyncIndexElementDTO dto = GetIndexElement(indexManager, element);
         if (element.Type == SyncElementType.Directory)
         {
             //If a Directory was removed, remove all sub Element that
             //have been contained in that directory
             indexManager.RemoveAll(x => x.ReleativeFilePath.Length >= dto.ReleativeFilePath.Length &&
                                    x.ReleativeFilePath.Substring(0, dto.ReleativeFilePath.Length) == dto.ReleativeFilePath);
         }
         else
         {
             indexManager.Remove(dto);
         }
     }
 }
        /// <summary>
        /// Executes the synchronisation for the given configuration and using the
        /// given Client
        /// This Method can be called multiple times using different configurations or client
        /// to achive different synchronisations
        /// </summary>
        public void ExcecuteSynchronisation(CancellationToken cancleToken, Configuration config, ISyncClient client)
        {
            _logger.Debug("Executing synchronisation for Directory " + config.Local.LocalSyncDir + " using client of type " + client.GetType());
            //Check if default dir extists, else delete
            if (!Directory.Exists(config.Local.LocalSyncDir))
            {
                _logger.Debug("Creating sync directory at " + config.Local.LocalSyncDir);
                Directory.CreateDirectory(config.Local.LocalSyncDir);
            }

            //Read or create the index File
            _logger.Debug("Reading index File");
            SyncIndexManager indexManager = new SyncIndexManager(_logger, config);

            //The following three steps are performed for a synchronisation:
            //1. Iterate all local folder and their files, check for updates on server
            //2. Remove all files that exist in index and server but not local from server -> deleted local
            //3. Add all files that are not in index to local copy
            //Start getting Files at the root Folder
            GetDataAndHandleSync(cancleToken, config.Local.LocalSyncDir, client, _logger, config, indexManager);

            if (cancleToken.IsCancellationRequested)
            {
                return;
            }

            DetectLocalDeletedElements(cancleToken, config, client, _logger, indexManager);

            if (cancleToken.IsCancellationRequested)
            {
                return;
            }

            FetchNewFilesFromServer(cancleToken, config, client, _logger, indexManager);

            _logger.Debug("Synchronisation finished");
        }
        /// <summary>
        /// Uploads the given ISync Element using the ISyncClient
        /// If success the Element will be added or an existing element will be updated with
        /// the new values
        /// </summary>
        /// <param name="element"></param>
        /// <param name="client"></param>
        /// <param name="indexManager"></param>
        private void Upload(CancellationToken cancleToken, ISyncElement element, ISyncClient client, SyncIndexManager indexManager)
        {
            IRemoteProperty uploaded = element.Upload(cancleToken, client);

            if (uploaded != null)
            {
                //Upload was successfull, add as tracked element to index
                indexManager.AddOrUpdate(CreateIndexElement(element, uploaded));
            }
        }
        /// <summary>
        /// Patches the local file with the current File version provided by the ISyncClient
        /// </summary>
        /// <param name="file"></param>
        /// <param name="client"></param>
        /// <param name="indexManager"></param>
        private void PatchLocalFile(CancellationToken cancleToken, FileSyncElement file, ISyncClient client, SyncIndexManager indexManager,
                                    IEnumerable <IRemoteProperty> remoteProperties)
        {
            //File has been changed remotely
            FileInfo update = file.PatchLocalFile(cancleToken, client);

            if (update != null)
            {
                //Updte Index Manager
                indexManager.AddOrUpdate(CreateIndexElement(file, GetProperty(remoteProperties, file)));
            }
        }
        /// <summary>
        /// Checks wheter
        /// </summary>
        /// <param name="file"></param>
        /// <param name="client"></param>
        /// <param name="properties"></param>
        /// <param name="indexManager"></param>
        private void CheckForFileUpdates(CancellationToken cancleToken, FileSyncElement file, ISyncClient client, IEnumerable <IRemoteProperty> properties, SyncIndexManager indexManager)
        {
            //Get Index and property instances
            SyncIndexElementDTO index    = GetIndexElement(indexManager, file);
            IRemoteProperty     property = GetProperty(properties, file);

            //Compare the change dates
            if (!index.LocalRevision.Equals(file.Revision) && index.RemoteRevision.Equals(property.RemoteRevision))
            {
                //File has been changed locally
                _logger.Debug("File " + file.RelativePath + " has been changed locally. Index rev: " + index.LocalRevision + " current file rev: " + file.Revision);
                Upload(cancleToken, file, client, indexManager);
            }
            else if (index.LocalRevision.Equals(file.Revision) && !index.RemoteRevision.Equals(property.RemoteRevision))
            {
                //File has been changed remotely
                _logger.Debug("File " + file.RelativePath + " has been changed remotely. Index remote rev: " + index.RemoteRevision + " current remote rev: " + property.RemoteRevision);
                PatchLocalFile(cancleToken, file, client, indexManager, properties);
            }
            else if (!index.LocalRevision.Equals(file.Revision) && !index.RemoteRevision.Equals(property.RemoteRevision))
            {
                //Conflict! Remote and server version has been changed!
                //Priorise server version, patch Local file
                _logger.Debug("File " + file.RelativePath + " has been changed remote and locally. Will fetch server version over local version");
                _logger.Debug("File " + file.RelativePath + " local index local rev: " + index.LocalRevision + " file local rev: " + file.Revision);
                _logger.Debug("File " + file.RelativePath + " remote index rev: " + index.RemoteRevision + " file remote rev: " + property.RemoteRevision);
                PatchLocalFile(cancleToken, file, client, indexManager, properties);
            }
        }
 /// <summary>
 /// Checks for the given elements children or remote updates, has the option to only check the children and ignore updates in case
 /// Can be usefull to give the server enough time to index new files
 /// </summary>
 /// <param name="cancleToken"></param>
 /// <param name="element"></param>
 /// <param name="client"></param>
 /// <param name="properties"></param>
 /// <param name="indexManager"></param>
 /// <param name="logger"></param>
 /// <param name="config"></param>
 /// <param name="ignoreUpdates"></param>
 private void HandleISyncElementChildrenOrUpdates(CancellationToken cancleToken, ISyncElement element, ISyncClient client,
                                                  IEnumerable <IRemoteProperty> properties, SyncIndexManager indexManager, ILogger logger,
                                                  Configuration config, bool ignoreUpdates)
 {
     //File: check for updates
     //Directoy: process with going one hierarchy deeper
     if (element.Type.Equals(SyncElementType.File))
     {
         if (!ignoreUpdates)
         {
             CheckForFileUpdates(cancleToken, (FileSyncElement)element, client, properties, indexManager);
         }
     }
     else if (element.Type.Equals(SyncElementType.Directory))
     {
         GetDataAndHandleSync(cancleToken, element.AbsolutePath, client, logger, config, indexManager);
     }
     else
     {
         throw new NotImplementedException();
     }
 }
        /// <summary>
        /// Iterates trough all Index Elements and checks wheter the still exist
        /// If not they have been deleted locally and will be removed on the server
        /// </summary>
        /// <param name="dto"></param>
        /// <param name="client"></param>
        /// <param name="logger"></param>
        /// <param name="indexManager"></param>
        private void DetectLocalDeletedElements(CancellationToken cancleToken, Configuration dto, ISyncClient client, ILogger logger, SyncIndexManager indexManager)
        {
            IEnumerable <SyncIndexElementDTO> indexElements  = indexManager.GetOrCreateIndexFile();
            List <SyncIndexElementDTO>        removeElements = new List <SyncIndexElementDTO>();

            foreach (SyncIndexElementDTO element in indexElements)
            {
                if (cancleToken.IsCancellationRequested)
                {
                    return;
                }

                bool   exists       = true;
                string absolutePath = dto.Local.LocalSyncDir + element.ReleativeFilePath;
                try
                {
                    if (element.ElementType.Equals(SyncElementType.File))
                    {
                        exists = File.Exists(absolutePath);
                    }
                    else if (element.ElementType.Equals(SyncElementType.Directory))
                    {
                        exists = Directory.Exists(absolutePath);
                    }
                } catch (Exception exc) { _logger.Error("Error occured while checking " + element.ElementType + "existance: ", exc); }
                if (!exists)
                {
                    _logger.Debug(element.ElementType + " " + element.ReleativeFilePath + " does not exist anymore. Will be deleted from server and index.");
                    removeElements.Add(element);
                }
            }

            //Get List of Directories that are contained in the removeElements list
            List <SyncIndexElementDTO> directories = removeElements.Where(x => x.ElementType == SyncElementType.Directory).ToList();

            foreach (SyncIndexElementDTO element in directories)
            {
                //remove all Elements that are contained in removeElements list and
                //are part of this Directory
                removeElements.RemoveAll(x => x.ReleativeFilePath.Length > element.ReleativeFilePath.Length &&
                                         x.ReleativeFilePath.Substring(0, element.ReleativeFilePath.Length) == element.ReleativeFilePath);
            }

            //Remove all elements that can be removed
            foreach (SyncIndexElementDTO element in removeElements)
            {
                if (client.Remove(cancleToken, element.ReleativeFilePath))
                {
                    //If its a Directory delete all Elements that are contained in this Directory
                    //From the index!
                    if (element.ElementType == SyncElementType.Directory)
                    {
                        indexManager.RemoveAll(x => x.ReleativeFilePath.Length >= element.ReleativeFilePath.Length &&
                                               x.ReleativeFilePath.Substring(0, element.ReleativeFilePath.Length) == element.ReleativeFilePath);
                    }
                    else
                    {
                        indexManager.Remove(element);
                    }
                }
            }
        }
 /// <summary>
 /// Downloaded the given RemotePropery and adds it as a local file
 /// </summary>
 /// <param name="remoteProperty"></param>
 /// <param name="client"></param>
 /// <param name="conf"></param>
 /// <param name="logger"></param>
 /// <param name="indexManager"></param>
 private void FetchFileFromServer(CancellationToken cancleToken, IRemoteProperty remoteProperty, ISyncClient client, Configuration conf, ILogger logger, SyncIndexManager indexManager)
 {
     try
     {
         string localFilePath = conf.Local.LocalSyncDir + remoteProperty.DecodedRelativeRemotePath;
         if (!File.Exists(localFilePath))
         {
             //File does not exist local, will be downloaded and added
             _logger.Debug(remoteProperty.DecodedRelativeRemotePath + " does not exist locally. Will be downloaded and added to index");
             FileInfo temp = client.DownloadRemoteFileToTemp(cancleToken, remoteProperty.DecodedRelativeRemotePath);
             if (temp != null)
             {
                 FileInfo newFile = FileManager.CopyFile(temp.FullName, localFilePath, true, _logger);
                 if (newFile != null)
                 {
                     FileSyncElement newFileElement = new FileSyncElement(newFile, conf.Local.LocalSyncDir, logger);
                     indexManager.AddOrUpdate(CreateIndexElement(newFileElement, remoteProperty));
                 }
             }
         }
     } catch (Exception exc)
     {
         logger.Error("Unexpected error while fetching " + remoteProperty.RelativeRemotePath + " from server: ", exc);
     }
 }
        /// <summary>
        /// Fetches the given Folder and its content from the server
        /// </summary>
        private void FetchFolderFromServer(CancellationToken cancleToken, string relativePath, ISyncClient client, Configuration conf, ILogger logger, SyncIndexManager indexManager)
        {
            if (cancleToken.IsCancellationRequested)
            {
                return;
            }

            //Check if folder exists locally -> else create
            string localDirectoryPath = conf.Local.LocalSyncDir + relativePath;

            try
            {
                if (!Directory.Exists(localDirectoryPath))
                {
                    _logger.Debug("Folder " + localDirectoryPath + " does not exist locally. Creating folder and adding to the index.");
                    Directory.CreateDirectory(localDirectoryPath);

                    indexManager.AddOrUpdate(CreateIndexElement(new DirectorySyncElement(new DirectoryInfo(localDirectoryPath), conf.Local.LocalSyncDir, logger), null));
                }
            } catch (Exception exc)
            {
                _logger.Error("A error occured while creating local folder", exc);
            }

            //Get Properties and Process them
            IEnumerable <IRemoteProperty> remoteProperties = client.GetProperties(GetRemotePath(conf, localDirectoryPath));

            //Something went wrong during the connection to the server
            if (remoteProperties == null)
            {
                _logger.Error("Something went wrong during connection to server.");
                return;
            }

            foreach (IRemoteProperty property in remoteProperties)
            {
                if (cancleToken.IsCancellationRequested)
                {
                    return;
                }
                if (property.ElementType.Equals(SyncElementType.Directory))
                {
                    FetchFolderFromServer(cancleToken, property.DecodedRelativeRemotePath, client, conf, logger, indexManager);
                }
                else
                {
                    FetchFileFromServer(cancleToken, property, client, conf, logger, indexManager);
                }
            }
        }
 /// <summary>
 /// Iterates all remote files and check if they have been added localy
 /// These that have not been added yet will be downloaded and added to the index
 /// </summary>
 /// <param name="dto"></param>
 /// <param name="client"></param>
 /// <param name="logger"></param>
 /// <param name="indexManager"></param>
 private void FetchNewFilesFromServer(CancellationToken cancleToken, Configuration dto, ISyncClient client, ILogger logger, SyncIndexManager indexManager)
 {
     FetchFolderFromServer(cancleToken, "", client, dto, logger, indexManager);
 }