/*
Copyright 2013 BarD Software s.r.o
Copyright 2012 GanttProject Team

This file is part of GanttProject, an opensource project management tool.

GanttProject is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

GanttProject is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GanttProject.  If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.ganttproject;

import java.util.List;
import java.util.Queue;

import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import net.sourceforge.ganttproject.util.collect.Pair;

import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import org.jdesktop.swingx.treetable.TreeTableNode;

import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;

/**
 * Utility methods for working with Swing trees.
 *
 * @author dbarashev (Dmitry Barashev)
 */
public class TreeUtil {
  static int getPrevSibling(TreeNode node, TreeNode child) {
    if (node == null) {
      return -1;
    }
    int childIndex = node.getIndex(child);
    return childIndex - 1;
  }

  static TreeTableNode getPrevSibling(TreeTableNode node) {
    TreeTableNode parent = node.getParent();
    int idxPrev = getPrevSibling(parent, node);
    return idxPrev == -1 ? null : parent.getChildAt(idxPrev);
  }

  static int getNextSibling(TreeNode node, TreeNode child) {
    if (node == null) {
      return -1;
    }
    int childIndex = node.getIndex(child);
    return childIndex == node.getChildCount() - 1 ? -1 : childIndex + 1;
  }

  static TreeTableNode getNextSibling(TreeTableNode node) {
    TreeTableNode parent = node.getParent();
    int idxNext = getNextSibling(parent, node);
    return idxNext == -1 ? null : parent.getChildAt(idxNext);
  }

  public static TreePath createPath(TreeNode node) {
    List<TreeNode> ascendingPath = Lists.newArrayList();
    while (node != null) {
      ascendingPath.add(node);
      node = node.getParent();
    }
    TreeNode[] descendingPath = Lists.reverse(ascendingPath).toArray(new TreeNode[ascendingPath.size()]);
    return new TreePath(descendingPath);
  }

  public static List<MutableTreeTableNode> breadthFirstSearch(MutableTreeTableNode rootNode) {
    final List<MutableTreeTableNode> result = Lists.newArrayList();
    breadthFirstSearch(rootNode, new Predicate<Pair<MutableTreeTableNode,MutableTreeTableNode>>() {
      public boolean apply(Pair<MutableTreeTableNode, MutableTreeTableNode> parent_child) {
        result.add(parent_child.second());
        return true;
      }
    });
    return result;
  }

  public static void breadthFirstSearch(MutableTreeTableNode root, Predicate<Pair<MutableTreeTableNode, MutableTreeTableNode>> predicate) {
    final Queue<MutableTreeTableNode> queue = Queues.newArrayDeque();
    if (predicate.apply(Pair.create((MutableTreeTableNode) null, root))) {
      queue.add(root);
    }
    while (!queue.isEmpty()) {
      MutableTreeTableNode head = queue.poll();
      for (int i = 0; i < head.getChildCount(); i++) {
        MutableTreeTableNode child = (MutableTreeTableNode) head.getChildAt(i);
        if (predicate.apply(Pair.create(head, child))) {
          queue.add(child);
        }
      }
    }
  }

  public static List<MutableTreeTableNode> collectSubtree(MutableTreeTableNode root) {
    final List<MutableTreeTableNode> result = Lists.newArrayList();
    collectSubtree(root, result);
    return result;
  }

  static void collectSubtree(MutableTreeTableNode root, List<MutableTreeTableNode> result) {
    result.add(root);
    for (int i = 0; i < root.getChildCount(); i++) {
      collectSubtree((MutableTreeTableNode) root.getChildAt(i), result);
    }
  }

  public static void removeAllChildren(MutableTreeTableNode node) {
    List<MutableTreeTableNode> children = Lists.newArrayList();
    for (int i = 0; i < node.getChildCount(); i++) {
      children.add((MutableTreeTableNode) node.getChildAt(i));
    }
    for (MutableTreeTableNode child : children) {
      node.remove(child);
    }
  }

  public static int getLevel(TreeTableNode treeNode) {
    int level = 0;
    while (treeNode != null) {
      treeNode = treeNode.getParent();
      level++;
    }
    return level - 1;
  }
}