using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core.DI;
using Tango.FSE.Common;
using Tango.FSE.Common.FileAssociation;
using Tango.FSE.Common.FSEApplication;
using Tango.FSE.Common.Threading;
using Tango.Logging;
using ZetaIpc.Runtime.Server;
namespace Tango.FSE.UI.FileAssociation
{
[TangoCreateWhenRegistered]
public class DefaultFileAssociationProvider : FSEExtendedObject, IFileAssociationProvider
{
private Dictionary<String, Action<FileAssociationPackage>> _handlers;
private IpcServer _ipcServer;
private bool _initialized;
public const string FILE_ASSOCIATION_PREFIX = "-file";
public const int FILE_ASSOCIATION_ARGS_COUNT = 3;
public bool HasStartArgsPackage { get; private set; }
[TangoInject]
private IDispatcherProvider DispatcherProvider { get; set; }
[TangoInject]
private IFSEApplicationManager ApplicationManager { get; set; }
public DefaultFileAssociationProvider()
{
_handlers = new Dictionary<string, Action<FileAssociationPackage>>();
_ipcServer = new IpcServer();
}
public void RegisterFileAssociationHandler(string targetName, Action<FileAssociationPackage> handler)
{
_handlers.Add(targetName.ToLower(), handler);
}
public async void OnApplicationReady(IFSEApplicationManager applicationManager)
{
if (_initialized) return;
_initialized = true;
try
{
LogManager.Log("Starting file association IPC service...");
_ipcServer.Start(Settings.FileAssociationServicePort);
_ipcServer.ReceivedRequest += _ipcServer_ReceivedRequest;
}
catch (Exception ex)
{
LogManager.Log(ex, "Error starting file association IPC service. Another instance of the application might already be running...");
}
var args = applicationManager.StartupArgs;
LogManager.Log("Processing file association if any...");
if (args.Count == FILE_ASSOCIATION_ARGS_COUNT)
{
if (args[0] == FILE_ASSOCIATION_PREFIX)
{
//This delay is a workaround for when the invoked handler navigates to some module.
//The navigation system does not catches the navigation request properly... :/
await Task.Delay(500);
LogManager.Log($"File association package found: '{String.Join(" ", args)}'. Invoking handler...");
try
{
String targetName = args[1].ToLower();
if (_handlers.ContainsKey(targetName))
{
var package = new FileAssociationPackage()
{
TargetName = targetName,
File = args[2]
};
_handlers[targetName].Invoke(package);
}
else
{
LogManager.Log("File association handler not found.", LogCategory.Warning);
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error invoking file association handler for package '{String.Join(" ", args)}'.");
}
}
}
}
private void _ipcServer_ReceivedRequest(object sender, ReceivedRequestEventArgs e)
{
try
{
e.Handled = true;
LogManager.Log($"File association package received through IPC server.\n{e.Request}\nInvoking handler...");
var package = JsonConvert.DeserializeObject<FileAssociationPackage>(e.Request);
if (_handlers.ContainsKey(package.TargetName.ToLower()))
{
DispatcherProvider.Invoke(() =>
{
if (ApplicationManager.WindowState == System.Windows.WindowState.Minimized)
{
ApplicationManager.WindowState = System.Windows.WindowState.Normal;
}
ApplicationManager.ActivateMainWindow();
_handlers[package.TargetName.ToLower()].Invoke(package);
});
}
else
{
LogManager.Log($"Handler not found for target '{package.TargetName}'.", LogCategory.Warning);
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error invoking file association handler.");
}
}
public void OnApplicationStarted(IFSEApplicationManager applicationManager)
{
HasStartArgsPackage =
applicationManager.StartupArgs.Count == FILE_ASSOCIATION_ARGS_COUNT
&&
applicationManager.StartupArgs[0] == FILE_ASSOCIATION_PREFIX;
}
}
}