// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.collide.shared.util;

import java.util.Comparator;

import com.google.collide.dto.WorkspaceInfo;
import com.google.collide.json.shared.JsonArray;
import com.google.collide.json.shared.JsonStringMap;

/**
 * Utilities for managing workspaces.
 */
public class WorkspaceUtils {

  /**
   * A node in a hierarchical tree of workspaces.
   */
  public static class WorkspaceNode {
    private final WorkspaceInfo workspace;
    private final JsonArray<WorkspaceNode> children;

    public WorkspaceNode(WorkspaceInfo workspace) {
      this.workspace = workspace;
      this.children = JsonCollections.createArray();
    }

    public void addChild(WorkspaceNode child) {
      children.add(child);
    }

    public WorkspaceNode getChild(int index) {
      return children.get(index);
    }

    public int getChildCount() {
      return children.size();
    }

    /**
     * Sort the direct children of this node.
     */
    public void sortChildren(Comparator<? super WorkspaceNode> comparator) {
      sortChildren(comparator, false);
    }

    /**
     * Sort the children of this node and optionally sort the entire branch
     * recursively.
     */
    public void sortChildren(Comparator<? super WorkspaceNode> comparator, boolean recursive) {
      // Sort the direct children.
      children.sort(comparator);

      // Recursively sort the branch children.
      if (recursive) {
        for (int i = 0; i < getChildCount(); i++) {
          getChild(i).sortChildren(comparator, true);
        }
      }
    }

    public WorkspaceInfo getWorkspace() {
      return workspace;
    }
  }

  /**
   * Take a flat list of workspaces and organize them into a hierarchy (or set
   * or hierarchies) based on workspace parent IDs. The children within each
   * node are not sorted. This method is O(n).
   * 
   * @param workspaces the list of workspaces to organize
   * @return a array of root workspace nodes
   */
  public static JsonArray<WorkspaceNode> getWorkspaceHierarchy(
      JsonArray<WorkspaceInfo> workspaces) {
    /*
     * Assume all workspaces are root nodes. Create a map of workspace IDs to
     * their associated tree nodes.
     */
    final JsonStringMap<WorkspaceNode> idToNode = JsonCollections.createMap();
    final JsonArray<WorkspaceNode> rootNodes = JsonCollections.createArray();
    for (int i = 0; i < workspaces.size(); i++) {
      WorkspaceInfo value = workspaces.get(i);
      WorkspaceNode node = new WorkspaceNode(value);
      rootNodes.add(node);
      idToNode.put(value.getId(), node);
    }

    /*
     * Iterate over the list of workspaces and add each workspace as a child of
     * its parent.
     */
    int count = rootNodes.size();
    for (int i = 0; i < count; i++) {
      WorkspaceNode node = rootNodes.get(i);
      WorkspaceInfo workspace = node.getWorkspace();
      WorkspaceNode parentNode = idToNode.get(workspace.getParentId());
      if (parentNode != null) {
        parentNode.addChild(node);

        // This node has a parent, so it is not a root node.
        rootNodes.remove(i);
        i--;
        count--;
      }
    }

    return rootNodes;
  }

  /**
   * Take a flat list of workspaces and organize them into a hierarchy (or set
   * or hierarchies) based on workspace parent IDs, then sort children within
   * each node using the specified comparator.
   * 
   * @param workspaces the list of workspaces to organize
   * @return a array of root workspace nodes
   */
  public static JsonArray<WorkspaceNode> getWorkspaceHierarchy(
      JsonArray<WorkspaceInfo> workspaces, final Comparator<WorkspaceInfo> comparator) {
    JsonArray<WorkspaceNode> rootNodes = getWorkspaceHierarchy(workspaces);

    // Wrap the WorkspaceInfo comparator in a WorkspaceNode comparator.
    Comparator<WorkspaceNode> nodeComparator = new Comparator<WorkspaceUtils.WorkspaceNode>() {
      @Override
      public int compare(WorkspaceNode o1, WorkspaceNode o2) {
        return comparator.compare(o1.getWorkspace(), o2.getWorkspace());
      }
    };

    // Sort each root node.
    for (int i = 0; i < rootNodes.size(); i++) {
      rootNodes.get(i).sortChildren(nodeComparator, true);
    }

    // Sort the list of root nodes.
    rootNodes.sort(nodeComparator);

    return rootNodes;
  }

  /**
   * Returns the name of the trunk workspace given the name of the project.
   */
  public static String createTrunkWorkspaceName(String projectName) {
    return projectName + " source";
  }
}
