using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography.X509Certificates; namespace DataOrganizer { class Snapshot { public Folder Root { get; private set; } public Dictionary Files { get; private set; } = new Dictionary(); public Dictionary Directories { get; private set; } = new Dictionary(); public Dictionary FileDuplicates { get; private set; } = new Dictionary(); //Contains files associated with particular, first level folders. //So if the root folder group is X:\Anime and we have a folder path of x:\Anime\Death Note, then indexing this dictonary //with the text "Death Note" would return all files in that folder. public Dictionary> OrganizationFolders { get; private set; } = new Dictionary>(); public Snapshot(List fileRecords, DB.FolderGroup folderGroup) { Root = new Folder(folderGroup.Name, folderGroup.Path, null); Directories.Add(Root.FullPath, Root); if (folderGroup.Type == DB.OrganizationType.SingleLevel) ProcessFilesAsOneLevel(fileRecords, folderGroup); //else if (folderGroup.Type == DB.OrganizationType.Flat) // ProcessFilesAsFlat(fileRecords, folderGroup); } public Snapshot(List files, DB.FolderGroup folderGroup) { Root = new Folder(folderGroup.Name, folderGroup.Path, null); Directories.Add(Root.FullPath, Root); if (folderGroup.Type == DB.OrganizationType.SingleLevel) ProcessFilesAsOneLevel(files, folderGroup); } private void ProcessFilesAsOneLevel(List files, DB.FolderGroup folderGroup) { Folder firstLevelFolder = null; foreach (var file in files) { var folders = file.PathNoRoot.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); var orgFolder = Path.Combine(folderGroup.Path, folders[0]); var currentPath = Path.Combine(folderGroup.Path, folders[0]); if(firstLevelFolder == null || firstLevelFolder.Name != folders[0]) { firstLevelFolder = new Folder(folders[0], Path.Combine(folderGroup.Path, folders[0]), Root); Directories.Add(orgFolder, firstLevelFolder); OrganizationFolders.Add(orgFolder, new List()); Root.AddDirectory(firstLevelFolder); } for(var i = 1; i < folders.Length - 1; i++) { var parentFolder = currentPath; currentPath = Path.Combine(currentPath, folders[i]); if(!Directories.ContainsKey(currentPath)) { var newFolder = new Folder(folders[i], currentPath, Directories[parentFolder]); Directories.Add(currentPath, newFolder); Directories[parentFolder].AddDirectory(newFolder); } } //var duplicates = OrganizationFolders[orgFolder].Where(x => x.Size == file.Size && x.Id != file.Id); //foreach(var dup in duplicates) //{ // if(FileDuplicates.ContainsKey(file.PathNoRoot)) // { // FileDuplicates[file.PathNoRoot].Duplicates.Add(dup); // } // else FileDuplicates.Add(file.PathNoRoot, new Duplicate() { File = file, Duplicates = new List() { dup } }); //} var snap = new FileSnapshot(file, firstLevelFolder, Directories[currentPath], folderGroup.Path); OrganizationFolders[orgFolder].Add(snap); Directories[currentPath].AddFile(snap); Files.Add(Path.Combine(folderGroup.Path, file.PathNoRoot), snap); } } private void ProcessFilesAsOneLevel(List files, DB.FolderGroup folderGroup) { Folder firstLevelFolder = null; foreach (var file in files) { //Add one to the length to remove the trailing slash in the root path. var pathNoRoot = file.FullName.Remove(0, folderGroup.Path.Length + 1); var folders = pathNoRoot.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); var orgFolder = Path.Combine(folderGroup.Path, folders[0]); var currentPath = Path.Combine(folderGroup.Path, folders[0]); if (firstLevelFolder == null || firstLevelFolder.Name != folders[0]) { firstLevelFolder = new Folder(folders[0], Path.Combine(folderGroup.Path, folders[0]), Root); Directories.Add(orgFolder, firstLevelFolder); OrganizationFolders.Add(orgFolder, new List()); Root.AddDirectory(firstLevelFolder); } for (var i = 1; i < folders.Length - 1; i++) { var parentFolder = currentPath; currentPath = Path.Combine(currentPath, folders[i]); if (!Directories.ContainsKey(currentPath)) { var newFolder = new Folder(folders[i], currentPath, Directories[parentFolder]); Directories.Add(currentPath, newFolder); Directories[parentFolder].AddDirectory(newFolder); } } //var duplicates = OrganizationFolders[orgFolder].Where(x => x.Size == file.Size && x.Id != file.Id); //foreach(var dup in duplicates) //{ // if(FileDuplicates.ContainsKey(file.PathNoRoot)) // { // FileDuplicates[file.PathNoRoot].Duplicates.Add(dup); // } // else FileDuplicates.Add(file.PathNoRoot, new Duplicate() { File = file, Duplicates = new List() { dup } }); //} var snap = new FileSnapshot(file, firstLevelFolder, Directories[currentPath], folderGroup.Path); OrganizationFolders[orgFolder].Add(snap); Directories[currentPath].AddFile(snap); Files.Add(snap.FullPath, snap); } } private void ProcessFilesAsFlat(List files, DB.FolderGroup folderGroup) { } public void DeleteFile(FileSnapshot file) { if (Files.ContainsKey(file.FullPath)) Files.Remove(file.FullPath); Root.DeleteFile(file); file.ParentFolder.DeleteFile(file); } public void DeleteFolder(Folder folder) { ClearSubFolders(folder); } private void ClearSubFolders(Folder folder) { if (Directories.ContainsKey(folder.FullPath)) Directories.Remove(folder.FullPath); if (OrganizationFolders.ContainsKey(folder.FullPath)) OrganizationFolders.Remove(folder.FullPath); Root.DeleteFolder(folder); folder.Parent.DeleteFolder(folder); foreach (var dir in folder.Folders) ClearSubFolders(dir.Value); } public void MergeSnapshot(Snapshot snapshot) { var newFiles = new List(); var existingFiles = Files.Values.Except(snapshot.Files.Values); foreach(var file in snapshot.Files) if (!Files.ContainsKey(file.Key)) newFiles.Add(file.Value); foreach (var folder in snapshot.OrganizationFolders) { if (!OrganizationFolders.ContainsKey(folder.Key) && folder.Key != Root.FullPath) { Root.AddDirectory(snapshot.Directories[folder.Key]); OrganizationFolders.Add(folder.Key, new List()); } } foreach (var existingFile in existingFiles) { if (!snapshot.Files.ContainsKey(existingFile.FullPath)) { existingFile.Status = FileSnapshot.FileStatus.PendingDelete; } else if (existingFile.ModifiedDate != snapshot.Files[existingFile.FullPath].ModifiedDate) { existingFile.ModifiedDate = snapshot.Files[existingFile.FullPath].ModifiedDate; } } foreach(var newFile in newFiles) { Files.Add(newFile.FullPath, newFile); Folder folder = newFile.ParentFolder; while(folder != null) { if (!Directories.ContainsKey(folder.FullPath)) Directories.Add(folder.FullPath, folder); folder = folder.Parent; } OrganizationFolders[newFile.OrganizationalFolder.FullPath].Add(newFile); } } public struct Duplicate { public DB.FileSnapshot File; public List Duplicates; } } public class Folder { public string Name { get; private set; } public string FullPath { get; private set; } public Folder Parent { get; private set; } public Dictionary Files { get; private set; } = new Dictionary(); public Dictionary Folders { get; private set; } = new Dictionary(); public bool IsEmpty { get { return !(Files.Count > 0 || Folders.Count > 0); } } public bool IsRootFolder { get { return Parent == null; } } public Folder(string name, string fullPath, Folder parent) { Name = name; FullPath = fullPath; Parent = parent; } public void AddFile(FileSnapshot file) { Files.Add(file.FullPath, file); } public void AddDirectory(Folder folder) { Folders.Add(folder.FullPath, folder); } public List GetAllPendingChanges() { var files = new List(); files.AddRange(Files.Values.Where(x => x.Status != FileSnapshot.FileStatus.None)); foreach (var folder in Folders.Values) files.AddRange(folder.GetAllPendingChanges()); return files; } public void DeleteFile(FileSnapshot file) { if (Files.ContainsKey(file.FullPath)) Files.Remove(file.FullPath); } public void DeleteFolder(Folder folder) { if (Folders.ContainsKey(folder.FullPath)) Folders.Remove(folder.FullPath); } } public class FileSnapshot { public Folder OrganizationalFolder { get; } public Folder ParentFolder { get; } public string PathNoRoot { get; } private string Root { get; } private int _FileId { get; set; } private string _Name { get; set; } private DateTime _ModifiedDate { get; set; } public DateTime CreatedDate { get; } public long Size { get; } private string _NamingPattern { get; set; } public FileStatus Status { get; set; } public int FileId { get { return _FileId; } set { //Since this file is getting an ID, that means we've been commited to the database, so change the Status to reflect this. Status = FileStatus.None; FileId = value; } } public DateTime ModifiedDate { get { return _ModifiedDate; } set { if (Status != FileStatus.PendingInsert) Status = FileStatus.PendingUpdate; _ModifiedDate = value; } } public string NamingPattern { get { return _NamingPattern; } set { if (Status != FileStatus.PendingInsert) Status = FileStatus.PendingUpdate; _NamingPattern = value; } } public string Name { get { return _Name; } } public FileSnapshot(DB.FileSnapshot file, Folder organizationalFolder, Folder parentFolder, string rootPath) { OrganizationalFolder = organizationalFolder; ParentFolder = parentFolder; var db = new DB(); if (file.PatternId != 0) _NamingPattern = db.GetNamingPatternById(file.PatternId); PathNoRoot = file.PathNoRoot; Root = rootPath; _Name = file.Name; _ModifiedDate = file.ModifiedDate; CreatedDate = file.CreationDate; Size = file.Size; _FileId = file.Id; Status = FileStatus.None; } public FileSnapshot(FileInfo file, Folder organizationalFolder, Folder parentFolder, string rootPath) { OrganizationalFolder = organizationalFolder; ParentFolder = parentFolder; _Name = file.Name; //Add one to the length to remove the trailing slash in the root path. PathNoRoot = file.FullName.Remove(0, rootPath.Length + 1); Root = rootPath; _ModifiedDate = file.LastWriteTimeUtc; CreatedDate = file.CreationTimeUtc; Size = file.Length; _FileId = 0; Status = FileStatus.PendingInsert; } public bool HasOwnNamingPattern { get { return !string.IsNullOrEmpty(NamingPattern); } } public string FullPath { get { return Path.Combine(Root, PathNoRoot); } } public bool HasPendingChanges { get { return Status != FileStatus.None; } } public enum FileStatus { PendingInsert, PendingUpdate, PendingDelete, Deleted, None } } public class SnapshotComparsion { } }