/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
/* This work has been released under the CC0 1.0 Universal license!           */
/******************************************************************************/

using System;
using System.Diagnostics;
using System.IO;

using com.muldersoft.slunkcrypt.gui.utils;

namespace com.muldersoft.slunkcrypt.gui.process
{
    static class ExecutableHelper
    {
        private const string FILENAME_FORMAT = "slunkcrypt-cli-{0}.exe";

        // =============================================================================
        // Exception classes
        // =============================================================================

        public class ExecutableNotFoundException : FileNotFoundException
        {
            public ExecutableNotFoundException(string message, string fileName) : base(message, fileName)
            {
            }
        }
        
        // =============================================================================
        // Public methods
        // =============================================================================

        public static FileStream GetExecutableFile()
        {
            FileStream executableFile = null;
            string appBaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            CPUFeatures cpuFeatures = CPUFeatures.Features;
            if (cpuFeatures.cpuArch.Equals(CPUFeatures.CPUArch.a64))
            {
                if (CheckExecutableFile(ref executableFile, appBaseDirectory, "arm64"))
                {
                    Trace.Assert(executableFile != null);
                    return executableFile;
                }
            }
            if (cpuFeatures.cpuArch.Equals(CPUFeatures.CPUArch.x64))
            {
                if (cpuFeatures.hasAVX2 && CheckExecutableFile(ref executableFile, appBaseDirectory, "avx2"))
                {
                    Trace.Assert(executableFile != null);
                    return executableFile;
                }
                if (CheckExecutableFile(ref executableFile, appBaseDirectory, "x64"))
                {
                    Trace.Assert(executableFile != null);
                    return executableFile;
                }
            }
            if (cpuFeatures.hasSSE2 && CheckExecutableFile(ref executableFile, appBaseDirectory, "sse2"))
            {
                Trace.Assert(executableFile != null);
                return executableFile;
            }
            if (CheckExecutableFile(ref executableFile, appBaseDirectory, "i686"))
            {
                Trace.Assert(executableFile != null);
                return executableFile;
            }
            throw new ExecutableNotFoundException("SlunkCrypt executable file not found!", FILENAME_FORMAT);
        }

        // =============================================================================
        // Internal methods
        // =============================================================================

        private static bool CheckExecutableFile(ref FileStream executableFile, string appBaseDirectory, string suffix)
        {
            bool success = false;
            try
            {
                executableFile = new FileStream(Path.Combine(appBaseDirectory, String.Format(FILENAME_FORMAT, suffix)), FileMode.Open, FileAccess.Read, FileShare.Read);
                try
                {
                    FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(executableFile.Name);
                    Version appVersion = VersionInfo.Version;
                    if (string.Equals(fileVersion.FileDescription, "SlunkCrypt", StringComparison.OrdinalIgnoreCase) &&
                        string.Equals(fileVersion.CompanyName, "Muldersoft", StringComparison.OrdinalIgnoreCase) &&
                        (fileVersion.FileMajorPart == appVersion.Major) && (fileVersion.FileMinorPart == appVersion.Minor))
                    {
                        success = (fileVersion.FilePrivatePart >= appVersion.Revision);
                    }
                }
                finally
                {
                    if (!success)
                    {
                        executableFile.Dispose(); /*clean-up*/
                    }
                }
            }
            catch { }
            return success;
        }
    }
}
