using Google.Protobuf;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity.Validation;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Core.Commands;
using Tango.Core.Helpers;
using Tango.Integration.Observables;
using Tango.Integration.Services;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Synchronization.Navigation;
using Tango.PMR.Integration;
using Tango.SharedUI;
using Tango.Synchronization;
using Tango.Synchronization.Local;
using Tango.Synchronization.Remote;
namespace Tango.MachineStudio.Synchronization.ViewModels
{
///
/// Represents the 'Direct Synchronization' view model.
///
///
public class DirectSynchronizationViewVM : ViewModel
{
private SyncNavigationManager _navigation;
private String _slaveDBFile;
private String _masterDBFile;
private LocalDBComparer _comparer;
private INotificationProvider _notification;
private String _comparedSerialNumber;
private MainViewVM _mainView;
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The application manager.
/// The navigation.
/// The notification.
public DirectSynchronizationViewVM(IStudioApplicationManager applicationManager, SyncNavigationManager navigation, INotificationProvider notification)
{
ApplicationManager = applicationManager;
_navigation = navigation;
_notification = notification;
BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView));
Differences = new ObservableCollection();
CompareCommand = new RelayCommand(Compare, (x) => !IsWorking && SelectedMachine != null);
CommitAllCommand = new RelayCommand(Synchronize, (x) => Differences.Count > 0 && !IsWorking && SelectedMachine != null);
}
#endregion
#region Commands
///
/// Gets or sets the back command.
///
public RelayCommand BackCommand { get; set; }
///
/// Gets or sets the browse master database command.
///
public RelayCommand BrowseMasterDBCommand { get; set; }
///
/// Gets or sets the browse slave database command.
///
public RelayCommand BrowseSlaveDBCommand { get; set; }
///
/// Gets or sets the compare command.
///
public RelayCommand CompareCommand { get; set; }
///
/// Gets or sets the commit command.
///
public RelayCommand CommitCommand { get; set; }
///
/// Gets or sets the commit all command.
///
public RelayCommand CommitAllCommand { get; set; }
///
/// Gets or sets the clean command.
///
public RelayCommand CleanCommand { get; set; }
#endregion
#region Properties
public IStudioApplicationManager ApplicationManager { get; set; }
private bool _isWorking;
///
/// Gets or sets a value indicating whether this instance is working.
///
public bool IsWorking
{
get { return _isWorking; }
set { _isWorking = value; RaisePropertyChangedAuto(); }
}
private bool _isClearMachine;
///
/// Gets or sets a value indicating whether this instance is clear machine.
///
public bool IsClearMachine
{
get { return _isClearMachine; }
set
{
_isClearMachine = value;
RaisePropertyChangedAuto();
}
}
private ObservableCollection _differences;
///
/// Gets or sets the differences.
///
public ObservableCollection Differences
{
get { return _differences; }
set { _differences = value; RaisePropertyChanged(nameof(Differences)); }
}
private Diff _selectedDifference;
///
/// Gets or sets the selected difference.
///
public Diff SelectedDifference
{
get { return _selectedDifference; }
set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); }
}
private Machine _selectedMachine;
///
/// Gets or sets the selected machine.
///
public Machine SelectedMachine
{
get { return _selectedMachine; }
set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
}
#endregion
#region Event Handlers
private void Comparer_Progress(object sender, string e)
{
if (_mainView == null)
{
_mainView = ServiceLocator.Current.GetInstance();
_mainView.Log = String.Empty;
}
_mainView.Log += ("[" + DateTime.Now.ToTimeString() + "] " + e + Environment.NewLine);
}
#endregion
#region Private Methods
///
/// Compares the selected machine against the remote database.
///
private void Compare()
{
if (SelectedMachine.SerialNumber != ApplicationManager.ConnectedMachine.As().SerialNumber)
{
if (!_notification.ShowQuestion("The selected machine serial number does not match the connected machine. Are you sure you want to continue?"))
{
return;
}
}
Task.Factory.StartNew(async () =>
{
using (_notification.PushTaskItem("Downloading machine database..."))
{
try
{
IsWorking = true;
InvalidateRelayCommands();
Thread.Sleep(1500);
ApplicationManager.ConnectedMachine.UseKeepAlive = false;
var response = await ApplicationManager.ConnectedMachine.SendRequest(new DirectSynchronizationRequest(), TimeSpan.FromSeconds(60));
using (_notification.PushTaskItem("Generating temporary files..."))
{
String tempFolder = PathHelper.GetTempFolderPath();
//File path for the reflected remote data base SQLite.
_masterDBFile = Path.Combine(tempFolder, "Remote.db");
//File path for the received machine SQLite db.
_slaveDBFile = Path.Combine(tempFolder, "Local.db");
//Save the machine db to file.
File.WriteAllBytes(_slaveDBFile, response.Message.LocalDB.ToByteArray());
//Copy the SQLite db template.
File.Copy(Path.Combine(PathHelper.GetStartupPath(), "Tango.db"), _masterDBFile);
//Synchronize the SQL Server db with the new SQLite template. (Overwrite basically)
RemoteDBSynchronizer.Synchronize(_masterDBFile, SelectedMachine.SerialNumber, true);
_comparedSerialNumber = SelectedMachine.SerialNumber;
using (_notification.PushTaskItem("Comparing database..."))
{
_comparer = new LocalDBComparer(new SQLiteDataBase(_masterDBFile), new SQLiteDataBase(_slaveDBFile));
_comparer.Progress += Comparer_Progress;
var diffs = _comparer.Compare();
Differences = new ObservableCollection(diffs);
if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0)
{
ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences.");
}
else
{
ShowInfo("The machine database is synchronized.");
}
}
}
}
catch (DbEntityValidationException ex)
{
String message = "The following validation errors occurred." + Environment.NewLine + Environment.NewLine;
foreach (var error in ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).ToList())
{
message += error.ErrorMessage + Environment.NewLine;
}
ShowError(message);
}
catch (Exception ex)
{
ShowError(ex.Message);
}
finally
{
IsWorking = false;
SelectedDifference = null;
InvalidateRelayCommands();
ApplicationManager.ConnectedMachine.UseKeepAlive = true;
}
}
});
}
///
/// Synchronizes the selected machine with the remote database.
///
private void Synchronize()
{
if (IsClearMachine)
{
if (!_notification.ShowQuestion("This will erase and override the existing machine database. Do you want to proceed?"))
{
return;
}
}
if (SelectedMachine.SerialNumber != _comparedSerialNumber)
{
_notification.ShowError("You have selected a different machine serial number after comparing. Please compare again if you wish to synchronize a different machine.");
return;
}
Task.Factory.StartNew(async () =>
{
using (_notification.PushTaskItem("Synchronizing..."))
{
try
{
ApplicationManager.ConnectedMachine.UseKeepAlive = false;
IsWorking = true;
InvalidateRelayCommands();
Thread.Sleep(1500);
for (int i = 0; i < Differences.Count; i++)
{
var diff = Differences[i];
diff.Commit();
InvokeUINow(() => Differences.Remove(diff));
i--;
}
_comparer.Dispose();
byte[] remoteDbBytes = IsClearMachine ? File.ReadAllBytes(_masterDBFile) : File.ReadAllBytes(_slaveDBFile);
var response = await ApplicationManager.ConnectedMachine.SendRequest(new OverrideDataBaseRequest()
{
RemoteDB = ByteString.CopyFrom(remoteDbBytes)
}, TimeSpan.FromSeconds(30));
if (!response.Message.Successful)
{
ShowError("The remote machine has reported some error while trying to override the database.");
}
else
{
//Synchronize the SQL Server db with the synchronized master DB.
RemoteDBSynchronizer.Synchronize(_masterDBFile, _comparedSerialNumber, false);
PathHelper.TryDeleteFile(_slaveDBFile);
PathHelper.TryDeleteFile(_masterDBFile);
_slaveDBFile = null;
_masterDBFile = null;
InvalidateRelayCommands();
}
}
catch (Exception ex)
{
ShowError(ex.Message);
}
finally
{
IsWorking = false;
SelectedDifference = null;
InvalidateRelayCommands();
ApplicationManager.ConnectedMachine.UseKeepAlive = true;
}
}
});
}
///
/// Displays an error message.
///
/// The message.
private void ShowError(String message)
{
InvokeUINow(() => _notification.ShowError(message));
}
///
/// Displays an information message.
///
/// The message.
private void ShowInfo(String message)
{
InvokeUINow(() => _notification.ShowInfo(message));
}
#endregion
}
}