context: using System.Collections.Generic;
  using Antlr4.Runtime;
  using Antlr4.Runtime.Tree;
  using Rubberduck.Parsing.Grammar;
  using Rubberduck.VBEditor;
  using System.Linq;
  using Antlr4.Runtime.Misc;
  using System;
  
  namespace Rubberduck.Parsing
  {
      public static class ParserRuleContextExtensions
      {
          /// <summary>
          ///  Returns a Selection structure containing the context
          /// </summary>
          public static Selection GetSelection(this ParserRuleContext context)
          {
              // if we have an empty module, `Stop` is null
              if (context?.Stop == null) { return Selection.Home; }
  
              // ANTLR indexes for columns are 0-based, but VBE's are 1-based.
              // ANTLR lines and VBE's lines are both 1-based
              // 1 is the default value that will select all lines. Replace zeroes with ones.
              // See also: https://msdn.microsoft.com/en-us/library/aa443952(v=vs.60).aspx
  
              return new Selection(context.Start.Line == 0 ? 1 : context.Start.Line,
                                   context.Start.Column + 1,
                                   context.Stop.Line == 0 ? 1 : context.Stop.EndLine(),
                                   context.Stop.EndColumn() + 1);
          }
  
          /// <summary>
          ///  Gets the tokens belonging to the context from the token stream.
          /// </summary>
          public static IEnumerable<IToken> GetTokens(this ParserRuleContext context, CommonTokenStream tokenStream)
          {
              var sourceInterval = context.SourceInterval;
              if (sourceInterval.Equals(Interval.Invalid) || sourceInterval.b < sourceInterval.a)
              {
                  return new List<IToken>();
              }
              return tokenStream.GetTokens(sourceInterval.a, sourceInterval.b);
          }
  
          /// <summary>
          ///  Gets the original source, without "synthetic" text such as an 'EOF' token.
          /// </summary>
          public static string GetText(this ParserRuleContext context, ICharStream stream)
          {
              // Can be null if the input is empty it seems.
              if (context.Stop == null)
              {
                  return string.Empty;
              }
              return stream.GetText(new Interval(context.Start.StartIndex, context.Stop.StopIndex));
          }
  
          /// <summary>
          /// Returns the first direct child of 'context' that is of the generic Type.
          /// </summary>
          public static TContext GetChild<TContext>(this ParserRuleContext context) where TContext : ParserRuleContext
          {
              if (context == null)
              {
                  return default;
              }
  
              var results = context.children
                  .Where(child => child is TContext)
                  .ToList();
              return results.Any() 
                  ? (TContext)results.First() 
                  : default;
          }
  
          /// <summary>
          /// Determines if any of the context's ancestors are the generic Type.
          /// </summary>
          public static bool IsDescendentOf<TContext>(this ParserRuleContext context)
          {
              if (context == null)
              {
                  return false;
              }
  
              if (context is TContext)
              {
                  return GetAncestor_Recursive<TContext>((ParserRuleContext)context.Parent) != null;
              }
              return GetAncestor_Recursive<TContext>(context) != null;
          }
  
          /// <summary>
          /// Determines if any of the context's ancestors are equal to the parameter 'ancestor'.
          /// </summary>
          public static bool IsDescendentOf<T>(this ParserRuleContext context, T ancestor) where T : ParserRuleContext
          {
              if (context == null || ancestor == null)
              {
                  return false;
              }
              if (context == ancestor)
              {
                  return IsDescendentOf_Recursive(context.Parent, ancestor);
              }
              return IsDescendentOf_Recursive(context, ancestor);
          }
  
          private static bool IsDescendentOf_Recursive(IParseTree context, IParseTree targetParent)
          {
              if (context == null)
              {
                  return false;
              }
              return context == targetParent 
                     || IsDescendentOf_Recursive(context.Parent, targetParent);
          }
  
          /// <summary>
          /// Returns the context's first ancestor of the generic Type.
          /// </summary>
          public static TContext GetAncestor<TContext>(this ParserRuleContext context)
          {
              switch (context)
              {
                  case null:
                      return default;
                  case TContext _:
                      return GetAncestor_Recursive<TContext>((ParserRuleContext)context.Parent);
                  default:
                      return GetAncestor_Recursive<TContext>(context);
              }
          }
  
          /// <summary>
          /// Tries to return the context's first ancestor of the generic Type.
          /// </summary>
          public static bool TryGetAncestor<TContext>(this ParserRuleContext context, out TContext ancestor)
          {
              ancestor = context.GetAncestor<TContext>();
              return ancestor != null;
          }
  
          private static TContext GetAncestor_Recursive<TContext>(ParserRuleContext context)
          {
              switch (context)
              {
                  case null:
                      return default;
                  case TContext tContext:
                      return tContext;
                  default:
                      return GetAncestor_Recursive<TContext>((ParserRuleContext)context.Parent);
              }
          }
  
          /// <summary>
          /// Returns the context's first ancestor containing the token with the specified token index or the context itels if it already contains the token.
          /// </summary>
          public static ParserRuleContext GetAncestorContainingTokenIndex(this ParserRuleContext context, int tokenIndex)
          {
              if (context == null)
              {
                  return default;
              }
  
              if (context.ContainsTokenIndex(tokenIndex))
              {
                  return context;
              }
  
              if (!(context.Parent is ParserRuleContext parent))
              {
                  return default;
              }
  
              return GetAncestorContainingTokenIndex(parent, tokenIndex);
          }
  
          /// <summary>
          /// Determines whether the context contains the token with the specified token index.
          /// </summary>
          public static bool ContainsTokenIndex(this ParserRuleContext context, int tokenIndex)
          {
              if (context?.Stop == null)
              {
                  return false;
              }
  
              return context.Start.TokenIndex <= tokenIndex && tokenIndex <= context.Stop.TokenIndex;
          }
  
          /// <summary>
          /// Returns the context's first descendent of the generic Type.
          /// </summary>
          public static TContext GetDescendent<TContext>(this ParserRuleContext context) where TContext : ParserRuleContext
          {
              if (context?.children == null)
              {
                  return null;
              }
  
              foreach (var child in context.children)
              {
                  if (child == null)
                  {
                      continue;
                  }
  
                  if (child is TContext match)
                  {
                      return match;
                  }
                  
                  var childResult = (child as ParserRuleContext)?.GetDescendent<TContext>();
                  if (childResult != null)
                  {
                      return childResult;
                  }
              }
  
              return null;
          }
  
          /// <summary>
          /// Returns all the context's descendents of the generic Type.
          /// </summary>
          public static IEnumerable<TContext> GetDescendents<TContext>(this ParserRuleContext context) where TContext : ParserRuleContext
          {
              var walker = new ParseTreeWalker();
              var listener = new ChildNodeListener<TContext>();
              walker.Walk(listener, context);
              return listener.Matches;
          }
  
          /// <summary>
          /// Try to get the first child of the generic context type.
          /// </summary>
          public static bool TryGetChildContext<TContext>(this ParserRuleContext ctxt, out TContext opCtxt) where TContext : ParserRuleContext
          {
              opCtxt = ctxt.GetChild<TContext>();
              return opCtxt != null;
          }
  
          /// <summary>
          /// Returns the endOfStatementContext's first endOfLine context.
          /// </summary>
          public static VBAParser.EndOfLineContext GetFirstEndOfLine(this VBAParser.EndOfStatementContext endOfStatement)
          {
              //This dedicated method exists for performance reasons on hot-paths.
              var individualEndOfStatements = endOfStatement.individualNonEOFEndOfStatement();
  
  
gt:             if (individualEndOfStatements == null)