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

namespace Rokojori
{
  public class LexerList 
  {
    protected List<LexerEvent> _events;
    protected LexerMatcher[] _ignore = null;

    public LexerMatcher[] ignore 
    {
      get { return _ignore; }
      set { _ignore = value; }
    }

    public List<LexerEvent> events => _events;

    public LexerList Filter( System.Predicate<LexerEvent> le )
    {
      var list = Create( events.Filter( e => le( e ) ), ignore );
      return list;

    }

    public LexerEvent this[ int index ]
    {
      get 
      { 
        return _events[ index ]; 
      }

      set
      {
        _events[ index ] = value;
      }
    }


    public LexerList GetAll( LexerMatcher lexerMatcher )
    {
      return Create( events.Filter( e => e.Is( lexerMatcher ) ), ignore );
    }

    public List<RangeI> Seperate( int start, int end, LexerMatcher[] ignore = null )
    {
      return Seperate( ",", start, end, ignore ); 
    }

    public List<RangeI> Seperate( string seperator, int start, int end, LexerMatcher[] ignoring = null )
    {
      ignoring = GetIgnore( ignoring );

      var list = new List<RangeI>();
      
      var currentStart = -1;

      for ( int i = start; i <= end; i++ )
      {
        if ( _events[ i ].MatchIs( seperator ) )
        {
          if ( currentStart != -1 )
          {
            list.Add( new RangeI( currentStart, i - 1 ) );
          }

          currentStart = -1;
        } 
        else if ( ignoring != null && _events[ i ].IsAnyOf( ignoring ) )
        {
          continue;
        }
        else if ( currentStart == -1 )
        {
          currentStart = i;
        }

      }

      if ( currentStart != -1 )
      {
        list.Add( new RangeI( currentStart, end ) );
      }

      return list;
    }

    public void ForEach( LexerMatcher lexerMatcher, System.Action<LexerEvent> action )
    {
      events.ForEach(
        ( e )=>
        {
          if ( ! e.Is( lexerMatcher ) )
          {
            return;
          }

          action( e );
        }
      );
    }

    public LexerEvent.FindResult FindOpeningBracket( int offset, string blockBracket = "{" )
    {
      return LexerEvent.FindOpeningBracket( _events, offset, blockBracket );
    }

    public LexerEvent.FindResult FindClosingBracket( int offset )
    {
      return LexerEvent.FindClosingBracket( _events, offset );
    }

    public LexerEvent.FindResult ReverseFindOpeningBracket( int offset )
    {
      return LexerEvent.ReverseFindOpeningBracket( _events, offset );
    }

    public List<RangeI> GetParameters( int offset )
    {
      var opener = FindOpeningBracket( offset, "(" ).index;

      if ( opener == -1 )
      {
        RJLog.Log( "No open bracket:", events[ offset ].match );
        return null;
      }

      var closer = FindClosingBracket( opener ).index;

      if ( closer == -1 )
      {
        RJLog.Log( "No closing bracket:", events[ offset ].match );
        return null;
      }

      // RJLog.Log( "GetParameters", opener, closer );
      
      return LexerEvent.Seperate( _events, opener + 1, closer - 1 );       
    }

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

    public LexerEvent.FindResult Find( int offset, System.Func<LexerEvent,LexerEvent.FindResultType> evaluator )
    {
      return LexerEvent.Find( _events, offset, evaluator, true );
    }

    public bool Is( int offset, string type, string match, LexerMatcher[] ignoring )
    {
      var next = Get( offset, ignoring );

      return next.Is( type, match );
    }

    public bool MatchIs( int offset, string match, LexerMatcher[] ignoring = null )
    {
      var next = Get( offset, ignoring );

      return next.MatchIs( match );
    }

    LexerMatcher[] GetIgnore( LexerMatcher[] ignoring )
    { 
      ignoring = ignoring != null ? ignoring : _ignore;
      ignoring = ignoring != null ? ignoring : [];

      return ignoring;
    }

    public LexerEvent Get( int offset, LexerMatcher[] ignoring = null )
    {
      return events[ GetIndex( offset, ignoring ) ];
    }

    public LexerEvent GetReverse( int offset, LexerMatcher[] ignoring = null )
    {
      return events[ GetIndexReverse( offset, ignoring ) ];
    }

    public int GetIndex( int offset, LexerMatcher[] ignoring = null )
    {      
      ignoring = GetIgnore( ignoring );

      for ( int i = offset; i < _events.Count; i++ )
      {
        if ( _events[ i ].IsAnyOf( ignoring ) )
        {
          continue;
        }

        return i;
      }

      return -1;
    }

    public int GetIndexReverse( int offset, LexerMatcher[] ignoring = null )
    {
      ignoring = GetIgnore( ignoring );

      for ( int i = offset; i >= 0; i-- )
      {
        if ( _events[ i ].IsAnyOf( ignoring ) )
        {
          continue;
        }

        return i;
      }

      return -1;
    }

    public int FindIndexOfType( int offset, string type )
    {
      for ( int i = offset; i < _events.Count; i++ )
      {
        if ( _events[ i ].type == type )
        {
          return i;
        }
      }

      return -1;
    }

    public int FindIndexOfMatch( int offset, string match )
    {
      for ( int i = offset; i < _events.Count; i++ )
      {
        if ( _events[ i ].match == match )
        {
          return i;
        }
      }

      return -1;
    }
    
    public LexerEvent.FindResult Find( int offset, List<LexerEventSelector> found,     
    List<LexerEventSelector> keepSearching = null, List<LexerEventSelector> errors = null )
    {
      return Find(
        offset,
        ( le )=>
        {
          var errorItem = errors == null ? null : errors.Find( s => s.Selects( le ) );

          if ( errorItem != null )
          {
            return LexerEvent.FindResultType.Error;
          } 

          var foundItem = found.Find( s => s.Selects( le ) );

          if ( foundItem != null )
          {
            return LexerEvent.FindResultType.Found;
          } 


          if ( keepSearching == null )
          {
            return LexerEvent.FindResultType.KeepSearching;
          }

          var keepSearchingItem = keepSearching.Find( s => s.Selects( le ) );

          return keepSearchingItem == null ? LexerEvent.FindResultType.Error : LexerEvent.FindResultType.KeepSearching; 

        }
      );
    }

    public List<List<LexerEvent>> FindSequences( System.Func<int,bool,Trillean> matcher )
    {
      return LexerEvent.FindSequences( _events, matcher );
    }

    public List<RangeI> GetBlocks()
    {
      return LexerEvent.GetBlocks( _events );
    } 

    public void ReplaceBlocks( System.Func<RangeI,LexerEvent> blockReplacer )
    {
      ReplaceRanges( GetBlocks(), blockReplacer );
    }

    public void ReplaceRanges( List<RangeI> ranges, System.Func<RangeI,LexerEvent> blockReplacer )
    {
      for ( int i = 0; i < ranges.Count; i++ )
      {
        var r = ranges[ ranges.Count - 1 - i ];
        var le = blockReplacer( r );
        _events.ReplaceRange( r.min, r.max - r.min + 1, [ le ] );
      }
    }

    public int MoveIndexForward( int start, string[] matches, LexerMatcher[] ignoring = null )
    {
      return MoveIndex( start, -1, matches, ignoring );
    }

    public int MoveIndexBackwards( int start, string[] matches, LexerMatcher[] ignoring = null  )
    {
      return MoveIndex( start, -1, matches, ignoring );
    }


    public int MoveIndex( int start, int direction, string[] matches, LexerMatcher[] ignoring = null )
    {
      ignoring = GetIgnore( ignoring );

      var tokenIndex = start;
      var token = _events[ tokenIndex ];
      var moving = true;
      var moveIndex = start;

      while ( moving && token != null && token.IsAnyOf( ignoring ) )
      {
        moveIndex = tokenIndex;

        tokenIndex += direction;
        token = tokenIndex < 0 || tokenIndex >= _events.Count ? null : events[ tokenIndex ];

        if ( token != null && token.IsAnyOf( ignoring ) )
        {
          continue;
        }

        if ( token.MatchIsAny( matches ) )
        {
          moveIndex = tokenIndex;

          tokenIndex += direction;
          token = tokenIndex < 0 || tokenIndex >= _events.Count ? null : events[ tokenIndex ];
        }
      }

      return moveIndex;
    }
    

    public int Index( LexerEvent lexerEvent, int offset = 0 )
    {
      return _events.IndexOf( lexerEvent, offset );
    }

    public RangeI NextBlock( int offset )
    {
      return LexerEvent.GetNextBlock( _events, offset );
    }

    public LexerList Range( RangeI range )
    {
      return Range( range.min, range.max );
    }

    public string match
    {
      get { return _events.Map( e => e.match ).Join( "" ); }
    }

    public LexerList RangeInside( RangeI range )
    {
      return Range( range.min + 1, range.max - 1);
    }

    public int size => _events.Count;

    public LexerList Range( int start, int end = -1 )
    {

      if ( end == -1 )
      {
        end = _events.Count - 1;
      }

      if ( start < 0 || start >= _events.Count || end < 0 || end >= _events.Count )
      {
        RJLog.Log( "Range invalid", start, end, "count:", _events.Count );
        return null;
      }

      var list = Create( _events.Sub( start, end - start + 1 ), ignore );
      return list;
    }

    public static LexerList Create( List<LexerEvent> lexerEvents, LexerMatcher[] ignore = null )
    {
      var list = new LexerList();
      list._events = lexerEvents;
      list.ignore = ignore;

      return list;
    }
    
  } 
}