using System;
using System.IO;
using System.Windows;

namespace APE.model
{
    public interface FSNodeVisitor
    {
        void visit(RootNode i);
        void visit(DriveNode i);
        void visit(DirectoryNode i);
        void visit(FileNode i);
    }

	class FSExecuteVisitor : FSNodeVisitor {

		public void  visit(RootNode i)
		{
 			throw new NotImplementedException();
		}

		public void  visit(DriveNode i)
		{
 			throw new NotImplementedException();
		}

		public void  visit(DirectoryNode i)
		{
 			throw new NotImplementedException();
		}

		public void  visit(FileNode i)
		{
 			System.Diagnostics.Process.Start(i.getFullPath());
		}
	}

    class FSLoadVisitor : FSNodeVisitor
    {
        void FSNodeVisitor.visit(RootNode i)
        {
            DriveInfo[] allDrives = DriveInfo.GetDrives();
			i.children.Clear();
            foreach (DriveInfo d in allDrives)
            {
                if (d.DriveType != DriveType.Removable)
                {
                    if (d.IsReady)
                    {
                        i.children.Add(new DriveNode(i, d));
                    }
                }
            }
            i.loaded = true;
        }

        void FSNodeVisitor.visit(DriveNode i)
        {
            if (i.isReady)
            {
                loadContent(i);
                i.loaded = true;
            }
        }

        void FSNodeVisitor.visit(DirectoryNode i)
        {
            loadContent(i);
            i.loaded = true;
        }

        void FSNodeVisitor.visit(FileNode i)
        {
            // Does not load any content; 
			// Goal is to simplify recursion.
            i.loaded = true;
        }

        private void loadContent(AbstractNode i)
        {
			i.children.Clear();
            DirectoryInfo[] subDirectories = (new DirectoryInfo(i.getFullPath())).GetDirectories();
            foreach (DirectoryInfo d in subDirectories)
            {
                if ((d.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
                i.children.Add(new DirectoryNode(i, d.Name));
            }
            FileInfo[] subFiles = (new DirectoryInfo(i.getFullPath())).GetFiles();
            foreach (FileInfo d in subFiles)
            {
                if ((d.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
				i.children.Add(new FileNode(i, d.Name));
            }
        }
    }

	class FSRenameVisitor : FSNodeVisitor
    {
		string renameToThis;

		public FSRenameVisitor(string newName) {
			renameToThis = newName;
		}

        class InvalidRenameException : Exception
        {
            public InvalidRenameException(string cause)
            {
                MessageBox.Show(cause);
            }
        }

        void FSNodeVisitor.visit(RootNode i)
        {
            throw new InvalidRenameException("Cannot rename RootNode!");
        }

        void FSNodeVisitor.visit(DriveNode i)
        {
            throw new InvalidRenameException("Cannot rename DriveNode!");
        }

        void FSNodeVisitor.visit(DirectoryNode i)
        {
			// Directory.Move does not like the same source and destination
            if (i.Name != renameToThis) 
            {
                Directory.Move(i.getFullPath(), i.getDirectory() + "\\" + renameToThis);
				i.Name = renameToThis;
            }
        }

        void FSNodeVisitor.visit(FileNode i)
        {
			// File.Move does not like the same source and destination either
			if (i.Name != renameToThis) 
            {
                File.Move(i.getFullPath(), i.getDirectory() + "\\" + renameToThis);
				i.Name = renameToThis;
            }
        }
    }

	class FSDeleteVisitor : FSNodeVisitor
    {
        class InvalidDeleteException : Exception
        {
            public InvalidDeleteException(string cause)
            {
                MessageBox.Show(cause);
            }
        }

        void FSNodeVisitor.visit(RootNode i)
        {
            throw new InvalidDeleteException("Cannot delete root node!");
        }

        void FSNodeVisitor.visit(DriveNode i)
        {
            throw new InvalidDeleteException("Cannot delete drive node!");
        }

        void FSNodeVisitor.visit(DirectoryNode i)
        {
            Directory.Delete(i.getFullPath(), true);
            i.Parent.children.Remove(i);
        }

        void FSNodeVisitor.visit(FileNode i)
        {
            File.Delete(i.getFullPath());
			i.Parent.children.Remove(i);
		}
    }

	class FSCopyVisitor : FSNodeVisitor
    {
        AbstractNode destinationNode;

        class InvalidCopyException : Exception
        {
            public InvalidCopyException(string cause)
            {
                MessageBox.Show(cause);
            }
        }

        public FSCopyVisitor(AbstractNode destinationNode_in)
        {
            destinationNode = destinationNode_in;
        }

        void FSNodeVisitor.visit(RootNode i)
        {
            throw new InvalidCopyException("Cannot copy root node!");
        }

        void FSNodeVisitor.visit(DriveNode i)
        {
            throw new InvalidCopyException("Cannot copy drive node!");
        }

        void FSNodeVisitor.visit(DirectoryNode i)
        {
			if (destinationNode != null) {
				directoryCopy(i.getFullPath(), destinationNode.getFullPath() + "\\" + i.Name, true);
				DirectoryNode newNode = new DirectoryNode(destinationNode.Parent, i.Name);
				destinationNode.children.Add(newNode);
			}
        }

        void FSNodeVisitor.visit(FileNode i)
        {
            File.Copy(i.getFullPath(), destinationNode.getFullPath() + "\\" + i.Name);
            FileNode newNode = new FileNode(destinationNode, i.Name);
            destinationNode.children.Add(newNode);
        }

        private static void directoryCopy(string sourceDirName, 
			string destDirName, bool copySubDirs) {
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);
            DirectoryInfo[] dirs = dir.GetDirectories();

            // If the source directory does not exist, throw an exception.
            if (!dir.Exists) {
                throw new DirectoryNotFoundException(
                    "Directory " + sourceDirName + 
					" does not exist or could not be found.");
            }

            // If the destination directory does not exist, create it.
            if (!Directory.Exists(destDirName)) {
                Directory.CreateDirectory(destDirName);
            }

            // Get subfiles of the directory to copy.
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files) {
                // Create the path to the new copy of the file.
                string temppath = Path.Combine(destDirName, file.Name);

                // Copy the file.
                file.CopyTo(temppath, false);
            }

            // If copySubDirs is true, copy the subdirectories.
            if (copySubDirs) {
                foreach (DirectoryInfo subdir in dirs) {
                    // Create the path of the new subdirectory.
                    string temppath = Path.Combine(destDirName, subdir.Name);

                    // Copy the subdirectories.
                    directoryCopy(subdir.FullName, temppath, copySubDirs);
                }
            }
        }
    }

	class FSMoveVisitor : FSNodeVisitor
    {
        AbstractNode destinationNode;

        class InvalidMoveException : Exception
        {
            public InvalidMoveException(string cause)
            {
                MessageBox.Show(cause);
            }
        }

        public FSMoveVisitor(AbstractNode f)
        {
            destinationNode = f;
        }

        void FSNodeVisitor.visit(RootNode i)
        {
            throw new InvalidMoveException("Cannot move root node!");
        }

        void FSNodeVisitor.visit(DriveNode i)
        {
            throw new InvalidMoveException("Cannot move drive node!");
        }

        void FSNodeVisitor.visit(DirectoryNode i)
        {
            Directory.Move(i.getFullPath(), 
				destinationNode.getFullPath() + "\\" + i.Name);
            i.Parent.children.Remove(i);
            destinationNode.children.Add(i);
        }

        void FSNodeVisitor.visit(FileNode i)
        {
            File.Move(i.getFullPath(), destinationNode.getFullPath() + "\\" + i.Name);
			i.Parent.children.Remove(i);
			destinationNode.children.Add(i);
        }
    }

}
