using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Win32;

namespace RevitToIfcConverter.Core
{
    public class RevitIntegration
    {
        public const string REVIT_APP_INITIALIZED_OK = "ok";
        public const string COMMON_SETTINGS_NAME = "CommonSettings";

        private static readonly string RevitRegistryFolder = @"SOFTWARE\Autodesk\Revit";
        private static readonly string ProductNameValueName = @"ProductName";
        private static readonly string ProductName = @"Autodesk Revit";
        private static readonly string InstallationLocationValueName = @"InstallationLocation";
        private static readonly string VersionValueName = @"Version";
        private static readonly string RevitExecutableName = @"Revit.exe";

        private static readonly string AddinManifestFormat =
            @"<?xml version = ""1.0"" encoding = ""utf-8"" ?>
<RevitAddIns>
    <AddIn Type = ""Application"">
        <Name>Revit To Ifc Converter</Name>
        <Assembly>{0}</Assembly>
        <ClientId>99F47221-074B-4575-83DA-66C06F702D20</ClientId>
        <FullClassName>{1}</FullClassName>
        <VendorId>ASCON</VendorId>
        <VendorDescription>ASCON, www.ascon.ru</VendorDescription>
    </AddIn>
</RevitAddIns>";

        public static VersionInfo GetLatestVersionInfo()
        {
            VersionInfo versionInfo;

            using (var key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(RevitRegistryFolder, false))
            {
                versionInfo = GetLatestVersionInfo(key, latestVersion: 0);
            }

            if (string.IsNullOrEmpty(versionInfo.ExecutablePath))
                throw new Exception("Revit installation has not been found.");

            if(versionInfo.Version == 0)
                throw new Exception("Unknown Revit version.");

            return versionInfo;
        }

        public static string BuildConverterPipeAddress(int revitProcessId)
        {
            return $"RevitConverterPipe{revitProcessId}";
        }

        public static string BuildInitializationPipeAddress(int revitProcessId)
        {
            return $"RevitInitializationPipe{revitProcessId}";
        }

        private static VersionInfo GetLatestVersionInfo(RegistryKey registryKey, uint latestVersion)
        {
            var path = string.Empty;
            uint outVersion = 0;
            var subKeyNames = registryKey.GetSubKeyNames();
            foreach (var subKeyName in subKeyNames)
            {
                using (var subKey = registryKey.OpenSubKey(subKeyName))
                {
                    if (subKey == null)
                        continue;

                    var productName = subKey.GetValue(ProductNameValueName) as string;
                    var installationPath = subKey.GetValue(InstallationLocationValueName) as string;
                    if (productName != null && productName.StartsWith(ProductName) && installationPath != null)
                    {
                        var version = TryGetVersion(subKey);
                        if (version < latestVersion)
                            continue;

                        latestVersion = version;
                        outVersion = latestVersion;
                        path = Path.Combine(installationPath, RevitExecutableName);
                    }

                    var pathFromSubKey = GetLatestVersionInfo(subKey, latestVersion);
                    if (!string.IsNullOrEmpty(pathFromSubKey.ExecutablePath))
                    {
                        path = pathFromSubKey.ExecutablePath;
                        outVersion = pathFromSubKey.Version;
                    }
                }
            }

            return new VersionInfo(path, outVersion);
        }

        private static uint TryGetVersion(RegistryKey key)
        {
            var versionStr = key.GetValue(VersionValueName) as string;
            if (string.IsNullOrEmpty(versionStr))
                return 0;

            var versionDigitsStr = versionStr.Split(' ').FirstOrDefault();
            return uint.TryParse(versionDigitsStr, out var version) ? version : 0;
        }

        public static void InitRevitAddinManifest(uint revitVersion, string converterName)
        {
            var revitAddinsFolder = GetAddinsFolder(revitVersion);
            var addinFilePath = Path.Combine(revitAddinsFolder, converterName + ".addin");
            var assemblyPath = Assembly.GetExecutingAssembly().Location;
            var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
            var addinClassName = $"{assemblyName}.{nameof(ExternalApplication)}";
            var addinManifest = string.Format(AddinManifestFormat, assemblyPath, addinClassName);

            File.WriteAllText(addinFilePath, addinManifest);
        }

        private static string GetAddinsFolder(uint revitVersion)
        {
            var programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            var revitAddinsFolder = Path.Combine(programData, "Autodesk", "Revit", "Addins", revitVersion.ToString());
            return revitAddinsFolder;
        }
    }

    public class VersionInfo
    {
        public string ExecutablePath { get; }
        public uint Version { get; }

        public VersionInfo(string executablePath, uint version)
        {
            ExecutablePath = executablePath;
            Version = version;
        }
    }
}
