using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using KattekerCreator.Exceptions;
using Minimatch;
using Vestris.ResourceLib;

namespace KattekerCreator.Helper
{
    public static class Utils
    {
        public static bool CheckFileExistens(string filePath)
        {
            if (File.Exists(filePath)) return true;
            var file = filePath.Replace(Directory.GetParent(filePath) + "\\", "");
            Console.WriteLine("Checking the prerequisites has failed.");
            Console.WriteLine($"{file} is missing. Program exits now.");
            Console.ReadKey();
            return false;
        }

        public static bool IsFilesizeWrong(string filename)
        {
            var file = new FileInfo(filename);
            return !file.Exists || file.Length < 524288;
        }

        public static void CopyResources(string inFile, string outFile)
        {
            using (var ri = new ResourceInfo())
            {
                ri.Load(inFile);

                if (ri.ResourceTypes.Any(x => x.ResourceType == Kernel32.ResourceTypes.RT_GROUP_ICON))
                {
                    var groupIcon = ri[Kernel32.ResourceTypes.RT_GROUP_ICON];
                    var iconDictionary = groupIcon.FirstOrDefault() as IconDirectoryResource;
                    iconDictionary?.SaveTo(outFile);
                }
                if (ri.ResourceTypes.Any(x => x.ResourceType == Kernel32.ResourceTypes.RT_VERSION))
                {
                    var versionResource = (VersionResource)ri[Kernel32.ResourceTypes.RT_VERSION].First();
                    versionResource.Language = 0;
                    versionResource.SaveTo(outFile);
                }
            }
        }

        /// <summary>
        /// Get the signtool.exe command line. Returns "" if no certificate string is provided.
        /// </summary>
        /// <param name="certificate"></param>
        /// <param name="password"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static string GetSignToolCommandLine(string certificate, string password, string filename)
        {
            return string.IsNullOrWhiteSpace(certificate)
                ? string.Empty
                : $"\"{GetSignTool()}\" {GetSignToolParameters(certificate, password, filename)}";
        }

        public static string GetSignToolParameters(string certificate, string password, string filename)
        {
            return $"sign /fd SHA256 /t http://timestamp.digicert.com /f \"{certificate}\" /p {password} {filename}";
        }

        public static string GetSignTool()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "contrib", "signtool.exe");
        }


        public static void DeleteTempDir(string tempDir)
        {
#if !DEBUG
            try
            {
                Directory.Delete(tempDir, true);
            }
            catch
            {
                //silently ignore
            }
#endif
        }

        public static IList<FileInfo> EnumerateFiles(string path, IEnumerable<string> userdefinedFileExclusions = null)
        {
            if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
            var result = new DirectoryInfo(path).EnumerateFiles("*.*", SearchOption.AllDirectories).ToList();
            var minimatchOptions = new Options { AllowWindowsPaths = true };
            foreach (var pattern in FileExclusions(userdefinedFileExclusions))
            {
                var matcher = new Minimatcher(pattern, minimatchOptions);
                for (var index = result.Count - 1; index >= 0; index--)
                {
                    if (matcher.IsMatch(result[index].Name))
                    {
                        result.Remove(result[index]);
                    }
                }
            }

            return result;
        }

        private static IEnumerable<string> FileExclusions(IEnumerable<string> userdefinedfileExclusions = null)
        {
            yield return "*.pdb";
            yield return "*.tmp";
            yield return "*.obj";
            yield return "*.pch";
            yield return "*.vshost.exe*";
            yield return "squirrelHelperInfo.json";
            yield return "Katteker.config";

            if (userdefinedfileExclusions == null) yield break;
            foreach (var fileExclusion in userdefinedfileExclusions)
            {
                yield return fileExclusion;
            }
        }

        public static void SignExecutable(string certificate, string password, string filename)
        {
            if (!string.IsNullOrWhiteSpace(certificate))
            {
                var exitCode = ExecuteProcess(GetSignTool(), GetSignToolParameters(certificate, password, filename));
                if (exitCode != 0) throw new KattekerCreatorException("AppStub could not signed.");
            }
        }

        public static int ExecuteProcess(string executable, string arguments)
        {
            int exitCode;
            using (var p = new Process())
            {
                p.StartInfo = new ProcessStartInfo
                {
                    FileName = executable,
                    Arguments = arguments,
                    UseShellExecute = false
                };
                var sw = Stopwatch.StartNew();
                p.Start();
                p.WaitForExit();
                exitCode = p.ExitCode;
                Log.WriteInfoLine($"{Path.GetFileName(executable)} has exited with exit code: {exitCode} (Took: {sw.ElapsedMilliseconds}ms)");
            }

            return exitCode;
        }
    }
}