private void Button_TakeLeftWithConflict_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var note = _repo.FindNoteByID(_noteID);
                if (note == null)
                {
                    LoggerSingleton.Inst.Warn("ConflictWindow", "Note not found", $"Could not update note {_noteID}, because it no longer exists in the repository");
                    Close();
                    return;
                }

                if (note.Text != _dataLeft.Item1)
                {
                    note.Text = _dataLeft.Item1;
                }
                if (note.Title != _dataLeft.Item2)
                {
                    note.Title = _dataLeft.Item2;
                }
                if (!note.Tags.UnorderedCollectionEquals(_dataLeft.Item3))
                {
                    note.Tags.Synchronize(_dataLeft.Item3);
                }
                if (note.Path != _dataLeft.Item4)
                {
                    note.Path = _dataLeft.Item4;
                }

                var conflict = _repo.CreateNewNote(_dataRight.Item4);
                conflict.Title = string.Format("{0}_conflict_manual-{1:yyyy-MM-dd_HH:mm:ss}", _dataRight.Item2, DateTime.Now);
                conflict.Text  = _dataRight.Item1;
                conflict.Tags.Synchronize(_dataRight.Item3);
                conflict.IsConflictNote = true;
                _repo.SaveNote(conflict);

                note.SetRemoteDirty("Data updated in ConflictWindow [Take Left With Conflict]");
                _repo.SyncNow();

                Close();
            }
            catch (Exception ex)
            {
                LoggerSingleton.Inst.Error("ConflictWindow", "Error in conflict resolution", ex);
            }
        }
        private void DoSyncThreaded(Stopwatch sw, long t1, List <INote> entriesNoteRepo, Dictionary <INote, INote> realNoteMapping)
        {
            List <Tuple <string, DateTimeOffset> > deletes;

            lock (_repoDeletedNotes)
            {
                deletes = _repoDeletedNotes.ToList();
                _repoDeletedNotes.Clear();
            }

            var unmodifiedBoth     = new List <Tuple <PathEntry, INote> >();
            var modifiedFilesystem = new List <Tuple <PathEntry, INote> >();
            var modifiedNoteRepo   = new List <Tuple <PathEntry, INote> >();
            var createdFilesystem  = new List <Tuple <PathEntry, INote> >();
            var deletedFilesystem  = new List <Tuple <PathEntry, INote> >();
            var createdNoteRepo    = new List <Tuple <PathEntry, INote> >();
            var deletedNoteRepo    = new List <Tuple <PathEntry, INote> >();

            var entriesFilesystem = EnumerateFolder(DirectoryPath.Root(), _path, 0).ToList();

            LoggerSingleton.Inst.Debug("RawFolderSync",
                                       $"Found {entriesFilesystem.Count} entries in filesystem",
                                       string.Join("\n", entriesFilesystem.Select(e => e.FilesystemPath)));
            LoggerSingleton.Inst.Debug("RawFolderSync",
                                       $"Found {entriesNoteRepo.Count} entries in repository",
                                       string.Join("\n", entriesNoteRepo.Select(e => e.UniqueName + "\t" + e.Title)));

            #region Step 1: Analyze

            foreach (var map in _pathMapping.ToList())
            {
                var entryNR = entriesNoteRepo.FirstOrDefault(n => n.UniqueName == map.Key);
                var entryFS = entriesFilesystem.FirstOrDefault(n => n.FilesystemPath.ToLower() == map.Value.ToLower());

                if (entryNR == null && entryFS == null)
                {
                    continue;                                                 // mkay...
                }
                if (entryNR == null && entryFS != null)
                {
                    // file removed in NR
                    deletedNoteRepo.Add(Tuple.Create(entryFS, (INote)null));
                    entriesFilesystem.Remove(entryFS);
                    continue;
                }

                if (entryNR != null && entryFS == null)
                {
                    // file removed in FS
                    deletedFilesystem.Add(Tuple.Create((PathEntry)null, entryNR));
                    entriesNoteRepo.Remove(entryNR);
                    continue;
                }

                if (entryNR != null && entryFS != null)
                {
                    if (entryNR.Text == entryFS.Content && entryFS.FilesystemPath == GetFilesystemPath(entryNR))
                    {
                        // nothing changed
                        unmodifiedBoth.Add(Tuple.Create(entryFS, entryNR));
                        entriesFilesystem.Remove(entryFS);
                        entriesNoteRepo.Remove(entryNR);
                        continue;
                    }
                    else
                    {
                        if (_lastSync == null || entryNR.ModificationDate > _lastSync)
                        {
                            // NoteRepo Changed
                            modifiedNoteRepo.Add(Tuple.Create(entryFS, entryNR));
                            entriesFilesystem.Remove(entryFS);
                            entriesNoteRepo.Remove(entryNR);
                            continue;
                        }
                        else if (entryNR.ModificationDate >= entryFS.MDate)
                        {
                            // NoteRepo Changed
                            modifiedNoteRepo.Add(Tuple.Create(entryFS, entryNR));
                            entriesFilesystem.Remove(entryFS);
                            entriesNoteRepo.Remove(entryNR);
                            continue;
                        }
                        else
                        {
                            // Filesystem Changed
                            modifiedFilesystem.Add(Tuple.Create(entryFS, entryNR));
                            entriesFilesystem.Remove(entryFS);
                            entriesNoteRepo.Remove(entryNR);
                            continue;
                        }
                    }
                }

                throw new Exception("Invalid control flow in DoSyncThreaded::Step1");
            }

            foreach (var entryFS in entriesFilesystem.ToList())
            {
                var entryNR = entriesNoteRepo.FirstOrDefault(e => e.Path.EqualsIgnoreCase(entryFS.NotePath) && (e.Title.ToLower() == entryFS.Title.ToLower() || (entryFS.Title == e.UniqueName && e.Title == "")));

                if (entryNR != null)
                {
                    // data exists in both

                    if (entryNR.Text == entryFS.Content && entryNR.Title == entryFS.Title)
                    {
                        // nothing changed
                        unmodifiedBoth.Add(Tuple.Create(entryFS, entryNR));
                        entriesFilesystem.Remove(entryFS);
                        entriesNoteRepo.Remove(entryNR);
                    }
                    else
                    {
                        if (entryNR.ModificationDate >= entryFS.MDate)
                        {
                            // NoteRepo Changed
                            modifiedNoteRepo.Add(Tuple.Create(entryFS, entryNR));
                            entriesFilesystem.Remove(entryFS);
                            entriesNoteRepo.Remove(entryNR);
                        }
                        else
                        {
                            // Filesystem Changed
                            modifiedFilesystem.Add(Tuple.Create(entryFS, entryNR));
                            entriesFilesystem.Remove(entryFS);
                            entriesNoteRepo.Remove(entryNR);
                        }
                    }
                }
                else
                {
                    // data missing in noterepo

                    if (deletes.Any(d => d.Item1.ToLower() == entryFS.FilesystemPath.ToLower()))
                    {
                        // file removed in NR
                        deletedNoteRepo.Add(Tuple.Create(entryFS, (INote)null));
                        entriesFilesystem.Remove(entryFS);
                    }
                    else
                    {
                        // new file in FS
                        createdFilesystem.Add(Tuple.Create(entryFS, (INote)null));
                        entriesFilesystem.Remove(entryFS);
                    }
                }
            }

            foreach (var entryNR in entriesNoteRepo.ToList())
            {
                // data missing in FS

                if (_lastSync == null || entryNR.ModificationDate > _lastSync)
                {
                    // new file in NR
                    createdNoteRepo.Add(Tuple.Create((PathEntry)null, entryNR));
                    entriesNoteRepo.Remove(entryNR);
                }
                else
                {
                    // file removed in FS
                    deletedFilesystem.Add(Tuple.Create((PathEntry)null, entryNR));
                    entriesNoteRepo.Remove(entryNR);
                }
            }

            #endregion

            if (entriesFilesystem.Any())
            {
                throw new Exception("entriesFilesystem must be empty after analyze");
            }
            if (entriesNoteRepo.Any())
            {
                throw new Exception("entriesNoteRepo must be empty after analyze");
            }

            #region Step 2: Apply changes

            int countSuccess = 0;
            int countError   = 0;
            int countIgnored = 0;

            var newMapping = new List <Tuple <string, string> >();          // path -> UUID

            foreach (var data in unmodifiedBoth)
            {
                newMapping.Add(Tuple.Create(data.Item1.FilesystemPath, data.Item2.UniqueName));
            }

            foreach (var data in modifiedNoteRepo)              // Change [Filesystem] <-- [NoteRepo]
            {
                try
                {
                    var p2     = GetFilesystemPath(data.Item2);
                    var domove = (data.Item1.FilesystemPath.ToLower() != p2.ToLower());

                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [modification{(domove?"+rename":"")}] to Filesystem ({data.Item2.Title})",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{Fmt(data.Item2)}");

                    if (domove)
                    {
                        if (File.Exists(p2))
                        {
                            File.Delete(p2);
                        }
                        File.Move(data.Item1.FilesystemPath, p2);
                    }

                    File.WriteAllText(p2, data.Item2.Text, _encoding);
                    newMapping.Add(Tuple.Create(p2, data.Item2.UniqueName));

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [modification] to Filesystem failed ({data.Item2.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [modification] to Filesystem failed ({data.Item2.Title})", e);
                    countError++;
                }
            }

            foreach (var data in createdNoteRepo)             // Created [Filesystem] <-- [NoteRepo]
            {
                try
                {
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [creation] to Filesystem ({data.Item2.Title})",
                                               $"Filesystem:\n{"NULL"}\n\nRepository:\n{Fmt(data.Item2)}");

                    var tpath = GetFilesystemPath(data.Item2);
                    Directory.CreateDirectory(Path.GetDirectoryName(tpath) ?? "");
                    File.WriteAllText(tpath, data.Item2.Text, _encoding);
                    newMapping.Add(Tuple.Create(tpath, data.Item2.UniqueName));

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [creation] to Filesystem failed ({data.Item2.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [creation] to Filesystem failed ({data.Item2.Title})", e);
                    countError++;
                }
            }

            foreach (var data in deletedNoteRepo)             // Deleted [Filesystem] <-- [NoteRepo]
            {
                try
                {
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [deletion] to Filesystem ({data.Item1.Title})",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{"NULL"}");

                    File.Delete(data.Item1.FilesystemPath);

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [deletion] to Filesystem failed ({data.Item1.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [deletion] to Filesystem failed ({data.Item1.Title})", e);
                    countError++;
                }
            }

            foreach (var data in modifiedFilesystem)             // Change [Filesystem] --> [NoteRepo]
            {
                if (!_syncModifications)
                {
                    countIgnored++;
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Event [modification] from filesystem to repository ignored due to settings",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{Fmt(data.Item2)}");
                }

                try
                {
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [modification] to Repository ({data.Item2.Title})",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{Fmt(data.Item2)}");

                    _dispatcher.Invoke(() =>
                    {
                        var note   = realNoteMapping[data.Item2];
                        note.Text  = data.Item1.Content;
                        note.Title = data.Item1.Title;
                        newMapping.Add(Tuple.Create(data.Item1.FilesystemPath, note.UniqueName));
                    });

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [modification] to Repository failed ({data.Item2.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [modification] to Repository failed ({data.Item2.Title})", e);
                    countError++;
                }
            }

            foreach (var data in createdFilesystem)             // Created [Filesystem] --> [NoteRepo]
            {
                if (!_syncCreation)
                {
                    countIgnored++;
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Event [creation] from filesystem to repository ignored due to settings",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{"NULL"}");
                }

                try
                {
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [creation] to Repository ({data.Item1.Title})",
                                               $"Filesystem:\n{Fmt(data.Item1)}\n\nRepository:\n{"NULL"}");

                    _dispatcher.Invoke(() =>
                    {
                        var n   = _repo.CreateNewNote(data.Item1.NotePath);
                        n.Title = data.Item1.Title;
                        n.Text  = data.Item1.Content;
                        newMapping.Add(Tuple.Create(data.Item1.FilesystemPath, n.UniqueName));
                    });

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [creation] to Repository failed ({data.Item1.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [creation] to Repository failed ({data.Item1.Title})", e);
                    countError++;
                }
            }

            foreach (var data in deletedFilesystem)             // Deleted [Filesystem] --> [NoteRepo]
            {
                if (!_syncDeletion)
                {
                    countIgnored++;
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Event [deletion] from filesystem to repository ignored due to settings",
                                               $"Filesystem:\n{"NULL"}\n\nRepository:\n{Fmt(data.Item2)}");
                }

                try
                {
                    LoggerSingleton.Inst.Debug("RawFolderSync",
                                               $"Synchronized [deletion] to Repository ({data.Item2.Title})",
                                               $"Filesystem:\n{"NULL"}\n\nRepository:\n{Fmt(data.Item2)}");

                    _dispatcher.Invoke(() =>
                    {
                        var note = realNoteMapping[data.Item2];
                        _repo.DeleteNote(note, true);
                    });

                    countSuccess++;
                }
                catch (Exception e)
                {
                    LoggerSingleton.Inst.Error("RawFolderSync", $"Synchronize [deletion] to Repository failed ({data.Item2.Title})", e);
                    LoggerSingleton.Inst.ShowSyncErrorDialog($"Synchronize [deletion] to Repository failed ({data.Item2.Title})", e);
                    countError++;
                }
            }

            #endregion

            _pathMapping.Clear();
            foreach (var newmap in newMapping)
            {
                _pathMapping[newmap.Item2] = newmap.Item1;
            }

            _lastSync = DateTimeOffset.Now;

            sw.Stop();

            LoggerSingleton.Inst.Info("RawFolderSync",
                                      $"Synchronization with local folder finished in {sw.ElapsedMilliseconds}ms ({t1}ms synchronous) ({countSuccess} changes | {countError} errors)",
                                      $"Successful changes: {countSuccess}\n" +
                                      $"Errored changes:    {countError}\n" +
                                      $"Ignored changes:    {countIgnored}\n" +
                                      "\n\n" +
                                      $"Unchanged entries: {unmodifiedBoth.Count}\n" +
                                      $"Modified(FS):      {modifiedFilesystem.Count}\n" +
                                      $"Modified(NR):      {modifiedNoteRepo .Count}\n" +
                                      $"Created(FS):       {createdFilesystem.Count}\n" +
                                      $"Created(NR):       {createdNoteRepo.Count}\n" +
                                      $"Deleted(FS):       {deletedFilesystem.Count}\n" +
                                      $"Deleted(NR):       {deletedNoteRepo.Count}\n" +
                                      "\n\n" +
                                      "PathMapping(new):\n" +
                                      string.Join("\n", newMapping.Select(m => $"  {m.Item2}      {m.Item1}")));
        }