/*
 * Copyright 2009 the original author or authors.
 *
 * 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 org.gradle.foundation.visitors;

import org.gradle.foundation.ProjectView;
import org.gradle.foundation.TaskView;
import org.gradle.gradleplugin.foundation.filters.AllowAllProjectAndTaskFilter;
import org.gradle.gradleplugin.foundation.filters.ProjectAndTaskFilter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * This visits each project and task in a hierarchical manner. This visitor is specifically meant to walk the projects first and tasks second for the purpose of populating a tree where the
 * projects/subprojects are first and the tasks are second.
 *
 * @author mhunsicker
 */
public class TaskTreePopulationVisitor {
    public interface Visitor<P, T> {
        /*
           This is called for each project.
         @param  project    the project
         @param indexOfProject
         @param  parentProjectObject whatever you handed back from a prior call to
                 visitProject if this is a sub project. Otherwise, it'll be whatever
                 was passed into the visitPojectsAndTasks function.
           @return an object that will be handed back to you for each of this
                   project's tasks.
           @author mhunsicker
        */

        public P visitProject(ProjectView project, int indexOfProject, P parentProjectObject);

        /*
           This is called for each task.

         @param  task               the task
         @param indexOfTask
         @param  tasksProject       the project for this task
         @param  userProjectObject  whatever you returned from the parent project's visitProject
           @author mhunsicker
        */

        public T visitTask(TaskView task, int indexOfTask, ProjectView tasksProject, P userProjectObject);

        /*
           This is called when a project has been visited completely and is just a
           notification giving you an opportunity to do whatever you like.
           This is possibly where you want to delete any nodes that we didn't
           visit.

           @param  parentProjectObject the object that represents the parent of
                                       the project and task objects below
           @param  projectObjects      a list of whatever you returned from visitProject
           @param  taskObjects         a list of whatever you returned from visitTask
           @author mhunsicker
        */

        public void completedVisitingProject(P parentProjectObject, List<P> projectObjects, List<T> taskObjects);
    }

    /*
       This visitor will visit each project, sub-project and task that was discovered
       by the GradleHelper. This is useful for building a list or tree of
       projects and tasks.

       This is the same as the other version of visitProjectsAndTasks except this
       one visits everything.

       @author mhunsicker
    */

    public static <P, T> void visitProjectAndTasks(List<ProjectView> projects, Visitor<P, T> visitor,
                                                   P rootProjectObject) {
        visitProjectAndTasks(projects, visitor, new AllowAllProjectAndTaskFilter(), rootProjectObject);
    }

    /*
       This visitor will visit each project, sub-project and task that was discovered
       by the GradleHelper. This is useful for building a list or tree of
       projects and tasks.

       @param  visitor    this notified you of each project and task.
       @param  filter     allows you to skip projects and tasks as specified by the filter.
       @param  rootProjectObject whatever you pass here will be passed to the
                root-level projects as parentProjectObject.
       @author mhunsicker
    */

    public static <P, T> void visitProjectAndTasks(List<ProjectView> projects, Visitor<P, T> visitor,
                                                   ProjectAndTaskFilter filter, P rootProjectObject) {
        List<P> userProjectObjects = visitProjects(visitor, filter, projects, rootProjectObject, new AlphabeticalProjectNameComparator(), new AlphabeticalTaskNameComparator());

        //notify the visitation of the root projects. There are no tasks for this one, but there are projects.
        visitor.completedVisitingProject(rootProjectObject, userProjectObjects, Collections.EMPTY_LIST);
    }

    private static <P, T> List<P> visitProjects(Visitor<P, T> visitor, ProjectAndTaskFilter filter,
                                                List<ProjectView> sourceProjects, P parentProjectObject, Comparator<ProjectView> projectSorter, Comparator<TaskView> taskSorter) {
        List<P> projectObjects = new ArrayList<P>();

        sourceProjects = new ArrayList<ProjectView>(sourceProjects);  //make a copy because we're going to sort them.
        Collections.sort(sourceProjects, projectSorter);

        Iterator<ProjectView> iterator = sourceProjects.iterator();
        int index = 0;
        while (iterator.hasNext()) {
            ProjectView project = iterator.next();

            if (filter.doesAllowProject(project)) {
                P userProjectObject = visitor.visitProject(project, index, parentProjectObject);
                projectObjects.add(userProjectObject);

                //visit sub projects
                List<P> subProjectObjects = visitProjects(visitor, filter, project.getSubProjects(), userProjectObject, projectSorter, taskSorter);

                //visit tasks. Notice that we pass in the number of subprojects as a starting index. This is so they'll come afterwards.
                List<T> taskObjects = visitTasks(visitor, filter, project, subProjectObjects.size(), userProjectObject, taskSorter);

                visitor.completedVisitingProject(userProjectObject, subProjectObjects, taskObjects);
            }
            index++;
        }

        return projectObjects;
    }

    /*
       Add the list of tasks to the parent tree node.

       @author mhunsicker
    */

    private static <P, T> List<T> visitTasks(Visitor<P, T> visitor, ProjectAndTaskFilter filter, ProjectView project,
                                             int startingIndex, P userProjectObject, Comparator<TaskView> taskSorter) {
        List<T> taskObjects = new ArrayList<T>();
        List<TaskView> tasks = new ArrayList<TaskView>(project.getTasks()); //make a copy because we're going to sort them
        Collections.sort(tasks, taskSorter);

        Iterator<TaskView> iterator = tasks.iterator();
        int index = startingIndex;
        while (iterator.hasNext()) {
            TaskView task = iterator.next();
            if (filter.doesAllowTask(task)) {
                T taskObject = visitor.visitTask(task, index, project, userProjectObject);
                taskObjects.add(taskObject);
            }
            index++;
        }

        return taskObjects;
    }

    /**
     * This comparator sorts project names alphabetically ignoring case.
     */
    public static class AlphabeticalProjectNameComparator implements Comparator<ProjectView> {
        public int compare(ProjectView o1, ProjectView o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    }

    /**
     * This comparator sorts task names alphabetically ignoring case.
     */
    public static class AlphabeticalTaskNameComparator implements Comparator<TaskView> {
        public int compare(TaskView o1, TaskView o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    }
}

@@ -887,122 +887,38 @@
 er;%0A
-%0A
 import 
-java.util.ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.Comparator;%0Aimport java.util.Iterator;
+org.gradle.util.GUtil;%0A
 %0Aimp
@@ -931,20 +931,17 @@
 va.util.
-List
+*
 ;%0A%0A/**%0A 
@@ -7659,41 +7659,54 @@
 urn 
-o1.getName().compareToIgnoreCase(
+GUtil.caseInsensitive().compare(o1.getName(), 
 o2.g
@@ -7986,41 +7986,54 @@
 urn 
-o1.getName().compareToIgnoreCase(
+GUtil.caseInsensitive().compare(o1.getName(), 
 o2.g

Use GUtil.caseInsensitive(), so that the UI handles projects and tasks with names that differ in case only.