using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Tango.Core.Commands;
using Tango.Core.Helpers;
using Tango.FileSystem;
using Tango.FSE.Common;
using Tango.FSE.Common.Connection;
using Tango.FSE.Common.FileSystem;
using static Tango.SharedUI.Controls.NavigationControl;
namespace Tango.FSE.PPCConsole.ViewModels
{
///
/// Represents the remote file system view model.
///
///
///
public class FileSystemViewVM : FSEViewModel, INavigationViewModel
{
#region Properties
private FileSystemItem _currentItem;
///
/// Gets or sets the current file system item.
///
public FileSystemItem CurrentItem
{
get { return _currentItem; }
set { _currentItem = value; RaisePropertyChangedAuto(); OnCurrentItemChanged(); }
}
private String _currentPath;
///
/// Gets or sets the current remote path.
///
public String CurrentPath
{
get { return _currentPath; }
set { _currentPath = value; RaisePropertyChangedAuto(); }
}
private List _drives;
///
/// Gets or sets the file system drives.
///
public List Drives
{
get { return _drives; }
set { _drives = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the selected file system items.
///
public ObservableCollection SelectedItems { get; set; }
///
/// Gets or sets the list of file system handlers downloads/uploads.
///
public ObservableCollection FileSystemHandlers { get; set; }
#endregion
#region Commands
///
/// Navigates to the current path.
///
public RelayCommand NavigateCommand { get; set; }
///
/// Opens the current selected item (context menu).
///
public RelayCommand OpenItemCommand { get; set; }
///
/// Navigates back.
///
public RelayCommand BackCommand { get; set; }
///
/// Navigates to the specified special folder.
///
public RelayCommand NavigateSpecialFolderCommand { get; set; }
///
/// Navigates to the specified folder.
///
public RelayCommand NavigateToFolderCommand { get; set; }
///
/// Deletes the specified file system item (context menu or DEL key).
///
public RelayCommand> DeleteCommand { get; set; }
///
/// Handles the drag & drop of items from the operation system shell onto the file system view.
///
public RelayCommand> DropCommand { get; set; }
///
/// Handles the drag & drop of items from the file system view onto the operation system shell.
///
public RelayCommand> DragCommand { get; set; }
///
/// Removes the specified file system handler.
///
public RelayCommand DeleteFileSystemHandlerCommand { get; set; }
///
/// Opens the parent directory of a file system handler item.
/// A download will execute the local explorer.exe.
/// An upload will navigate to the remote parent folder.
///
public RelayCommand OpenFileSystemHandlerDestinationCommand { get; set; }
///
/// Not supported.
///
public RelayCommand RetryFailedFileSystemHandlerCommand { get; set; }
///
/// Handles the copy paste of remote items to a remote location.
///
public RelayCommand> CopyPasteCommand { get; set; }
///
/// Handles the cut parse of remote items to a remote location.
///
public RelayCommand> CutPasteCommand { get; set; }
///
/// Downloads the specified remote items.
///
public RelayCommand> DownloadCommand { get; set; }
///
/// Invokes a dialog for choosing items to upload and starts the upload.
///
public RelayCommand UploadCommand { get; set; }
///
/// Renames the specified file system item.
///
public RelayCommand RenameCommand { get; set; }
///
/// Creates a new folder at the current remote path.
///
public RelayCommand NewFolderCommand { get; set; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public FileSystemViewVM()
{
SelectedItems = new ObservableCollection();
FileSystemHandlers = new ObservableCollection();
NavigateCommand = new RelayCommand(NavigateToCurrentPath);
OpenItemCommand = new RelayCommand(OpenFileSystemItem);
BackCommand = new RelayCommand(NavigateBack, () => !(CurrentItem is FolderItem) || !(CurrentItem as FolderItem).IsRoot);
NavigateSpecialFolderCommand = new RelayCommand(NavigateToSpecialFolder);
NavigateToFolderCommand = new RelayCommand(async (x) => await Navigate(x));
DeleteCommand = new RelayCommand>(DeleteSelectedItems);
DragCommand = new RelayCommand>(OnItemsDraggedOut);
DropCommand = new RelayCommand>(OnItemsDroppedIn);
DeleteFileSystemHandlerCommand = new RelayCommand(DeleteFileSystemHandler);
OpenFileSystemHandlerDestinationCommand = new RelayCommand(OpenFileSystemHandlerDestination);
RetryFailedFileSystemHandlerCommand = new RelayCommand(RetryFailedFileSystemHandler);
CopyPasteCommand = new RelayCommand>((items) => PasteItems(items, false));
CutPasteCommand = new RelayCommand>((items) => PasteItems(items, true));
DownloadCommand = new RelayCommand>(DownloadSelectedItems);
RenameCommand = new RelayCommand(RenameFileSystemItem);
NewFolderCommand = new RelayCommand(CreateNewFolder);
UploadCommand = new RelayCommand(UploadFilesAndFolder);
}
#endregion
#region Override Methods
///
/// Called when the application has been started.
///
public override void OnApplicationStarted()
{
base.OnApplicationStarted();
MachineProvider.MachineConnected += MachineProvider_MachineConnected;
}
#endregion
#region Event Handlers
private async void MachineProvider_MachineConnected(object sender, MachineConnectedEventArgs e)
{
if (MachineProvider.ConnectionType.IsRemote())
{
if (e.DifferentFromPrevious)
{
await Navigate(null);
}
}
}
#endregion
#region Private Methods
private async void NavigateBack()
{
if (CurrentItem.Path.Length == 3)
{
await Navigate(null);
}
else
{
String parent = Path.GetDirectoryName(CurrentItem.Path);
await Navigate(parent);
}
}
private void NavigateToCurrentPath()
{
InvokeUI(async () =>
{
await Navigate(CurrentPath);
});
}
private async void OpenFileSystemItem(FileSystemItem item)
{
if (item == null) return;
if (item.Type == FileSystemItemType.Folder || item.Type == FileSystemItemType.Drive)
{
await Navigate(item.Path);
}
else if (item.Type == FileSystemItemType.File)
{
//TODO: Download/Open file?...
}
}
private async void DeleteSelectedItems(IList items)
{
if (items != null && items.Count > 0)
{
if (await NotificationProvider.ShowWarningQuestion($"Are you sure you want to delete {(items.Count == 1 ? $"'{items.First().Name}'" : $"the {items.Count} selected files/folders")}?", "DELETE"))
{
using (var task = NotificationProvider.PushTaskItem("Removing..."))
{
int remainingItems = items.Count;
foreach (var item in items)
{
task.UpdateProgress($"Removing '{item.Name}'...");
try
{
remainingItems--;
await FileSystemProvider.Delete(item);
}
catch (Exception ex)
{
LogManager.Log(ex, $"Could not remove '{item.Name}'.");
if (remainingItems > 0)
{
if (!await NotificationProvider.ShowWarningQuestion($"Could not remove '{item.Name}'.\n{ex.FlattenMessage()}\nDo you wish to continue removing the remaining items?"))
{
break;
}
}
else
{
await NotificationProvider.ShowError($"Could not remove '{item.Name}'.\n{ex.FlattenMessage()}");
}
}
}
}
NavigateToCurrentPath();
}
}
}
private async void OnItemsDraggedOut(List items)
{
foreach (var item in items.Where(x => x.FileSystemItem.Type != FileSystemItemType.Drive))
{
Debug.WriteLine($"Dropped out: {item.FileSystemItem.Name} => {item.Destination}");
if (File.Exists(Path.Combine(item.Destination, item.FileSystemItem.Name)) || Directory.Exists(Path.Combine(item.Destination, item.FileSystemItem.Name)))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{item.FileSystemItem.Name}' already exists on '{Path.GetDirectoryName(item.Destination)}'. Do you want to overwrite?"))
{
continue;
}
}
var handler = await FileSystemProvider.Download(item.FileSystemItem, item.Destination);
FileSystemHandlers.Insert(0, handler);
}
}
private async void OnItemsDroppedIn(List items)
{
String currentPathBefore = CurrentPath;
foreach (var item in items.Where(x => x.Type != FileSystemItemType.Drive))
{
Debug.WriteLine($"Dropped in: {item.Name} => {CurrentItem.Path}");
if ((CurrentItem as IFileSystemContainer).Items.ToList().Exists(x => x.Name.ToLower() == item.Name.ToLower()))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{item.Name}' already exists on '{CurrentItem.Name}'. Do you want to overwrite?"))
{
continue;
}
}
var handler = await FileSystemProvider.Upload(item.Path, CurrentItem);
handler.StatusChanged += (x, status) =>
{
if (status == FileSystemHandlerStatus.Completed && currentPathBefore == CurrentPath)
{
NavigateToCurrentPath();
}
};
FileSystemHandlers.Insert(0, handler);
}
}
private async void DeleteFileSystemHandler(FileSystemHandler handler)
{
if (handler.Status != FileSystemHandlerStatus.Completed && handler.Status != FileSystemHandlerStatus.Failed)
{
if (await NotificationProvider.ShowWarningQuestion($"This item is currently {handler.Status}. Do you wish to abort and delete this item?"))
{
try
{
handler.Abort();
}
catch { }
FileSystemHandlers.Remove(handler);
}
}
else
{
FileSystemHandlers.Remove(handler);
}
}
private async void OpenFileSystemHandlerDestination(FileSystemHandler handler)
{
String destination = String.Empty;
if (handler.Type == FileSystemHandlerType.FileDownload || handler.Type == FileSystemHandlerType.FolderDownload)
{
if (File.Exists(handler.Destination) || Directory.Exists(handler.Destination))
{
destination = handler.Destination;
Process.Start("explorer.exe", string.Format("/select,\"{0}\"", destination));
}
else
{
destination = Path.GetDirectoryName(handler.Destination);
Process.Start("explorer.exe", destination);
}
}
else
{
await Navigate(Path.GetDirectoryName(handler.Destination));
}
}
private async void RetryFailedFileSystemHandler(FileSystemHandler handler)
{
if (handler.Status == FileSystemHandlerStatus.Failed)
{
var newHandler = await FileSystemProvider.Download(handler.FileSystemItem, Path.GetDirectoryName(handler.Destination));
FileSystemHandlers.Replace(handler, newHandler);
}
}
private async void NavigateToSpecialFolder(string folder)
{
Environment.SpecialFolder specialFolder = (Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), folder);
await Navigate(null, specialFolder);
}
private async Task Navigate(String path, Environment.SpecialFolder? specialFolder = null)
{
try
{
IsFree = false;
Mouse.OverrideCursor = Cursors.AppStarting;
if (path != null)
{
CurrentItem = await FileSystemProvider.GetFolder(path) as FileSystemItem;
}
else if (specialFolder != null)
{
CurrentItem = await FileSystemProvider.GetSpecialFolder(specialFolder.Value) as FileSystemItem;
}
else
{
CurrentItem = await FileSystemProvider.GetThisPC() as FileSystemItem;
try
{
Drives = (CurrentItem as IFileSystemContainer).Items.Cast().ToList();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error setting file system drives menu.");
}
}
}
catch (Exception ex)
{
IsFree = true;
Mouse.OverrideCursor = null;
await NotificationProvider.ShowError($"Error navigating to the specified path.\n{ex.FlattenMessage()}");
}
finally
{
IsFree = true;
Mouse.OverrideCursor = null;
}
}
private async void PasteItems(List items, bool move = false)
{
using (var task = NotificationProvider.PushTaskItem("Please wait..."))
{
int remainingItems = items.Count;
foreach (var item in items)
{
Debug.WriteLine($"{(move ? "Cut" : "Copy")} Paste Item '{item.Name}' To '{CurrentItem.Name}'.");
try
{
remainingItems--;
if (move)
{
task.UpdateProgress($"Moving '{item.Name}'...");
await FileSystemProvider.Move(item, CurrentItem);
}
else
{
task.UpdateProgress($"Copying '{item.Name}'...");
await FileSystemProvider.Copy(item, CurrentItem);
}
}
catch (Exception ex)
{
string operation = move ? "move" : "copy";
LogManager.Log(ex, $"Could not {operation} '{item.Name}'.");
if (remainingItems > 0)
{
if (!await NotificationProvider.ShowWarningQuestion($"Could not {operation} '{item.Name}'.\n{ex.FlattenMessage()}\nDo you wish to continue with the remaining items?"))
{
break;
}
}
else
{
await NotificationProvider.ShowError($"Could not {operation} '{item.Name}'.\n{ex.FlattenMessage()}");
}
}
}
NavigateToCurrentPath();
}
}
private async void DownloadSelectedItems(List items)
{
var result = await StorageProvider.SelectFolder("Select download destination folder");
if (result)
{
String destination = result.SelectedItem;
Debug.WriteLine($"Download to {result.SelectedItem}");
foreach (var item in items.Where(x => x.Type != FileSystemItemType.Drive))
{
if (File.Exists(Path.Combine(destination, item.Name)) || Directory.Exists(Path.Combine(destination, item.Name)))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{item.Name}' already exists on '{Path.GetDirectoryName(destination)}'. Do you want to overwrite?"))
{
continue;
}
}
var handler = await FileSystemProvider.Download(item, destination);
FileSystemHandlers.Insert(0, handler);
}
}
}
private async void UploadFilesAndFolder()
{
var result = await StorageProvider.SelectFilesAndFolders("Select files and folders to upload");
if (result)
{
String currentPathBefore = CurrentPath;
foreach (var item in result.SelectedItems)
{
if (!File.Exists(item) && !Directory.Exists(item))
{
await NotificationProvider.ShowError($"File or folder '{item}' cannot be uploaded.");
return;
}
}
foreach (var item in result.SelectedItems)
{
String itemName = Path.GetFileName(item);
if ((CurrentItem as IFileSystemContainer).Items.ToList().Exists(x => x.Name.ToLower() == itemName.ToLower()))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{itemName}' already exists on '{CurrentItem.Name}'. Do you want to overwrite?"))
{
continue;
}
}
var handler = await FileSystemProvider.Upload(item, CurrentItem);
handler.StatusChanged += (x, status) =>
{
if (status == FileSystemHandlerStatus.Completed && currentPathBefore == CurrentPath)
{
NavigateToCurrentPath();
}
};
FileSystemHandlers.Insert(0, handler);
}
}
}
private async void RenameFileSystemItem(FileSystemItem item)
{
if (item.Type != FileSystemItemType.Drive)
{
var result = await NotificationProvider.ShowInputBox(
"Rename",
$"Please enter a new {(item.Type == FileSystemItemType.File ? "file" : "folder")} name and press 'ENTER'.",
PackIconKind.Rename, item.Name,
$"{(item.Type == FileSystemItemType.File ? "file" : "folder")} name",
100,
"RENAME");
if (result.Confirmed && result.Input != item.Name)
{
try
{
using (NotificationProvider.PushTaskItem("Renaming..."))
{
await FileSystemProvider.Rename(item, result.Input);
item.Path = Path.Combine(Path.GetDirectoryName(item.Path), result.Input);
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error renaming '{item.Path}' to {result.Input}.");
await NotificationProvider.ShowError($"Error renaming '{item.Name}'.\n{ex.FlattenMessage()}");
}
}
}
}
private async void CreateNewFolder()
{
if (CurrentItem == null) return;
if (CurrentItem is FolderItem)
{
if ((CurrentItem as FolderItem).IsRoot) return;
}
var result = await NotificationProvider.ShowInputBox(
"New Folder",
$"Please enter a folder name and press 'ENTER'.",
PackIconKind.FolderAdd, "untitled",
"folder name",
100,
"CREATE");
if (result.Confirmed)
{
try
{
using (NotificationProvider.PushTaskItem("Creating new folder..."))
{
var folderItem = await FileSystemProvider.CreateFolder(CurrentItem, result.Input);
NavigateToCurrentPath(); //Instead of inserting folder item just refresh the current path...
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error creating new folder '{Path.Combine(CurrentItem.Path, result.Input)}.");
await NotificationProvider.ShowError($"Error creating folder '{result.Input}'.\n{ex.FlattenMessage()}");
}
}
}
private void OnCurrentItemChanged()
{
CurrentPath = CurrentItem.Path;
InvalidateRelayCommands();
}
#endregion
}
}