using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;

[assembly: AssemblyTitle("PANDA Setup")]
[assembly: AssemblyDescription("Installer für PANDA")]
[assembly: AssemblyProduct("PANDA")]
[assembly: AssemblyCompany("PANDA")]
[assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.4.0.0")]

namespace PandaSetup
{
    internal static class Program
    {
        [STAThread]
        private static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (args.Length == 1 && string.Equals(args[0], "--verify", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    ResourcePayload.Read("PANDA.Application.exe");
                    ResourcePayload.Read("PANDA.Uninstaller.exe");
                    Environment.ExitCode = 0;
                }
                catch
                {
                    Environment.ExitCode = 2;
                }
                return;
            }

            if (args.Length == 2 && string.Equals(args[0], "--screenshot", StringComparison.OrdinalIgnoreCase))
            {
                using (var form = new SetupForm())
                {
                    form.Show();
                    Application.DoEvents();
                    using (var bitmap = new Bitmap(form.Width, form.Height))
                    {
                        form.DrawToBitmap(bitmap, new Rectangle(Point.Empty, form.Size));
                        bitmap.Save(args[1], System.Drawing.Imaging.ImageFormat.Png);
                    }
                    form.Close();
                }
                return;
            }

            Application.Run(new SetupForm());
        }
    }

    internal static class ResourcePayload
    {
        public static byte[] Read(string name)
        {
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
            {
                if (stream == null)
                    throw new InvalidDataException("Installationsdatei fehlt: " + name);
                var bytes = new byte[stream.Length];
                int offset = 0;
                while (offset < bytes.Length)
                {
                    int read = stream.Read(bytes, offset, bytes.Length - offset);
                    if (read == 0) break;
                    offset += read;
                }
                if (offset != bytes.Length || bytes.Length < 1024)
                    throw new InvalidDataException("Installationsdatei ist unvollständig: " + name);
                return bytes;
            }
        }
    }

    internal sealed class SetupForm : Form
    {
        private readonly Color Navy = Color.FromArgb(24, 38, 58);
        private readonly Color Blue = Color.FromArgb(41, 112, 255);
        private readonly Color Background = Color.FromArgb(244, 247, 251);
        private readonly Color Muted = Color.FromArgb(94, 108, 128);
        private readonly TextBox installPath = new TextBox();
        private readonly CheckBox desktopShortcut = new CheckBox();
        private readonly CheckBox startMenuShortcut = new CheckBox();
        private readonly CheckBox launchAfterInstall = new CheckBox();
        private readonly Button installButton = new Button();
        private readonly Label statusLabel = new Label();

        public SetupForm()
        {
            Text = "PANDA Setup";
            StartPosition = FormStartPosition.CenterScreen;
            FormBorderStyle = FormBorderStyle.FixedDialog;
            MaximizeBox = false;
            MinimizeBox = false;
            ClientSize = new Size(720, 520);
            BackColor = Background;
            Font = new Font("Segoe UI", 9F);
            Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath) ?? SystemIcons.Application;
            BuildLayout();
        }

        private void BuildLayout()
        {
            var root = new TableLayoutPanel
            {
                Dock = DockStyle.Fill,
                ColumnCount = 1,
                RowCount = 4,
                Padding = new Padding(30, 24, 30, 22),
                BackColor = Background
            };
            root.RowStyles.Add(new RowStyle(SizeType.Absolute, 100));
            root.RowStyles.Add(new RowStyle(SizeType.Absolute, 120));
            root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
            root.RowStyles.Add(new RowStyle(SizeType.Absolute, 74));
            Controls.Add(root);

            var heading = new Panel { Dock = DockStyle.Fill };
            heading.Controls.Add(new Label
            {
                Text = "PANDA installieren",
                Font = new Font("Segoe UI Semibold", 22F),
                ForeColor = Navy,
                AutoSize = true,
                Location = new Point(0, 0)
            });
            heading.Controls.Add(new Label
            {
                Text = "Pseudonymisierung alphanumerischer Nutzdaten durch Alphabetverschiebung",
                ForeColor = Muted,
                AutoSize = true,
                Location = new Point(2, 48)
            });
            heading.Controls.Add(new Label
            {
                Text = "Version 1.4.0  •  Installation für den aktuellen Windows-Benutzer",
                ForeColor = Blue,
                AutoSize = true,
                Location = new Point(2, 73)
            });
            root.Controls.Add(heading, 0, 0);

            var location = new TableLayoutPanel
            {
                Dock = DockStyle.Fill,
                ColumnCount = 2,
                RowCount = 3,
                BackColor = Color.White,
                Padding = new Padding(18, 14, 18, 12),
                Margin = new Padding(0, 0, 0, 14)
            };
            location.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            location.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 110));
            location.RowStyles.Add(new RowStyle(SizeType.Absolute, 26));
            location.RowStyles.Add(new RowStyle(SizeType.Absolute, 34));
            location.RowStyles.Add(new RowStyle(SizeType.Absolute, 26));
            var locationTitle = new Label
            {
                Text = "INSTALLATIONSORDNER",
                Font = new Font("Segoe UI Semibold", 9F),
                ForeColor = Navy,
                AutoSize = true,
                Anchor = AnchorStyles.Left
            };
            location.Controls.Add(locationTitle, 0, 0);
            location.SetColumnSpan(locationTitle, 2);
            installPath.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "PANDA");
            installPath.Dock = DockStyle.Fill;
            installPath.ForeColor = Navy;
            location.Controls.Add(installPath, 0, 1);
            var browse = new Button
            {
                Text = "Durchsuchen …",
                Anchor = AnchorStyles.None,
                Size = new Size(102, 28),
                FlatStyle = FlatStyle.Flat,
                BackColor = Color.White,
                ForeColor = Navy,
                Margin = new Padding(8, 0, 0, 0)
            };
            browse.FlatAppearance.BorderColor = Color.FromArgb(206, 216, 230);
            browse.Click += delegate { BrowseForFolder(); };
            location.Controls.Add(browse, 1, 1);
            var note = new Label
            {
                Text = "Keine Administratorrechte erforderlich.",
                ForeColor = Muted,
                AutoSize = true,
                Anchor = AnchorStyles.Left
            };
            location.Controls.Add(note, 0, 2);
            location.SetColumnSpan(note, 2);
            root.Controls.Add(location, 0, 1);

            var options = new Panel { Dock = DockStyle.Fill, BackColor = Color.White, Padding = new Padding(18, 14, 18, 12) };
            options.Controls.Add(new Label
            {
                Text = "OPTIONEN",
                Font = new Font("Segoe UI Semibold", 9F),
                ForeColor = Navy,
                AutoSize = true,
                Location = new Point(18, 16)
            });
            desktopShortcut.Text = "Desktop-Verknüpfung erstellen";
            desktopShortcut.Checked = true;
            desktopShortcut.AutoSize = true;
            desktopShortcut.ForeColor = Navy;
            desktopShortcut.Location = new Point(20, 52);
            startMenuShortcut.Text = "Eintrag im Startmenü erstellen";
            startMenuShortcut.Checked = true;
            startMenuShortcut.AutoSize = true;
            startMenuShortcut.ForeColor = Navy;
            startMenuShortcut.Location = new Point(20, 82);
            launchAfterInstall.Text = "PANDA nach der Installation starten";
            launchAfterInstall.Checked = true;
            launchAfterInstall.AutoSize = true;
            launchAfterInstall.ForeColor = Navy;
            launchAfterInstall.Location = new Point(20, 112);
            options.Controls.Add(desktopShortcut);
            options.Controls.Add(startMenuShortcut);
            options.Controls.Add(launchAfterInstall);
            root.Controls.Add(options, 0, 2);

            var footer = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 1, Padding = new Padding(0, 18, 0, 0) };
            footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 190));
            footer.RowStyles.Add(new RowStyle(SizeType.Absolute, 38));
            statusLabel.Text = "Bereit zur Installation.";
            statusLabel.ForeColor = Muted;
            statusLabel.Dock = DockStyle.Fill;
            statusLabel.TextAlign = ContentAlignment.MiddleLeft;
            footer.Controls.Add(statusLabel, 0, 0);
            installButton.Text = "Jetzt installieren";
            installButton.Dock = DockStyle.Fill;
            installButton.Margin = new Padding(0);
            installButton.FlatStyle = FlatStyle.Flat;
            installButton.FlatAppearance.BorderSize = 0;
            installButton.BackColor = Blue;
            installButton.ForeColor = Color.White;
            installButton.Cursor = Cursors.Hand;
            installButton.Click += delegate { Install(); };
            footer.Controls.Add(installButton, 1, 0);
            root.Controls.Add(footer, 0, 3);
            AcceptButton = installButton;
        }

        private void BrowseForFolder()
        {
            using (var dialog = new FolderBrowserDialog())
            {
                dialog.Description = "Installationsordner für PANDA auswählen";
                dialog.SelectedPath = installPath.Text;
                if (dialog.ShowDialog(this) == DialogResult.OK)
                    installPath.Text = Path.Combine(dialog.SelectedPath, "PANDA");
            }
        }

        private void Install()
        {
            string target;
            try
            {
                target = Path.GetFullPath(installPath.Text.Trim());
                if (string.Equals(target, Path.GetPathRoot(target), StringComparison.OrdinalIgnoreCase))
                    throw new InvalidOperationException("Bitte wähle keinen Laufwerks-Stammordner aus.");

                installButton.Enabled = false;
                statusLabel.Text = "PANDA wird installiert …";
                Cursor = Cursors.WaitCursor;
                Application.DoEvents();

                Directory.CreateDirectory(target);
                string applicationPath = Path.Combine(target, "PANDA.exe");
                string uninstallerPath = Path.Combine(target, "PANDA-Uninstall.exe");
                File.WriteAllBytes(applicationPath, ResourcePayload.Read("PANDA.Application.exe"));
                File.WriteAllBytes(uninstallerPath, ResourcePayload.Read("PANDA.Uninstaller.exe"));

                string desktopLink = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "PANDA.lnk");
                if (desktopShortcut.Checked)
                    Shortcut.Create(desktopLink, applicationPath, target, "PANDA starten");
                else if (File.Exists(desktopLink))
                    File.Delete(desktopLink);

                string startMenuFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), "PANDA");
                if (startMenuShortcut.Checked)
                {
                    Directory.CreateDirectory(startMenuFolder);
                    Shortcut.Create(Path.Combine(startMenuFolder, "PANDA.lnk"), applicationPath, target, "PANDA starten");
                    Shortcut.Create(Path.Combine(startMenuFolder, "PANDA deinstallieren.lnk"), uninstallerPath, target, "PANDA deinstallieren");
                }
                else if (Directory.Exists(startMenuFolder))
                {
                    string oldApplicationLink = Path.Combine(startMenuFolder, "PANDA.lnk");
                    string oldUninstallLink = Path.Combine(startMenuFolder, "PANDA deinstallieren.lnk");
                    if (File.Exists(oldApplicationLink)) File.Delete(oldApplicationLink);
                    if (File.Exists(oldUninstallLink)) File.Delete(oldUninstallLink);
                    if (Directory.GetFileSystemEntries(startMenuFolder).Length == 0) Directory.Delete(startMenuFolder, false);
                }

                RegisterUninstaller(target, applicationPath, uninstallerPath);
                statusLabel.Text = "Installation erfolgreich abgeschlossen.";
                Cursor = Cursors.Default;
                MessageBox.Show(this, "PANDA wurde erfolgreich installiert.\r\n\r\nDie Deinstallation ist über Windows › Installierte Apps ‹ oder das Startmenü möglich.", "PANDA installiert", MessageBoxButtons.OK, MessageBoxIcon.Information);
                if (launchAfterInstall.Checked)
                    Process.Start(applicationPath);
                Close();
            }
            catch (Exception exception)
            {
                Cursor = Cursors.Default;
                installButton.Enabled = true;
                statusLabel.Text = "Installation fehlgeschlagen.";
                MessageBox.Show(this, "PANDA konnte nicht installiert werden.\r\n\r\n" + exception.Message + "\r\n\r\nFalls PANDA bereits läuft, schließe das Programm und versuche es erneut.", "Installationsfehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private static void RegisterUninstaller(string target, string applicationPath, string uninstallerPath)
        {
            using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA"))
            {
                key.SetValue("DisplayName", "PANDA");
                key.SetValue("DisplayVersion", "1.4.0");
                key.SetValue("DisplayIcon", applicationPath);
                key.SetValue("Publisher", "PANDA");
                key.SetValue("InstallLocation", target);
                key.SetValue("UninstallString", "\"" + uninstallerPath + "\"");
                key.SetValue("QuietUninstallString", "\"" + uninstallerPath + "\" --silent");
                key.SetValue("NoModify", 1, RegistryValueKind.DWord);
                key.SetValue("NoRepair", 1, RegistryValueKind.DWord);
                key.SetValue("EstimatedSize", 100, RegistryValueKind.DWord);
            }
        }
    }

    internal static class Shortcut
    {
        public static void Create(string shortcutPath, string targetPath, string workingDirectory, string description)
        {
            Type shellType = Type.GetTypeFromProgID("WScript.Shell");
            if (shellType == null)
                throw new InvalidOperationException("Windows-Verknüpfungen werden auf diesem System nicht unterstützt.");
            object shell = Activator.CreateInstance(shellType);
            object shortcut = null;
            try
            {
                shortcut = shellType.InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shell, new object[] { shortcutPath });
                Type shortcutType = shortcut.GetType();
                shortcutType.InvokeMember("TargetPath", BindingFlags.SetProperty, null, shortcut, new object[] { targetPath });
                shortcutType.InvokeMember("WorkingDirectory", BindingFlags.SetProperty, null, shortcut, new object[] { workingDirectory });
                shortcutType.InvokeMember("Description", BindingFlags.SetProperty, null, shortcut, new object[] { description });
                shortcutType.InvokeMember("IconLocation", BindingFlags.SetProperty, null, shortcut, new object[] { targetPath + ",0" });
                shortcutType.InvokeMember("Save", BindingFlags.InvokeMethod, null, shortcut, null);
            }
            finally
            {
                if (shortcut != null && Marshal.IsComObject(shortcut)) Marshal.FinalReleaseComObject(shortcut);
                if (shell != null && Marshal.IsComObject(shell)) Marshal.FinalReleaseComObject(shell);
            }
        }
    }
}
