// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.wiki;

import fitnesse.components.TraversalListener;

import java.util.Iterator;
import java.util.List;

public class PageCrawlerImpl implements PageCrawler {

  private final WikiPage context;

  protected PageCrawlerImpl(WikiPage context) {
    this.context = context;
  }

  public WikiPage getPage(WikiPagePath path) {
    return getPage(path, null);
  }

  public WikiPage getPage(WikiPagePath path, PageCrawlerDeadEndStrategy deadEndStrategy) {
    return getPage(context, path, deadEndStrategy);
  }

  private  WikiPage getPage(WikiPage page, WikiPagePath path, PageCrawlerDeadEndStrategy deadEndStrategy) {
    if (path == null)
      return null;

    if (isRoot(path))
      return getRoot(page);

    if (path.isEmpty())
      return page;

    if (path.isAbsolute()) {
      WikiPagePath relativeToRoot = new WikiPagePath(path);
      relativeToRoot.setPathMode(WikiPagePath.Mode.RELATIVE);
      return getPage(getRoot(page), relativeToRoot, deadEndStrategy);
    } else if (path.isBackwardSearchPath())
      return getSiblingPage(page, path);

    String firstPathElement = path.getFirst();
    WikiPagePath restOfPath = path.getRest();

    WikiPage childPage = page.getChildPage(firstPathElement);
    if (childPage != null)
      return getPage(childPage, restOfPath, deadEndStrategy);
    else
      return getPageAfterDeadEnd(page, firstPathElement, restOfPath, deadEndStrategy);
  }

  private boolean isRoot(WikiPagePath path) {
    return path.isAbsolute() && path.isEmpty();
  }

  private WikiPage getPageAfterDeadEnd(WikiPage page, String first, WikiPagePath rest, PageCrawlerDeadEndStrategy deadEndStrategy) {
    rest.addNameToFront(first);
    if (deadEndStrategy != null)
      return deadEndStrategy.getPageAfterDeadEnd(page, rest, this);
    else
      return null;
  }

  public boolean pageExists(WikiPagePath path) {
    return getPage(path) != null;
  }

  public WikiPagePath getFullPathOfChild(WikiPagePath childPath) {
    WikiPagePath fullPathOfChild;
    if (childPath.isAbsolute())
      fullPathOfChild = childPath.relativePath();
    else {
      WikiPagePath absolutePathOfParent = new WikiPagePath(context);
      fullPathOfChild = absolutePathOfParent.append(childPath);
    }
    return fullPathOfChild;
  }

  public WikiPagePath getFullPath() {
    return new WikiPagePath(context);
  }

  public String getRelativeName(WikiPage page) {
    StringBuffer name = new StringBuffer();
    for (WikiPage p = page; !p.isRoot() && !p.equals(context); p = p.getParent()) {
      if (p != page)
        name.insert(0, ".");
      name.insert(0, p.getName());
    }
    return name.toString();
  }

  public WikiPage getClosestInheritedPage(final String pageName) {
    final WikiPage[] foundPage = new WikiPage[1];
    traversePageAndAncestors(new TraversalListener<WikiPage>() {
      @Override
      public void process(WikiPage page) {
        WikiPage namedPage = page.getChildPage(pageName);
        if (namedPage != null && foundPage[0] == null)
          foundPage[0] = namedPage;
      }
    });
    return foundPage[0];
  }

  public WikiPage getRoot() {
    return getRoot(context);
  }

  private WikiPage getRoot(WikiPage page) {
    if (page.isRoot())
      return page;
    else
      return getRoot(page.getParent());
  }

  public void traverse(TraversalListener<? super WikiPage> listener) {
    traverse(context, listener);
  }

  private void traverse(WikiPage page, TraversalListener<? super WikiPage> listener) {
    if (page.getClass() == SymbolicPage.class)
      return;
    listener.process(page);
    List<?> children = page.getChildren();
    for (Iterator<?> iterator = children.iterator(); iterator.hasNext();) {
      WikiPage wikiPage = (WikiPage) iterator.next();
      traverse(wikiPage, listener);
    }
  }

  /*
     Todo: RcM.  All calls to getPage should actually come here,
     and be relative to the current page, not the parent page.
     It was a gross error to have the whole wiki know that references
     were relative to the parent instead of the page.
     */
  public WikiPage getSiblingPage(WikiPagePath pathRelativeToSibling) {
    return getSiblingPage(context, pathRelativeToSibling);
  }

  private WikiPage getSiblingPage(WikiPage page, WikiPagePath pathRelativeToSibling) {
    if (pathRelativeToSibling.isSubPagePath()) {
      WikiPagePath relativePath = new WikiPagePath(pathRelativeToSibling);
      relativePath.setPathMode(WikiPagePath.Mode.RELATIVE);
      return getPage(relativePath);
    } else if (pathRelativeToSibling.isBackwardSearchPath()) {
      String target = pathRelativeToSibling.getFirst();
      WikiPage ancestor = findAncestorWithName(target);
      if (ancestor != null) return getPage(ancestor, pathRelativeToSibling.getRest(), null);

      WikiPagePath absolutePath = new WikiPagePath(pathRelativeToSibling);
      absolutePath.makeAbsolute();
      WikiPage root = getRoot(page);
      return getPage(root, absolutePath, null);
    } else {
      WikiPage parent = page.getParent();
      return getPage(parent, pathRelativeToSibling, null);
    }
  }

  public WikiPage findAncestorWithName(String name) {
    for (WikiPage current = context.getParent(); !current.isRoot(); current = current.getParent()) {
      if (current.getName().equals(name)) return current;
    }
    return null;
  }

  public void traverseUncles(final String uncleName, final TraversalListener<? super WikiPage> callback) {
    traversePageAndAncestors(new TraversalListener<WikiPage>() {
      @Override
      public void process(WikiPage page) {
        WikiPage namedPage = page.getChildPage(uncleName);
        if (namedPage != null)
          callback.process(namedPage);
      }
    });
  }

  public void traversePageAndAncestors(TraversalListener<? super WikiPage> callback) {
    WikiPage page = context;
    while (!page.isRoot()) {
      callback.process(page);
      page = page.getParent();
    }
    // Call once more for root page.
    callback.process(page);
  }
}

@@ -3728,176 +3728,54 @@
     
-List%3C?%3E children = page.getChildren();%0A    for (Iterator%3C?%3E iterator = children.iterator(); iterator.hasNext();) %7B%0A      WikiPage wikiPage = (WikiPage) iterator.next();
+for (WikiPage wikiPage : page.getChildren()) %7B
 %0A   

Remove complexity from piece of PageCrawler code.