package jp.aonir.fuzzyxml.xpath;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jp.aonir.fuzzyxml.FuzzyXMLDocument;
import jp.aonir.fuzzyxml.FuzzyXMLElement;
import jp.aonir.fuzzyxml.FuzzyXMLNode;

import org.apache.commons.jxpath.ri.compiler.NodeTest;
import org.apache.commons.jxpath.ri.model.NodeIterator;
import org.apache.commons.jxpath.ri.model.NodePointer;

public class FuzzyXMLNodeIterator implements NodeIterator {

  private NodePointer _parent;
  private NodeTest _nodeTest;

  private boolean _reverse;
  private int _position = 0;
  private int _index = 0;
  private List<FuzzyXMLNode> _children;
  private Object _child;

  /**
   * 
   */
  public FuzzyXMLNodeIterator(NodePointer parent, NodeTest nodeTest, boolean reverse, NodePointer startWith) {
    this._parent = parent;
    if (startWith != null) {
      this._child = startWith.getNode();
    }
    // TBD: optimize me for different node tests
    Object node = parent.getNode();
    if (node instanceof FuzzyXMLDocument) {
      this._children = Arrays.asList(new FuzzyXMLNode[] { ((FuzzyXMLDocument) node).getDocumentElement() });
    }
    else if (node instanceof FuzzyXMLElement) {
      this._children = Arrays.asList(((FuzzyXMLElement) node).getChildren());
    }
    else {
      this._children = new ArrayList<FuzzyXMLNode>();
    }
    this._nodeTest = nodeTest;
    this._reverse = reverse;
  }

  public int getPosition() {
    return _position;
  }

  public boolean setPosition(int position) {
    while (this._position < position) {
      if (!next()) {
        return false;
      }
    }
    while (this._position > position) {
      if (!previous()) {
        return false;
      }
    }
    return true;
  }

  public NodePointer getNodePointer() {
    if (_child == null) {
      if (!setPosition(1)) {
        return null;
      }
      _position = 0;
    }

    return new FuzzyXMLNodePointer(_parent, _child);
  }

  /**
   * This is actually never invoked during the normal evaluation
   * of xpaths - an iterator is always going forward, never backwards.
   * So, this is implemented only for completeness and perhaps for
   * those who use these iterators outside of XPath evaluation.
   */
  private boolean previous() {
    _position--;
    if (!_reverse) {
      while (--_index >= 0) {
        _child = _children.get(_index);
        if (testChild()) {
          return true;
        }
      }
    }
    else {
      for (; _index < _children.size(); _index++) {
        _child = _children.get(_index);
        if (testChild()) {
          return true;
        }
      }
    }
    return false;
  }

  private boolean next() {
    _position++;
    if (!_reverse) {
      if (_position == 1) {
        _index = 0;
        if (_child != null) {
          _index = _children.indexOf(_child) + 1;
        }
      }
      else {
        _index++;
      }
      for (; _index < _children.size(); _index++) {
        _child = _children.get(_index);
        if (testChild()) {
          return true;
        }
      }
      return false;
    }
    if (_position == 1) {
      _index = _children.size() - 1;
      if (_child != null) {
        _index = _children.indexOf(_child) - 1;
      }
    }
    else {
      _index--;
    }
    for (; _index >= 0; _index--) {
      _child = _children.get(_index);
      if (testChild()) {
        return true;
      }
    }
    return false;
  }

  private boolean testChild() {
    return FuzzyXMLNodePointer.testNode(_parent, _child, _nodeTest);
  }

}
