using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;


namespace Rokojori
{
  public class LexerEvent
  {
    string _type;
    int _offset;
    int _length;
    string _match;

    public string match => _match;

    public LexerEvent( string type, int offset, int length )
    {
      Set( type, offset, length );
    }

    public string type { get { return _type; } }
    public int offset { get { return _offset; } }
    public int length { get { return _length; } }
    
    string _mode = null;

    public string mode 
    {
      get 
      {
        if ( _mode != null )
        {
          return _mode;
        }

        var modeDelimiterPosition = type.IndexOf( LexerMatcher.FullTypeDelimiter );

        if ( modeDelimiterPosition == -1 )
        {
          _mode = "";

          return _mode;
        }

        _mode = type.Substring( 0, modeDelimiterPosition );

        return _mode;
      }       
    }


    public bool isError
    {
      get { return this.length == -1; }
    }

    public bool isDone
    {
      get { return this.length == -2; }
    }

    public bool isErrorOrDone => isError || isDone;


    public void Set( string type, int offset, int length )
    {
      this._type = type;
      this._offset = offset;
      this._length = length;      
    }

    public LexerEvent Copy()
    {
      return new LexerEvent( _type, _offset, _length );
    }

    public int end
    {
      get { return this._offset + _length; }
    }
    public override string ToString()
    {
      if ( match != null )
      {
        return "Token{ '" + type + "':'" + match + "' (" + offset + "-" + end + ") }"; 
      }

      return "Token{ '" + type + "' (" + offset + "-" + end + ") }"; 
    }

    public void GrabMatch( string source )
    {
      if ( _length < 0 )
      {
        return;
      }

      _match = source.Substring( offset, length );
    }

    public bool Is( string type, string match = null )
    {
      var typeCorrect = type == null || type == this.type;

      if ( ! typeCorrect )
      {
        return false;
      }

      return match == null || match == this.match;
    }

    public bool Is( LexerMatcher matcher, string match = null )
    {
      return Is( matcher == null ? null : matcher.type, match );
    }

    public bool MatchIs( string match )
    {
      return match == this.match;
    }

    public string GetLineInfo( string source )
    {
      return TextLinesMapper.GetLineInfo( source, this );
    }


    public bool MatchIsAny( params string[] matches )
    {
      for ( int i = 0; i < matches.Length; i++ )
      {
        if ( MatchIs( matches[ i ] ) )
        {
          return true;
        }
      }

      return false;
    }

    public bool IsAnyOf( params LexerMatcher[] matchers )
    {
      for ( int i = 0; i < matchers.Length; i++ )
      {
        if ( Is( matchers[ i ] ) )
        {
          return true;
        }
      }

      return false;
    }

    public bool IsAny( LexerMatcher matcher, params string[] matches )
    {
      if ( matches == null || matches.Length == 0 )
      {
        return Is( matcher );
      }

      var correctType = matcher == null || matcher.type == type;

      if ( ! correctType )
      {
        return false;
      }
      
      return MatchIsAny( matches );
    }

    public enum FindResultType
    {
      Found,
      KeepSearching,
      NotFound,
      Error
    }

    public class FindResult
    {
      public FindResultType type = FindResultType.NotFound;

      public bool found => FindResultType.Found == type;

      public int index;


      public FindResult()
      { }

      public FindResult( FindResultType type, int index )
      {
        this.type = type;
        this.index = index;
      }
    }

    public static FindResult Found( int index)
    {
      return new FindResult( FindResultType.Found, index );
    }

    public static FindResult KeepSearch( int index )
    {
      return new FindResult( FindResultType.KeepSearching, index );
    }

    public static FindResult NotFound( int index )
    {
      return new FindResult( FindResultType.NotFound, index );
    }

    public static FindResult Error( int index )
    {
      return new FindResult( FindResultType.NotFound, index );
    }


    public static string GetMatchFromRange( List<LexerEvent> tokens, int offset, int length = -1 )
    {
      if ( length == -1 )
      {
        length = tokens.Count - offset;
      }

      var match = new StringBuilder();

      for ( int i = 0; i < length; i++ )
      {
        match.Append( tokens[ offset + i ].match );
      }

      return match.ToString();
    }

    public static int FindType( List<LexerEvent> tokens, int offset, params string[] types )
    {
      for ( int i = offset; i < tokens.Count; i++ )
      {
        if ( Arrays.IndexOf( types, tokens[ i ].type ) != -1 )
        {
          return i;
        }
      }

      return -1;

    }

    public static List<List<LexerEvent>> FindSequences( List<LexerEvent> tokens, System.Func<int,bool,Trillean> matcher )
    {
      var sequences = new List<List<LexerEvent>>();

      List<LexerEvent> currentSequence = null;

      for ( int i = 0; i < tokens.Count; i++ )
      {
        var token = tokens[ i ];
        var result = matcher( i, currentSequence != null );

        if ( currentSequence != null )
        {
          if ( Trillean.True == result )
          {
            currentSequence.Add( token );
          }
          else if ( Trillean.False == result )
          {            
            currentSequence.Add( token );

            sequences.Add( currentSequence );
            currentSequence = null;
          }
        }
        else
        {
          if ( Trillean.True == result )
          {
            currentSequence = new List<LexerEvent>();
            currentSequence.Add( token );
          }

          if ( Trillean.False == result )
          {
            sequences.Add( new List<LexerEvent>(){ tokens[ i ] } );
            sequences.Add( null ); 
            return sequences;
          }
          
        }

      }

      return sequences;
    }

    public static FindResult ReverseFind( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator )
    {
      return Find( tokens, offset, evaluator, false );
    }

    public static FindResult Find( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,FindResultType> evaluator, bool forward = true )
    {
      var result = new FindResult();

      var increment = forward ? 1 : -1;
      var end = forward ? tokens.Count : -1;

      for ( int i = offset; i != end && i >= 0 && i < tokens.Count; i += increment )
      {
        var tokenResult = evaluator( tokens[ i ] );

        if ( tokenResult == FindResultType.Error ||
             tokenResult == FindResultType.Found 
        )
        {
          result.type = tokenResult;
          result.index = i;

          return result;
        }

      }

      return result;
    }

    public static List<int> GetSeparatorsInBrackets( List<LexerEvent> tokens, int offset, string seperator = "," )
    { 
      var openTypes = new List<string>(){ "(","[","{" };
      var closingTypes = new List<string>(){ ")","]","}" };

      var stack = new List<string>();
      var separators = new List<int>();

      for ( int i = offset; i < tokens.Count; i++ )
      {
        if ( stack.Count == 1 && tokens[ i ].MatchIs(  seperator ) )
        {
          separators.Add( i );
        }

        var match = tokens[ i ].match;

        if ( openTypes.Contains( match ) )
        {
          stack.Add( match );
          continue;
        }
        else if ( closingTypes.Contains( match ) )
        {
          var closingTypeIndex = closingTypes.IndexOf( match );
          var expectedOpener = openTypes[ closingTypeIndex ];

          if ( stack[ stack.Count - 1 ] != expectedOpener )
          {
            return null;
          }

          stack.RemoveAt( stack.Count - 1 );
        }
      }

      return separators;
    
    }


   
    static List<string> openTypes = new List<string>(){ "(","[","{" };
    static List<string> closingTypes = new List<string>(){ ")","]","}" };
      
    public static FindResult FindClosingBracket( List<LexerEvent> tokens, int offset )
    {       
      var token = tokens[ offset ];

      var bracketIndex = openTypes.IndexOf( token.match );

      if ( bracketIndex == -1 )
      {
        var result = new FindResult( FindResultType.NotFound, offset );
        return result;
      }

      var opener = openTypes[ bracketIndex ];
      var closer = closingTypes[ bracketIndex ];

      var counter = ( LexerEvent le ) =>
      {
        if ( le.Is( LexerMatcherLibrary.BracketMatcher, closer ) )
        {
          return -1;
        }

        if ( le.Is( LexerMatcherLibrary.BracketMatcher, opener ) )
        {
          return 1;
        }

        return 0;
      };

      return FindCloser( tokens, offset, counter );
    }

    public static FindResult FindCloser( List<LexerEvent> tokens, int offset, System.Func<LexerEvent,int> counter, bool forward = true )
    {
      var result = new FindResult();
      var currentValue = 0;

      if ( forward )
      {
        for ( int i = offset; i < tokens.Count; i++ )
        {
          var countResult = counter( tokens[ i ] );

          currentValue += countResult;

          if ( currentValue == 0 )
          {
            result.type = FindResultType.Found;
            result.index = i;
            return result;
          }        
        }
      }
      else
      {
        for ( int i = offset; i >= 0; i-- )
        {
          var countResult = counter( tokens[ i ] );

          currentValue += countResult;

          if ( currentValue == 0 )
          {
            result.type = FindResultType.Found;
            result.index = i;
            return result;
          }        
        }
      }

      return result;
    }

    public static FindResult FindOpeningBracket( List<LexerEvent> tokens, int offset, string blockBracket = "{" )
    { 
      var result = Find( tokens, offset,  
        ( le ) => 
        {
          return le.Is( LexerMatcherLibrary.BracketMatcher, blockBracket ) ? FindResultType.Found : FindResultType.KeepSearching;
        } 
      );

      return result;
    }

    public static FindResult ReverseFindOpeningBracket( List<LexerEvent> tokens, int offset )
    {    

      var token = tokens[ offset ];

      var bracketIndex = closingTypes.IndexOf( token.match );

      if ( bracketIndex == -1 )
      {
        var result = new FindResult( FindResultType.NotFound, offset );
        return result;
      }

      var opener = openTypes[ bracketIndex ];
      var closer = closingTypes[ bracketIndex ];

      var counter = ( LexerEvent le ) =>
      {
        if ( le.Is( LexerMatcherLibrary.BracketMatcher, closer ) )
        {
          return 1;
        }

        if ( le.Is( LexerMatcherLibrary.BracketMatcher, opener ) )
        {
          return -1;
        }

        return 0;
      };

      return FindCloser( tokens, offset, counter, false );
    }

    

    public static List<RangeI> GetBlocks( List<LexerEvent> tokens )
    {
      var index = 0;
      List<RangeI> blocks = new List<RangeI>();

      while ( index < tokens.Count )
      {
        var openResult = FindOpeningBracket( tokens, index );

        if ( ! openResult.found || openResult.index < index )
        {
          // RJLog.Log( "No OpeningBracket after", index );

          if ( index == 0 )
          {
            // RJLog.Log( tokens.Map( t => t.type + ": '" + t.match + "'" ).Join( "\n" ) );
          }
          return blocks;
        }


        var closeResult = FindClosingBracket( tokens, openResult.index );

        if ( ! closeResult.found || closeResult.index <= openResult.index )
        {
          // RJLog.Log( "No ClosingBracket after", index );
          return null;
        }

        var range = new RangeI( openResult.index, closeResult.index );
        blocks.Add( range );

        index = closeResult.index + 1;

      }

      return blocks;
    }



  }
}