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.BL.Enumerations;
using Tango.Core.Commands;
using Tango.FileSystem;
using Tango.FSE.Common;
using Tango.FSE.Common.Connection;
using Tango.FSE.Common.FileSystem;
using Tango.FSE.Common.Notifications;
namespace Tango.FSE.Firmware.ViewModels
{
public class FileSystemViewVM : FSEViewModel
{
#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(); }
}
///
/// 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 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; }
///
/// 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; }
///
/// 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);
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);
DownloadCommand = new RelayCommand>(DownloadSelectedItems);
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 (e.DifferentFromPrevious && e.MachineOperator.IsConnected && AuthenticationProvider.CurrentUser.HasPermission(Permissions.FSE_FirmwareFileSystemRead))
{
try
{
await Navigate(null, true);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error initializing firmware file system.");
NotificationProvider.PushSnackbarItem(MessageType.Warning, "Firmware Module Error", true, "Could not initialize the remote firmware file system provider.", TimeSpan.FromSeconds(10));
}
}
}
#endregion
#region Private Methods
private async void NavigateBack()
{
if (CurrentItem.Path.Length == 1)
{
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 FirmwareStorageProvider.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)
{
if (items.Exists(x => x.FileSystemItem.Type != FileSystemItemType.File))
{
await NotificationProvider.ShowError("Firmware file system does not support folder downloads.");
return;
}
foreach (var item in items)
{
String destination = Path.Combine(item.Destination, item.FileSystemItem.Name);
if (File.Exists(destination))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{item.FileSystemItem.Name}' already exists on '{item.Destination}'. Do you want to overwrite?"))
{
continue;
}
}
try
{
var handler = await FirmwareStorageProvider.Download(item.FileSystemItem as FileItem, destination);
FileSystemHandlers.Insert(0, handler);
}
catch (Exception ex)
{
await NotificationProvider.ShowError(ex.FlattenMessage());
}
}
}
private void OnItemsDroppedIn(List items)
{
UploadFiles(items.Select(x => x.Path).ToList());
}
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)
{
if (handler.Type == FileSystemHandlerType.FileDownload || handler.Type == FileSystemHandlerType.FolderDownload)
{
await StorageProvider.ShowInExplorer(handler.Destination);
}
else
{
await Navigate(Path.GetDirectoryName(handler.Destination));
}
}
private async Task Navigate(String path, bool throwError = false)
{
try
{
IsFree = false;
Mouse.OverrideCursor = Cursors.AppStarting;
if (path != null)
{
CurrentItem = await FirmwareStorageProvider.GetFolder(path) as FileSystemItem;
}
else
{
CurrentItem = await FirmwareStorageProvider.GetRoot() as FileSystemItem;
}
}
catch (Exception ex)
{
IsFree = true;
Mouse.OverrideCursor = null;
if (!throwError)
{
await NotificationProvider.ShowError($"Error navigating to the specified path.\n{ex.FlattenMessage()}");
}
else
{
throw ex;
}
}
finally
{
IsFree = true;
Mouse.OverrideCursor = null;
}
}
private async void DownloadSelectedItems(List items)
{
if (items.Exists(x => x.Type != FileSystemItemType.File))
{
await NotificationProvider.ShowError("Firmware file system does not support folder downloads.");
return;
}
if (items.Count == 1)
{
var fileItem = items.First() as FileItem;
var result = await StorageProvider.SaveFile("Select download destination file", $"{fileItem.Description}|*{fileItem.Extension}", fileItem.Name, fileItem.Extension);
if (result)
{
try
{
var handler = await FirmwareStorageProvider.Download(fileItem, result.SelectedItem);
FileSystemHandlers.Insert(0, handler);
}
catch (Exception ex)
{
await NotificationProvider.ShowError(ex.FlattenMessage());
}
}
}
else
{
var result = await StorageProvider.SelectFolder("Select download destination folder");
if (result)
{
foreach (var item in items.OfType())
{
String destination = Path.Combine(result.SelectedItem, item.Name);
if (File.Exists(destination))
{
if (!await NotificationProvider.ShowWarningQuestion($"'{item.Name}' already exists on '{result.SelectedItem}'. Do you want to overwrite?"))
{
continue;
}
}
try
{
var handler = await FirmwareStorageProvider.Download(item, destination);
FileSystemHandlers.Insert(0, handler);
}
catch (Exception ex)
{
await NotificationProvider.ShowError(ex.FlattenMessage());
}
}
}
}
}
private async void UploadFilesAndFolder()
{
var result = await StorageProvider.OpenFiles("Select files to upload");
if (result)
{
UploadFiles(result.SelectedItems);
}
}
private async void UploadFiles(List files)
{
try
{
String currentPathBefore = CurrentPath;
foreach (var file in files)
{
if (!File.Exists(file))
{
await NotificationProvider.ShowError($"File '{file}' cannot be uploaded.");
return;
}
}
foreach (var file in files)
{
String itemName = Path.GetFileName(file);
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 FirmwareStorageProvider.Upload(file, Path.Combine(CurrentItem.Path, Path.GetFileName(file)));
handler.StatusChanged += (x, status) =>
{
if (status == FileSystemHandlerStatus.Completed && currentPathBefore == CurrentPath)
{
NavigateToCurrentPath();
}
};
FileSystemHandlers.Insert(0, handler);
}
}
catch (Exception ex)
{
await NotificationProvider.ShowError($"Error uploading to file system.\n{ex.FlattenMessage()}");
}
}
private async void CreateNewFolder()
{
if (CurrentItem == null) 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 FirmwareStorageProvider.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
}
}