using FluentFTP;
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Core.Commands;
using Tango.Core.DI;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Logging;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Navigation;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common.Update;
using Tango.SharedUI;
using Tango.MachineStudio.UI.Messages;
namespace Tango.MachineStudio.UI.ViewModels
{
public enum UpdateStatus
{
None,
CheckingForUpdate,
UpToDate,
UpdateAvailable,
Downloading,
Updating,
UpdateCompleted,
Error,
}
public class UpdateViewVM : ViewModel
{
private String _appPath = AppDomain.CurrentDomain.BaseDirectory;
private LogManager logManager = LogManager.Default;
private INotificationProvider _notification;
private INavigationManager _navigation;
private IStudioApplicationManager _application;
private IAuthenticationProvider _authentication;
private CheckForUpdatesResponse _updateInfo;
private TemporaryFolder _newPackageTempFolder;
private bool _forcedUpdate;
public bool ForcedUpdate
{
get { return _forcedUpdate; }
set { _forcedUpdate = value; RaisePropertyChangedAuto(); }
}
private UpdateStatus _status;
public UpdateStatus Status
{
get { return _status; }
set { _status = value; RaisePropertyChangedAuto(); }
}
private String _latestVersion;
public String LatestVersion
{
get { return _latestVersion; }
set { _latestVersion = value; RaisePropertyChangedAuto(); }
}
private String _updateComments;
///
/// Gets or sets the latest version comments.
///
public String UpdateComments
{
get { return _updateComments; }
set { _updateComments = value; RaisePropertyChangedAuto(); }
}
private double _downloadProgress;
public double DownloadProgress
{
get { return _downloadProgress; }
set { _downloadProgress = value; RaisePropertyChangedAuto(); }
}
private double _updateProgress;
public double UpdateProgress
{
get { return _updateProgress; }
set { _updateProgress = value; RaisePropertyChangedAuto(); }
}
private String _currentUpdateFile;
public String CurrentUpdateFile
{
get { return _currentUpdateFile; }
set { _currentUpdateFile = value; RaisePropertyChanged(nameof(CurrentUpdateFile)); }
}
public RelayCommand UpdateCommand { get; set; }
public RelayCommand BackCommand { get; set; }
public RelayCommand RestartCommand { get; set; }
public RelayCommand TryAgainCommand { get; set; }
public UpdateViewVM(INotificationProvider notification, IAuthenticationProvider authentication, INavigationManager navigation, IStudioApplicationManager application)
{
_notification = notification;
_navigation = navigation;
_application = application;
_authentication = authentication;
LatestVersion = "1.0.0.2";
Status = UpdateStatus.CheckingForUpdate;
UpdateCommand = new RelayCommand(StartUpdate, () => Status == UpdateStatus.UpdateAvailable);
BackCommand = new RelayCommand(BackToApplication, () => Status != UpdateStatus.Updating && !ForcedUpdate);
RestartCommand = new RelayCommand(RestartApplication, () => Status == UpdateStatus.UpdateCompleted);
TryAgainCommand = new RelayCommand(TryAgain, () => Status == UpdateStatus.Error);
TangoMessenger.Default.Register(HandleForcedUpdateMessage);
}
private void HandleForcedUpdateMessage(ForcedUpdateMessage msg)
{
ForcedUpdate = true;
InvalidateRelayCommands();
_updateInfo = msg.UpdateResponse;
Status = UpdateStatus.UpdateAvailable;
LatestVersion = _updateInfo.Version;
UpdateComments = _updateInfo.Comments;
StartUpdate();
}
public void OnNavigatedInto()
{
if (!ForcedUpdate)
{
CheckForUpdates();
}
}
private void CheckForUpdates()
{
Status = UpdateStatus.CheckingForUpdate;
ChannelFactory service = null;
Task.Factory.StartNew(() =>
{
try
{
Thread.Sleep(2000);
service = UpdateServiceHelper.GetUpdateServiceChannel();
var client = service.CreateChannel();
CheckForUpdatesResponse response = client.CheckForUpdates(new CheckForUpdatesRequest()
{
Email = _authentication.CurrentUser.Email,
Password = _authentication.CurrentUser.Password,
Version = _application.Version.ToString(),
});
if (response.IsUpdateAvailable)
{
_updateInfo = response;
Status = UpdateStatus.UpdateAvailable;
LatestVersion = response.Version;
UpdateComments = response.Comments;
}
else
{
Status = UpdateStatus.UpToDate;
}
}
catch (Exception ex)
{
logManager.Log(ex, "Error while checking for version update!");
Status = UpdateStatus.Error;
}
finally
{
if (service != null)
{
service.Close();
}
}
});
}
private void BackToApplication()
{
if (Status == UpdateStatus.Downloading)
{
if (!_notification.ShowQuestion("This will abort all update operations. Are you sure?"))
{
return;
}
}
_navigation.NavigateTo(NavigationView.MainView);
Status = UpdateStatus.None;
}
private void StartUpdate()
{
DownloadProgress = 0;
UpdateProgress = 0;
Status = UpdateStatus.Downloading;
Task.Factory.StartNew(() =>
{
var tempFile = TemporaryManager.CreateFile(".zip");
try
{
logManager.Log("Creating temporary file " + tempFile);
int fileSize = 0;
using (FileStreamWrapper fs = new FileStreamWrapper(tempFile.Path, FileMode.Create, (current) =>
{
InvokeUINow(() =>
{
Thread.Sleep(10);
DownloadProgress = ((double)current / (double)fileSize) * 100d;
});
}))
{
using (FtpClient ftp = new FtpClient(_updateInfo.FtpHost, _updateInfo.UserName, _updateInfo.Password))
{
logManager.Log("Connecting to FTP site: " + _updateInfo.FtpHost);
ftp.ConnectAsync().Wait();
logManager.Log("Retrieving download size...");
fileSize = (int)ftp.GetFileSize(_updateInfo.FilePath);
logManager.Log("Download size: " + fileSize + " bytes.");
logManager.Log("Starting download...");
ftp.DownloadAsync(fs, _updateInfo.FilePath).Wait();
}
}
Status = UpdateStatus.Updating;
_newPackageTempFolder = TemporaryManager.CreateFolder();
_newPackageTempFolder.Persist = true;
using (ZipFile zip = ZipFile.Read(tempFile.Path))
{
int currentEntry = 0;
zip.ExtractProgress += (x, args) =>
{
if (args.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
{
logManager.Log("Extracting " + Path.GetFileName(args.CurrentEntry.FileName));
UpdateProgress = ((double)(currentEntry++) / (double)zip.Entries.Count) * 100d;
}
};
foreach (ZipEntry entry in zip)
{
Thread.Sleep(10);
string newPath = Path.Combine(_newPackageTempFolder.Path, entry.FileName);
try
{
if (entry.IsDirectory)
{
Directory.CreateDirectory(newPath);
}
else
{
CurrentUpdateFile = Path.GetFileName(entry.FileName);
entry.Extract(_newPackageTempFolder.Path, ExtractExistingFileAction.OverwriteSilently);
}
}
catch
{
logManager.Log("Could not extract file " + entry.FileName);
}
}
}
TangoIOC.Default.GetInstance().DisableCheckForUpdates = true;
Status = UpdateStatus.UpdateCompleted;
}
catch (Exception ex)
{
logManager.Log(ex, "Error while extracting update package.");
Status = UpdateStatus.Error;
}
finally
{
tempFile.Delete();
}
});
}
private void TryAgain()
{
CheckForUpdates();
}
private void RestartApplication()
{
try
{
Process p = new Process();
p.StartInfo.FileName = _newPackageTempFolder + "\\Tango.MachineStudio.Updater.exe";
p.StartInfo.UseShellExecute = true;
p.StartInfo.Arguments = _appPath;
p.Start();
}
catch (Exception ex)
{
if (ex.Message == "The operation was canceled by the user")
{
_notification.ShowWarning("It seems like you refused to invoke our update utility. This prevents Machine Studio from completing the update process!");
return;
}
}
Environment.Exit(0);
}
protected override void RaisePropertyChangedAuto([CallerMemberName] string caller = null)
{
base.RaisePropertyChangedAuto(caller);
InvalidateRelayCommands();
}
}
}