/// <summary>
 /// Заполняет компонент похожести всех узлов из Result на текстовую строку из заданной точки.
 /// Если по четверке (Имя, внешний контекст, внутренний контекст, текстовая строка)
 /// найден ровно один совпадающий узел - устанавливает флаг Singular, удаляет несовпадающие узлы из результата.
 /// </summary>
 /// <param name="Result"></param>
 /// <param name="Point"></param>
 /// <param name="Source"></param>
 private static void ProcessText(TreeSearchResult Result, PointOfInterest Point, string Source, bool Full = false)
 {
     List<TreeSearchResultNode> ExactMatch = new List<TreeSearchResultNode>();
     TextSearch ts = new TextSearch(Source);
     foreach (TreeSearchResultNode node in Result._result)
     {
         Pair<int, int> Sim = ts.Similarity(node.TreeNode.Location, Point.Text);
         node.TextStringMatch = Sim.Second;
         if (Sim.First == TreeSearchOptions.Equility && node.NameMatch == TreeSearchOptions.Equility
                 && node.OuterContextMatch == TreeSearchOptions.Equility && node.InnerContextMatch == TreeSearchOptions.Equility)
             ExactMatch.Add(node);
     }
     if (ExactMatch.Count == 1 && !Full)
     {
         Result.Singular = true;
         Result._result = ExactMatch;
     }
 }
 /// <summary>
 /// Заполняет поле Location в единственном узле результата
 /// </summary>
 /// <param name="Result"></param>
 /// <param name="Source"></param>
 /// <param name="Text"></param>
 private static void FillLocationForSingularResult(TreeSearchResult Result, string Source, string Text)
 {
     PointOfInterest Point = Result._result[0].TreeNode.ClonePoint();
     Point.ApplyInnerContext();
     Point.Items.Clear();
     Point.Text = Text;
     LexLocation Loc = FindTextPosition(Point, Source);
     Point.Location = Loc;
     Result._result[0].TreeNode = Point;
 }
 /// <summary>
 /// Обрабатывает внешний контект всех узлов из Result, заполняет компонент похожести внешнего контекста.
 /// Если по паре (Имя, Внешний контекст) найден ровно один совпадающий узел - устанавливает флаг Singular, удаляет несовпадающие узлы из результата.
 /// </summary>
 /// <param name="Result"></param>
 /// <param name="Point"></param>
 private static void ProcessOuterContext(TreeSearchResult Result, PointOfInterest Point, bool Full = false)
 {
     List<TreeSearchResultNode> ExactMatch = new List<TreeSearchResultNode>();
     foreach (TreeSearchResultNode node in Result._result)
     {
         int Sim = TreeSearchComparer.OuterContextsSimilarity(node.TreeNode.Context.GetRange(1, node.TreeNode.Context.Count-1), Point.Context.GetRange(1, Point.Context.Count-1));
         node.OuterContextMatch = Sim;
         if (Sim == TreeSearchOptions.Equility && node.NameMatch == TreeSearchOptions.Equility)
             ExactMatch.Add(node);
     }
     if (ExactMatch.Count == 1 && !Full)
     {
         Result.Singular = true;
         Result._result = ExactMatch;
     }
 }
        /// <summary>
        /// Обрабатывает внутренний контект всех узлов из Result, заполняет компонент похожести внутреннего контекста.
        /// Если по паре (Имя, Внутренний контекст) или по тройке (Имя, Внешний контекст, Внутренний контекст)
        /// найден ровно один совпадающий узел - устанавливает флаг Singular, удаляет несовпадающие узлы из результата.
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="Point"></param>
        private static void ProcessInnerContext(TreeSearchResult Result, PointOfInterest Point, bool Full = false)
        {
            //return; //For testing, TODO remove

            List<TreeSearchResultNode> ExactMatchPair = new List<TreeSearchResultNode>();
            List<TreeSearchResultNode> ExactMatchThree = new List<TreeSearchResultNode>();
            List<List<string>> PointContext = ConvertInnerContextToList(Point);

            foreach (TreeSearchResultNode node in Result._result)
            {
                node.TreeNode.ApplyInnerContext();
                List<List<string>> ResContext = ConvertInnerContextToList(node.TreeNode);
                int Sim = TreeSearchComparer.TokenListListsIntersection(ResContext, PointContext);
                node.InnerContextMatch = Sim;
                if (Sim == TreeSearchOptions.Equility && node.NameMatch == TreeSearchOptions.Equility)
                    ExactMatchPair.Add(node);
                if (Sim == TreeSearchOptions.Equility && node.NameMatch == TreeSearchOptions.Equility && node.OuterContextMatch == TreeSearchOptions.Equility)
                    ExactMatchThree.Add(node);
            }
            if (ExactMatchPair.Count == 1 && !Full)
            {
                Result.Singular = true;
                Result._result = ExactMatchPair;
            }
            if (ExactMatchThree.Count == 1 && !Full)
            {
                Result.Singular = true;
                Result._result = ExactMatchThree;
            }
        }
 /// <summary>
 /// Обрабатывает заголовки всех узлов из Result, заполняет компонент похожести заголовка.
 /// Если по имени найден ровно один совпадающий узел - устанавливает флаг Singular, удаляет несовпадающие узлы из результата.
 /// </summary>
 /// <param name="Result"></param>
 /// <param name="Point"></param>
 private static void ProcessHeaders(TreeSearchResult Result, PointOfInterest Point, bool Full = false)
 {
     List<TreeSearchResultNode> exactMatch = new List<TreeSearchResultNode>();
     foreach (TreeSearchResultNode node in Result._result)
         if (node.TreeNode.Context.Count != 0)
         {
             int sim = 0;
             if (string.IsNullOrWhiteSpace(Point.Context[0].Type) || node.TreeNode.Context[0].Type == Point.Context[0].Type)
                 sim = TreeSearchComparer.TokenListsSimilarity(node.TreeNode.Context[0].Name, Point.Context[0].Name);
             node.HeaderMatch = sim;
             if (sim == TreeSearchOptions.Equility)
                 exactMatch.Add(node);
         }
     if (exactMatch.Count == 1 && !Full)
     {
         Result.Singular = true;
         Result._result = exactMatch;
     }
 }
        /// <summary>
        /// Обрабатывает идентификаторы всех узлов из Result, заполняет компонент похожести идентификатора.
        /// Если по имени найден ровно один совпадающий узел - устанавливает флаг Singular, удаляет несовпадающие узлы из результата.
        /// </summary>
        /// <param name="Result"></param>
        /// <param name="Point"></param>
        private static void ProcessIDs(TreeSearchResult Result, PointOfInterest Point, bool Full = false)
        {
            //пустой идентификатор похож на все непустые. Для совместимости со старыми версиями, где идентификатора не было.
            if (string.IsNullOrWhiteSpace(Point.ID))
            {
                foreach (TreeSearchResultNode node in Result._result)
                    node.NameMatch = TreeSearchOptions.Equility;
                return;
            }

            List<TreeSearchResultNode> exactMatch = new List<TreeSearchResultNode>();
            foreach (TreeSearchResultNode node in Result._result)
                if (!string.IsNullOrWhiteSpace(node.TreeNode.ID))
                {
                    node.NameMatch = TreeSearchComparer.StringsSimilarity(node.TreeNode.ID, Point.ID);
                    if (node.NameMatch == TreeSearchOptions.Equility)
                        exactMatch.Add(node);
                }
            if (exactMatch.Count == 1 && !Full)
                if ((exactMatch[0].TreeNode.Context[0]?.Type ?? "") == (Point.Context[0]?.Type ?? ""))
                {
                    Result.Singular = true;
                    Result._result = exactMatch;
                }
        }
        /// <summary>
        /// Возвращает величину похожести имени для самого похожего узла в множестве
        /// </summary>
        /// <param name="Nodes"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        private static float GetMaxSimilarityByName(TreeSearchResult Nodes, PointOfInterest point, string Text)
        {
            if (Nodes.Count == 0)
                return -1;

            int maxSim = 0;
            foreach (TreeSearchResultNode node in Nodes._result)
            {
                int sim = TreeSearchComparer.TokenListsSimilarity(point.Context[0].Name, node.TreeNode.Context[0].Name);
                if (sim > maxSim)
                    maxSim = sim;
            }

            //ProcessSearchResult(Nodes, point, Text);
            return (float)maxSim / TreeSearchOptions.Equility;
        }
        /// <summary>
        /// Создает первоначальный объект результата поиска, заполняя его всеми узлами заданного типа из заданного дерева
        /// </summary>
        /// <param name="TreeRoot"></param>
        /// <param name="Type"></param>
        /// <returns></returns>
        private static TreeSearchResult InitializeResultFromTree(PointOfInterest TreeRoot, string Type)
        {
            TreeSearchResult result = new TreeSearchResult();
            if (string.IsNullOrWhiteSpace(Type) || (TreeRoot.Context != null && TreeRoot.Context.Count != 0 && TreeRoot.Context[0].Type == Type))
                result._result.Add(new TreeSearchResultNode(TreeRoot));

            foreach (PointOfInterest point in TreeRoot.Items)
                result._result.AddRange(InitializeResultFromTree(point, Type)._result);
            return result;
        }
        /// <summary>
        /// Возвращает константы NearG и NearL для заданного узла в дереве
        /// </summary>
        /// <param name="TreeRoot"></param>
        /// <param name="point"></param>
        /// <param name="NearL"></param>
        /// <param name="NearG"></param>
        public static void SetNearLG(PointOfInterest TreeRoot, PointOfInterest point, string Text, out float NearL, out float NearG)
        {
            NearL = 0;
            NearG = 0;
            if (TreeRoot == null || point == null)
                return;
            //В TreeG записываем все потенциально искомые узлы
            TreeSearchResult TreeG = InitializeResultFromTree(TreeRoot, point.Context[0]?.Type);
            TreeSearchResult TreeL = new TreeSearchResult();

            //Переписываем в TreeL соседние узлы, удаляем их из TreeG (Point тоже удаляем)
            HashSet<PointOfInterest> Neighbours = GetNeighbours(TreeRoot, point);
            for (int i = 0; i < TreeG._result.Count;)
                if (TreeG._result[i].TreeNode == point)
                    TreeG._result.RemoveAt(i);
                else if (Neighbours.Contains(TreeG._result[i].TreeNode))
                {
                    TreeL._result.Add(TreeG._result[i]);
                    TreeG._result.RemoveAt(i);
                }
                else
                    ++i;

            //Находим метрику для наиболее похожих имен узлов в двух множествах
            NearL = GetMaxSimilarityByName(TreeL, point, Text);
            NearG = GetMaxSimilarityByName(TreeG, point, Text);
        }