using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

[assembly: AssemblyVersion("1.11.1")]
[assembly: AssemblyFileVersion("1.11.1")]
[assembly: AssemblyInformationalVersion("1.11.1")]

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new SetupForm());
    }
}

internal sealed class SetupForm : Form
{
    private readonly TextBox installPath;
    private readonly CheckBox acceptLicense;
    private readonly CheckBox addToPath;
    private readonly Button installButton;
    private readonly Button browseButton;
    private readonly Button cancelButton;
    private readonly Label statusLabel;
    private Image logoImage;

    public SetupForm()
    {
        Text = "Sura Language Setup";
        StartPosition = FormStartPosition.CenterScreen;
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;
        MinimizeBox = false;
        ClientSize = new Size(560, 462);
        Font = new Font("Segoe UI", 9F);

        Icon setupIcon = LoadInstallerIcon();
        if (setupIcon != null)
        {
            Icon = setupIcon;
        }

        logoImage = LoadInstallerLogo();
        if (logoImage != null)
        {
            PictureBox logo = new PictureBox();
            logo.Image = logoImage;
            logo.SizeMode = PictureBoxSizeMode.Zoom;
            logo.Location = new Point(424, 18);
            logo.Size = new Size(112, 112);
            Controls.Add(logo);
        }

        Label title = new Label();
        title.Text = "Install Sura";
        title.Font = new Font("Segoe UI", 18F, FontStyle.Bold);
        title.Location = new Point(20, 18);
        title.Size = new Size(390, 36);
        Controls.Add(title);

        Label subtitle = new Label();
        subtitle.Text = "Set up Sura Language, its package manager, and command launchers.";
        subtitle.Location = new Point(22, 58);
        subtitle.Size = new Size(390, 24);
        Controls.Add(subtitle);

        long payloadBytes = DirectorySize(PayloadDir());
        long estimatedInstallBytes = payloadBytes + 1048576L;
        Label sizeInfo = new Label();
        sizeInfo.Text = "Required space: about " + FormatByteSize(estimatedInstallBytes) + "  |  Package payload: " + FormatByteSize(payloadBytes);
        sizeInfo.Location = new Point(22, 92);
        sizeInfo.Size = new Size(390, 20);
        Controls.Add(sizeInfo);

        Label licenseLabel = new Label();
        licenseLabel.Text = "Installation actions";
        licenseLabel.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
        licenseLabel.Location = new Point(22, 136);
        licenseLabel.Size = new Size(200, 22);
        Controls.Add(licenseLabel);

        TextBox license = new TextBox();
        license.Multiline = true;
        license.ReadOnly = true;
        license.ScrollBars = ScrollBars.Vertical;
        license.Location = new Point(24, 160);
        license.Size = new Size(512, 108);
        license.Text = "Sura Language is installed as a per-user runtime. The installer copies the language engine and package manager, creates sura and surapkg command launchers, and can add the bin directory to your user PATH. You can uninstall it from this package with SuraSetup.ps1 -Uninstall.";
        Controls.Add(license);

        acceptLicense = new CheckBox();
        acceptLicense.Text = "I reviewed these installation actions";
        acceptLicense.Location = new Point(24, 282);
        acceptLicense.Size = new Size(300, 24);
        acceptLicense.CheckedChanged += delegate { installButton.Enabled = acceptLicense.Checked; };
        Controls.Add(acceptLicense);

        Label pathLabel = new Label();
        pathLabel.Text = "Install location";
        pathLabel.Font = new Font("Segoe UI", 10F, FontStyle.Bold);
        pathLabel.Location = new Point(22, 318);
        pathLabel.Size = new Size(200, 22);
        Controls.Add(pathLabel);

        installPath = new TextBox();
        installPath.Location = new Point(24, 342);
        installPath.Size = new Size(410, 24);
        installPath.Text = DefaultInstallDir();
        Controls.Add(installPath);

        browseButton = new Button();
        browseButton.Text = "Browse...";
        browseButton.Location = new Point(444, 341);
        browseButton.Size = new Size(92, 26);
        browseButton.Click += BrowseClicked;
        Controls.Add(browseButton);

        addToPath = new CheckBox();
        addToPath.Text = "Add Sura Language to my user PATH";
        addToPath.Checked = true;
        addToPath.Location = new Point(24, 376);
        addToPath.Size = new Size(260, 24);
        Controls.Add(addToPath);

        statusLabel = new Label();
        statusLabel.Text = "Ready.";
        statusLabel.Location = new Point(24, 408);
        statusLabel.Size = new Size(320, 24);
        Controls.Add(statusLabel);

        installButton = new Button();
        installButton.Text = "Install";
        installButton.Enabled = false;
        installButton.Location = new Point(350, 406);
        installButton.Size = new Size(86, 30);
        installButton.Click += InstallClicked;
        Controls.Add(installButton);

        cancelButton = new Button();
        cancelButton.Text = "Cancel";
        cancelButton.Location = new Point(450, 406);
        cancelButton.Size = new Size(86, 30);
        cancelButton.Click += delegate { Close(); };
        Controls.Add(cancelButton);
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        if (logoImage != null)
        {
            logoImage.Dispose();
            logoImage = null;
        }
        base.OnFormClosed(e);
    }

    private static string SupportFilePath(string fileName)
    {
        string rootFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
        if (File.Exists(rootFile))
        {
            return rootFile;
        }
        return Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "support"), fileName);
    }

    private static Image LoadInstallerLogo()
    {
        string path = SupportFilePath("SuraLogo.png");
        if (File.Exists(path))
        {
            return Image.FromFile(path);
        }
        return null;
    }

    private static Icon LoadInstallerIcon()
    {
        string path = SupportFilePath("SuraLogo.ico");
        if (File.Exists(path))
        {
            return new Icon(path);
        }
        return null;
    }

    private static string DefaultInstallDir()
    {
        string local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        if (!String.IsNullOrEmpty(local))
        {
            return Path.Combine(Path.Combine(local, "Programs"), "Sura");
        }
        string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        if (String.IsNullOrEmpty(home))
        {
            home = AppDomain.CurrentDomain.BaseDirectory;
        }
        return Path.Combine(home, ".sura");
    }

    private static string SetupScriptPath()
    {
        string rootScript = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SuraSetup.ps1");
        if (File.Exists(rootScript))
        {
            return rootScript;
        }
        return Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "support"), "SuraSetup.ps1");
    }

    private static string PayloadDir()
    {
        string rootPayload = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "payload");
        if (Directory.Exists(rootPayload))
        {
            return rootPayload;
        }
        return Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), "payload");
    }

    private static long DirectorySize(string path)
    {
        if (String.IsNullOrEmpty(path) || !Directory.Exists(path))
        {
            return 0L;
        }
        long total = 0L;
        foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
        {
            total += new FileInfo(file).Length;
        }
        return total;
    }

    private static string FormatByteSize(long bytes)
    {
        if (bytes >= 1073741824L)
        {
            return String.Format("{0:N1} GB", bytes / 1073741824.0);
        }
        if (bytes >= 1048576L)
        {
            return String.Format("{0:N1} MB", bytes / 1048576.0);
        }
        if (bytes >= 1024L)
        {
            return String.Format("{0:N1} KB", bytes / 1024.0);
        }
        return bytes + " bytes";
    }

    private static string PowerShellPath()
    {
        string ps = Path.Combine(Environment.SystemDirectory, "WindowsPowerShell\\v1.0\\powershell.exe");
        if (File.Exists(ps))
        {
            return ps;
        }
        return "powershell.exe";
    }

    private static string Quote(string value)
    {
        if (value == null)
        {
            value = "";
        }
        return "\"" + value.Replace("\"", "\\\"") + "\"";
    }

    private void BrowseClicked(object sender, EventArgs e)
    {
        using (FolderBrowserDialog dialog = new FolderBrowserDialog())
        {
            dialog.Description = "Choose where Sura Language should be installed";
            if (Directory.Exists(installPath.Text))
            {
                dialog.SelectedPath = installPath.Text;
            }
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                installPath.Text = dialog.SelectedPath;
            }
        }
    }

    private void InstallClicked(object sender, EventArgs e)
    {
        try
        {
            string script = SetupScriptPath();
            if (!File.Exists(script))
            {
                throw new InvalidOperationException("SuraSetup.ps1 was not found next to this installer.");
            }

            string chosenPath = Path.GetFullPath(installPath.Text.Trim());
            SetBusy(true);
            statusLabel.Text = "Installing...";
            Refresh();

            string args = "-NoProfile -ExecutionPolicy Bypass -File " + Quote(script) + " -InstallDir " + Quote(chosenPath) + " -Quiet";
            if (!addToPath.Checked)
            {
                args += " -NoPath";
            }

            ProcessStartInfo psi = new ProcessStartInfo(PowerShellPath(), args);
            psi.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
            psi.UseShellExecute = false;
            psi.CreateNoWindow = true;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            Process process = Process.Start(psi);
            StringBuilder output = new StringBuilder();
            process.OutputDataReceived += delegate(object outputSender, DataReceivedEventArgs outputEvent)
            {
                if (outputEvent.Data != null)
                {
                    lock (output) output.AppendLine(outputEvent.Data);
                }
            };
            process.ErrorDataReceived += delegate(object errorSender, DataReceivedEventArgs errorEvent)
            {
                if (errorEvent.Data != null)
                {
                    lock (output) output.AppendLine(errorEvent.Data);
                }
            };
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            process.WaitForExit();
            process.WaitForExit();
            if (process.ExitCode != 0)
            {
                string detail = output.ToString().Trim();
                if (detail.Length == 0)
                {
                    detail = "Installer exited with code " + process.ExitCode + ".";
                }
                throw new InvalidOperationException(detail);
            }

            statusLabel.Text = "Installed.";
            MessageBox.Show(this, "Sura Language was installed successfully. Open a new terminal before using the sura command.", "Sura Language Setup", MessageBoxButtons.OK, MessageBoxIcon.Information);
            Close();
        }
        catch (Exception ex)
        {
            statusLabel.Text = "Install failed.";
            MessageBox.Show(this, ex.Message, "Sura Language Setup", MessageBoxButtons.OK, MessageBoxIcon.Error);
            SetBusy(false);
        }
    }

    private void SetBusy(bool busy)
    {
        installButton.Enabled = !busy && acceptLicense.Checked;
        browseButton.Enabled = !busy;
        cancelButton.Enabled = !busy;
        acceptLicense.Enabled = !busy;
        addToPath.Enabled = !busy;
        installPath.Enabled = !busy;
    }
}