using System.Collections;
using System.Collections.Generic;


namespace Rokojori
{
  public enum TreeIteratorType
  {
    Parent,

    NextSibling,
    PreviousSibling,

    FirstChild,
    LastChild,
    LastGrandChild,

    NextNode,
    PreviousNode,

    Parents,

    DirectChildren,
    Children,
    
    Siblings,
    PreviousSiblings,
    NextSiblings,

    PreviousNodes,
    NextNodes    
  }

  public abstract class TreeIterator<N> where N:class
  {
    public abstract bool HasNext();
    public abstract N Current();
    protected abstract void _MoveToNext();
    bool safeMoving = true;

    public void MoveToNext()
    {
      if ( safeMoving && ! HasNext() )
      {
        throw new System.Exception( "Has no more elements" );
      }

      _MoveToNext();
    }
    
    public void ForEach( System.Action<N> callback )
    {
      while ( HasNext() )
      {
        _MoveToNext();
        callback( Current() );        
      }
    }

    public N Get( System.Func<N,bool> predicate )
    {
      while ( HasNext() )
      {
        _MoveToNext();

        var current = Current();
        var result = predicate( current );

        if ( result )
        {
          return current;
        }
      }

      return null;
    }

    public bool Has( System.Func<N,bool> predicate )
    {
      return Get( predicate ) != null;
    }

    public List<N> All( System.Func<N,bool> predicate )
    {
      var list = new List<N>();

      while ( HasNext() )
      {
        _MoveToNext();

        var current = Current();
        var result = predicate( current );

        if ( result )
        {
          list.Add( current );
        }
      }

      return list;
    }

    public static TreeIterator<N> GetIterator( TreeIteratorType type, N node, TreeWalker<N> walker )
    {

      switch ( type )
      {
        case TreeIteratorType.Parent:
          return SingleIterator<N>.Create( walker.Parent( node ) );

        case TreeIteratorType.NextSibling:
          return SingleIterator<N>.Create( walker.NextSibling( node ) );

        case TreeIteratorType.PreviousSibling:
          return SingleIterator<N>.Create( walker.PreviousSibling( node ) );

        case TreeIteratorType.FirstChild:
          return SingleIterator<N>.Create( walker.ChildAt( node, 0 ) );

        case TreeIteratorType.LastChild:
          return SingleIterator<N>.Create( walker.ChildAt( node, walker.NumChildren( node ) - 1 ) );

        case TreeIteratorType.LastGrandChild:
          return SingleIterator<N>.Create( walker.LastGrandChild( node ) );

        case TreeIteratorType.NextNode:
          return SingleIterator<N>.Create( walker.NextNode( node ) );
        
        case TreeIteratorType.PreviousNode:
          return SingleIterator<N>.Create( walker.PreviousNode( node ) );


        case TreeIteratorType.Parents:
          return ParentsIterator<N>.Create( walker, node );

        
        case TreeIteratorType.DirectChildren:
          return DirectChildrenIterator<N>.Create( walker, node );

        case TreeIteratorType.Children:
          return ChildrenIterator<N>.Create( walker, node );


        case TreeIteratorType.Siblings:
          return SiblingsIterator<N>.Create( walker, node, true, true );

        case TreeIteratorType.PreviousSiblings:
          return SiblingsIterator<N>.Create( walker, node, true, false );

        case TreeIteratorType.NextSiblings:
          return SiblingsIterator<N>.Create( walker, node, false, true );

      
        case TreeIteratorType.NextNodes:
          return NodesIterator<N>.Create( walker, node, true );

        case TreeIteratorType.PreviousNodes:
          return NodesIterator<N>.Create( walker, node, false );

      }

      return null;
    }
  }
}