<commit_before>using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using JetBrains.Annotations;
using JetBrains.Diagnostics;
using JetBrains.Util;
using JetBrains.Util.Interop;
using JetBrains.Util.Logging;

namespace JetBrains.ReSharper.Plugins.Unity
{
    public static class UnityInstallationFinder
    {
        private static readonly ILogger ourLogger = Logger.GetLogger(typeof(UnityInstallationFinder));

        [CanBeNull]
        public static UnityInstallationInfo GetApplicationInfo(Version version, UnityVersion unityVersion)
        {
            var possible = GetPossibleInstallationInfos().ToArray();
            var possibleWithVersion = possible.Where(a => a.Version != null).ToList();

            // fast check is we have a best choice
            var bestChoice = TryGetBestChoice(version, possibleWithVersion);
            if (bestChoice != null)
                return bestChoice;
            
            // best choice not found by version - try version by path then
            var pathForSolution = unityVersion.GetActualAppPathForSolution();
            var versionByAppPath = UnityVersion.GetVersionByAppPath(pathForSolution);
            if (versionByAppPath!=null)
                possibleWithVersion.Add(new UnityInstallationInfo(versionByAppPath, pathForSolution));
            
            // check best choice again, since newly added version may be best one
            bestChoice = TryGetBestChoice(version, possibleWithVersion);
            if (bestChoice != null)
                return bestChoice;

            var choice1 = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor &&
                a.Version.Build == version.Build).OrderBy(b=>b.Version).LastOrDefault();
            if (choice1 != null)
                return choice1;
            var choice2 = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor).OrderBy(b=>b.Version).LastOrDefault();
            if (choice2 != null)
                return choice2;
            var choice3 =  possibleWithVersion.Where(a => a.Version.Major == version.Major)
                .OrderBy(b=>b.Version).LastOrDefault();
            if (choice3!=null)
                return choice3;
            var choice4 =  possibleWithVersion
                .OrderBy(b=>b.Version).LastOrDefault();
            if (choice4!=null)
                return choice4;

            var worstChoice = possible.LastOrDefault();
            return worstChoice;
        }

        private static UnityInstallationInfo TryGetBestChoice(Version version, List<UnityInstallationInfo> possibleWithVersion)
        {
            var bestChoice = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor &&
                a.Version.Build == version.Build && a.Version.Revision == version.Revision
            ).OrderBy(b => b.Version).LastOrDefault();
            return bestChoice;
        }

        [NotNull]
        public static FileSystemPath GetApplicationContentsPath(FileSystemPath applicationPath)
        {
            if (applicationPath.IsNullOrEmpty())
                return applicationPath;

            AssertApplicationPath(applicationPath);

            switch (PlatformUtil.RuntimePlatform)
            {
                    case PlatformUtil.Platform.MacOsX:
                        return applicationPath.Combine("Contents");
                    case PlatformUtil.Platform.Linux:
                    case PlatformUtil.Platform.Windows:
                        return applicationPath.Directory.Combine("Data");
            }
            ourLogger.Error("Unknown runtime platform");
            return FileSystemPath.Empty;
        }

        private static List<UnityInstallationInfo> GetPossibleInstallationInfos()
        {
            var installations = GetPossibleApplicationPaths();
            return installations.Select(path =>
            {
                var version = UnityVersion.GetVersionByAppPath(path);
                return new UnityInstallationInfo(version, path);
            }).ToList();
        }

        public static List<FileSystemPath> GetPossibleApplicationPaths()
        {
            switch (PlatformUtil.RuntimePlatform)
            {
                case PlatformUtil.Platform.MacOsX:
                {
                    var appsHome = FileSystemPath.Parse("/Applications");
                    var unityApps = appsHome.GetChildDirectories("Unity*").Select(a=>a.Combine("Unity.app")).ToList();

                    var defaultHubLocation = appsHome.Combine("Unity/Hub/Editor");
                    var hubLocations = new List<FileSystemPath> {defaultHubLocation};

                    // Hub custom location
                    var home = Environment.GetEnvironmentVariable("HOME");
                    if (!string.IsNullOrEmpty(home))
                    {
                        var localAppData = FileSystemPath.Parse(home).Combine("Library/Application Support");
                        var hubCustomLocation = GetCustomHubInstallPath(localAppData);
                        if (!hubCustomLocation.IsEmpty)
                            hubLocations.Add(hubCustomLocation);
                    }

                    // /Applications/Unity/Hub/Editor/2018.1.0b4/Unity.app
                    unityApps.AddRange(hubLocations.SelectMany(l=>l.GetChildDirectories().Select(unityDir =>
                        unityDir.Combine(@"Unity.app"))));

                    return unityApps.Where(a=>a.ExistsDirectory).Distinct().OrderBy(b=>b.FullPath).ToList();
                }
                case PlatformUtil.Platform.Linux:
                {
                    var unityApps = new List<FileSystemPath>();
                    var homeEnv = Environment.GetEnvironmentVariable("HOME");
                    var homes = new List<FileSystemPath> {FileSystemPath.Parse("/opt")};

                    unityApps.AddRange(
                        homes.SelectMany(a => a.GetChildDirectories("Unity*"))
                            .Select(unityDir => unityDir.Combine(@"Editor/Unity")));

                    if (homeEnv == null)
                        return unityApps;
                    var home = FileSystemPath.Parse(homeEnv);
                    homes.Add(home);

                    var defaultHubLocation = home.Combine("Unity/Hub/Editor");
                    var hubLocations = new List<FileSystemPath> {defaultHubLocation};
                    // Hub custom location
                    var configPath = home.Combine(".config");
                    var customHubInstallPath = GetCustomHubInstallPath(configPath);
                    if (!customHubInstallPath.IsEmpty)
                        hubLocations.Add(customHubInstallPath);

                    unityApps.AddRange(hubLocations.SelectMany(l=>l.GetChildDirectories().Select(unityDir =>
                        unityDir.Combine(@"Editor/Unity"))));

                    return unityApps.Where(a=>a.ExistsFile).Distinct().OrderBy(b=>b.FullPath).ToList();
                }

                case PlatformUtil.Platform.Windows:
                {
                    var unityApps = new List<FileSystemPath>();

                    var programFiles = GetProgramFiles();
                    unityApps.AddRange(
                        programFiles.GetChildDirectories("Unity*")
                            .Select(unityDir => unityDir.Combine(@"Editor\Unity.exe"))
                        );

                    // default Hub location
                    //"C:\Program Files\Unity\Hub\Editor\2018.1.0b4\Editor\Data\MonoBleedingEdge"
                    unityApps.AddRange(
                        programFiles.Combine(@"Unity\Hub\Editor").GetChildDirectories()
                            .Select(unityDir => unityDir.Combine(@"Editor\Unity.exe"))
                    );<commit_msg>more accurate searching for Unity installations on linux (#1763)<commit_after>using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using JetBrains.Annotations;
using JetBrains.Diagnostics;
using JetBrains.Util;
using JetBrains.Util.Interop;
using JetBrains.Util.Logging;

namespace JetBrains.ReSharper.Plugins.Unity
{
    public static class UnityInstallationFinder
    {
        private static readonly ILogger ourLogger = Logger.GetLogger(typeof(UnityInstallationFinder));

        [CanBeNull]
        public static UnityInstallationInfo GetApplicationInfo(Version version, UnityVersion unityVersion)
        {
            var possible = GetPossibleInstallationInfos().ToArray();
            var possibleWithVersion = possible.Where(a => a.Version != null).ToList();

            // fast check is we have a best choice
            var bestChoice = TryGetBestChoice(version, possibleWithVersion);
            if (bestChoice != null)
                return bestChoice;
            
            // best choice not found by version - try version by path then
            var pathForSolution = unityVersion.GetActualAppPathForSolution();
            var versionByAppPath = UnityVersion.GetVersionByAppPath(pathForSolution);
            if (versionByAppPath!=null)
                possibleWithVersion.Add(new UnityInstallationInfo(versionByAppPath, pathForSolution));
            
            // check best choice again, since newly added version may be best one
            bestChoice = TryGetBestChoice(version, possibleWithVersion);
            if (bestChoice != null)
                return bestChoice;

            var choice1 = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor &&
                a.Version.Build == version.Build).OrderBy(b=>b.Version).LastOrDefault();
            if (choice1 != null)
                return choice1;
            var choice2 = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor).OrderBy(b=>b.Version).LastOrDefault();
            if (choice2 != null)
                return choice2;
            var choice3 =  possibleWithVersion.Where(a => a.Version.Major == version.Major)
                .OrderBy(b=>b.Version).LastOrDefault();
            if (choice3!=null)
                return choice3;
            var choice4 =  possibleWithVersion
                .OrderBy(b=>b.Version).LastOrDefault();
            if (choice4!=null)
                return choice4;

            var worstChoice = possible.LastOrDefault();
            return worstChoice;
        }

        private static UnityInstallationInfo TryGetBestChoice(Version version, List<UnityInstallationInfo> possibleWithVersion)
        {
            var bestChoice = possibleWithVersion.Where(a =>
                a.Version.Major == version.Major && a.Version.Minor == version.Minor &&
                a.Version.Build == version.Build && a.Version.Revision == version.Revision
            ).OrderBy(b => b.Version).LastOrDefault();
            return bestChoice;
        }

        [NotNull]
        public static FileSystemPath GetApplicationContentsPath(FileSystemPath applicationPath)
        {
            if (applicationPath.IsNullOrEmpty())
                return applicationPath;

            AssertApplicationPath(applicationPath);

            switch (PlatformUtil.RuntimePlatform)
            {
                    case PlatformUtil.Platform.MacOsX:
                        return applicationPath.Combine("Contents");
                    case PlatformUtil.Platform.Linux:
                    case PlatformUtil.Platform.Windows:
                        return applicationPath.Directory.Combine("Data");
            }
            ourLogger.Error("Unknown runtime platform");
            return FileSystemPath.Empty;
        }

        private static List<UnityInstallationInfo> GetPossibleInstallationInfos()
        {
            var installations = GetPossibleApplicationPaths();
            return installations.Select(path =>
            {
                var version = UnityVersion.GetVersionByAppPath(path);
                return new UnityInstallationInfo(version, path);
            }).ToList();
        }

        public static List<FileSystemPath> GetPossibleApplicationPaths()
        {
            switch (PlatformUtil.RuntimePlatform)
            {
                case PlatformUtil.Platform.MacOsX:
                {
                    var appsHome = FileSystemPath.Parse("/Applications");
                    var unityApps = appsHome.GetChildDirectories("Unity*").Select(a=>a.Combine("Unity.app")).ToList();

                    var defaultHubLocation = appsHome.Combine("Unity/Hub/Editor");
                    var hubLocations = new List<FileSystemPath> {defaultHubLocation};

                    // Hub custom location
                    var home = Environment.GetEnvironmentVariable("HOME");
                    if (!string.IsNullOrEmpty(home))
                    {
                        var localAppData = FileSystemPath.Parse(home).Combine("Library/Application Support");
                        var hubCustomLocation = GetCustomHubInstallPath(localAppData);
                        if (!hubCustomLocation.IsEmpty)
                            hubLocations.Add(hubCustomLocation);
                    }

                    // /Applications/Unity/Hub/Editor/2018.1.0b4/Unity.app
                    unityApps.AddRange(hubLocations.SelectMany(l=>l.GetChildDirectories().Select(unityDir =>
                        unityDir.Combine(@"Unity.app"))));

                    return unityApps.Where(a=>a.ExistsDirectory).Distinct().OrderBy(b=>b.FullPath).ToList();
                }
                case PlatformUtil.Platform.Linux:
                {
                    var unityApps = new List<FileSystemPath>();
                    var homeEnv = Environment.GetEnvironmentVariable("HOME");
                    var homes = new List<FileSystemPath> {FileSystemPath.Parse("/opt")};
                    if (!string.IsNullOrEmpty(homeEnv))
                    {
                        homes.Add(FileSystemPath.Parse(homeEnv));
                    }

                    // Old style installations
                    unityApps.AddRange(
                        homes.SelectMany(a => a.GetChildDirectories("Unity*"))
                            .Select(unityDir => unityDir.Combine(@"Editor/Unity")));

                    // Installations with Unity Hub
                    if (!string.IsNullOrEmpty(homeEnv))
                    {
                        var home = FileSystemPath.Parse(homeEnv);
                        var defaultHubLocation = home.Combine("Unity/Hub/Editor");
                        var hubLocations = new List<FileSystemPath> {defaultHubLocation};
                        // Hub custom location
                        var configPath = home.Combine(".config");
                        var customHubInstallPath = GetCustomHubInstallPath(configPath);
                        if (!customHubInstallPath.IsEmpty)
                            hubLocations.Add(customHubInstallPath);

                        unityApps.AddRange(hubLocations.SelectMany(l=>l.GetChildDirectories().Select(unityDir =>
                            unityDir.Combine(@"Editor/Unity"))));
                    }

                    return unityApps.Where(a=>a.ExistsFile).Distinct().OrderBy(b=>b.FullPath).ToList();
                }

                case PlatformUtil.Platform.Windows:
                {
                    var unityApps = new List<FileSystemPath>();

                    var programFiles = GetProgramFiles();
                    unityApps.AddRange(
                        programFiles.GetChildDirectories("Unity*")
                            .Select(unityDir => unityDir.Combine(@"Editor\Unity.exe"))
                        );

                    // default Hub location
                    //"C:\Program Files\Unity\Hub\Editor\2018.1.0b4\Editor\Data\MonoBleedingEdge"
                    unityApps.AddRange(
                        programFiles.Combine(@"Unity\Hub\Editor").GetChildDirectories()
                            .Select(unityDir => unityDir.Combine(@"Editor\Unity.exe"))
                    );
{'fraction_non_alphanumeric': 0.09324365637419749, 'fraction_numerical': 0.00342402934882299, 'mean_word_length': 1.7656408522150828, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2765247', 'n_tokens_mistral': 4058, 'n_tokens_neox': 3745, 'n_words': 702}