Newer
Older
Sunfish / src / Sunfish.WinForms / Program.cs
@Ivan Dominguez Ivan Dominguez 21 hours ago 5 KB 3.0
using System.IO.Pipes;
using System.Text;
using Sunfish.Config;
using Sunfish.Host;

namespace Sunfish.WinForms;

internal static class Program
{
    private const string MutexName = "Local\\Sunfish3-Gui";
    private const string PipeName = "Sunfish3-Gui";

    /// <summary>
    /// Sunfish.exe [CARPETA...] [-c FICHERO] [--lang es|en]
    /// Una carpeta como argumento (tambien arrastrandola sobre el .exe) se comparte al momento.
    /// Si ya hay una ventana abierta, se le pasan las carpetas y esta instancia termina.
    /// </summary>
    [STAThread]
    private static int Main(string[] args)
    {
        string? configPath = null;
        var folders = new List<string>();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] is "-c" or "--config" && i + 1 < args.Length)
                configPath = args[++i];
            else if (args[i] == "--lang" && i + 1 < args.Length)
                Strings.Force(args[++i]);
            else
                folders.Add(Path.GetFullPath(args[i]));
        }

        using var mutex = new Mutex(true, MutexName, out bool first);
        if (!first)
        {
            SendToRunningInstance(folders);
            return 0;
        }

        ApplicationConfiguration.Initialize();

        SunfishConfig config;
        string? found = ConfigLocator.Find(configPath);
        if (folders.Count > 0 && (found is null || !File.Exists(found)))
        {
            // Modo rapido: las carpetas sin fichero de configuracion; no se guarda nada.
            config = new SunfishConfig();
            foreach (var folder in folders.Where(Directory.Exists))
            {
                string name = ShareSettings.SuggestName(config, Path.GetFileName(Path.TrimEndingDirectorySeparator(folder)));
                config.Shares.Add(new ShareConfig
                {
                    Name = name,
                    Location = config.Shares.Count == 0 ? "/" : ShareSettings.SuggestLocation(config, name),
                    Path = folder,
                });
            }
            folders.Clear();
        }
        else if (found is not null && File.Exists(found))
        {
            config = SunfishConfig.Load(found);
        }
        else
        {
            var answer = MessageBox.Show(Texts.CreateConfigQuestion(ConfigLocator.PortablePath, ConfigLocator.UserPath),
                "Sunfish", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
            if (answer == DialogResult.Cancel)
                return 1;
            string created = answer == DialogResult.Yes ? ConfigLocator.PortablePath : ConfigLocator.UserPath;
            config = new SunfishConfig();
            // Si hay una configuracion de la 2.0 a mano, se ofrece traer sus shares.
            if (LegacyImport.Find() is { } legacy &&
                MessageBox.Show(Strings.ImportQuestion(legacy), "Sunfish", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                var notes = new List<string>();
                if (LegacyImport.FromV2(File.ReadAllText(legacy), notes) is { } imported)
                {
                    int count = LegacyImport.Merge(config, imported, isNew: true);
                    MessageBox.Show(string.Join("\n", notes.Prepend(Strings.ImportDone(count, created)).Select((n, i) => i == 0 ? n : "• " + n)),
                        "Sunfish", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    MessageBox.Show(Strings.ImportInvalid(legacy), "Sunfish", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
            config.Save(created);
        }

        var host = new SunfishHost(config);
        foreach (var error in config.Errors)
            host.Log.Warning(Strings.ConfigReloadError(error));
        host.WatchConfigFile();

        using var form = new MainForm(host, folders);
        _ = ListenForOtherInstancesAsync(form);
        Application.Run(form);
        host.DisposeAsync().AsTask().GetAwaiter().GetResult();
        return 0;
    }

    private static void SendToRunningInstance(List<string> folders)
    {
        try
        {
            using var pipe = new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
            pipe.Connect(2000);
            var data = Encoding.UTF8.GetBytes(string.Join('\n', folders) + "\n");
            pipe.Write(data, 0, data.Length);
        }
        catch (Exception e) when (e is IOException or TimeoutException) { }
    }

    /// <summary>Cada conexion trae carpetas (una por linea); sin carpetas solo se trae la ventana al frente.</summary>
    private static async Task ListenForOtherInstancesAsync(MainForm form)
    {
        while (!form.IsDisposed)
        {
            try
            {
                await using var pipe = new NamedPipeServerStream(PipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
                await pipe.WaitForConnectionAsync();
                using var reader = new StreamReader(pipe, Encoding.UTF8);
                string text = await reader.ReadToEndAsync();
                var folders = text.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
                if (!form.IsDisposed)
                    form.BeginInvoke(() => form.ReceiveFromOtherInstance(folders));
            }
            catch (IOException)
            {
                await Task.Delay(500);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
        }
    }
}