// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:collection';

import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/src/task/api/model.dart';

/**
 * An object that manages the information about the tasks that have been
 * defined.
 */
class TaskManager {
  /**
   * A table mapping [ResultDescriptor]s to a list of [TaskDescriptor]s
   * for the tasks that can be used to compute the result.
   */
  Map<ResultDescriptor, List<TaskDescriptor>> taskMap =
      new HashMap<ResultDescriptor, List<TaskDescriptor>>();

  /**
   * A list of the results that are to be computed for all sources within an
   * analysis root.
   */
  Set<ResultDescriptor> generalResults = new Set<ResultDescriptor>();

  /**
   * A list of the results that are to be computed for priority sources.
   */
  Set<ResultDescriptor> priorityResults = new Set<ResultDescriptor>();

  /**
   * Add the given [result] to the list of results that are to be computed for
   * all sources within an analysis root.
   */
  void addGeneralResult(ResultDescriptor result) {
    generalResults.add(result);
  }

  /**
   * Add the given [result] to the list of results that are to be computed for
   * priority sources.
   */
  void addPriorityResult(ResultDescriptor result) {
    priorityResults.add(result);
  }

  /**
   * Add the given [descriptor] to the list of analysis task descriptors that
   * can be used to compute analysis results.
   */
  void addTaskDescriptor(TaskDescriptor descriptor) {
    descriptor.results.forEach((ResultDescriptor result) {
      //
      // Add the result to the task map.
      //
      List<TaskDescriptor> descriptors = taskMap[result];
      if (descriptors == null) {
        descriptors = <TaskDescriptor>[];
        taskMap[result] = descriptors;
      }
      descriptors.add(descriptor);
    });
  }

  /**
   * Add the task descriptors in the given list of [descriptors] to the list of
   * analysis task descriptors that can be used to compute analysis results.
   */
  void addTaskDescriptors(List<TaskDescriptor> descriptors) {
    descriptors.forEach(addTaskDescriptor);
  }

  /**
   * Find a task that will compute the given [result] for the given [target].
   */
  TaskDescriptor findTask(AnalysisTarget target, ResultDescriptor result) {
    List<TaskDescriptor> descriptors = taskMap[result];
    if (descriptors == null) {
      throw new AnalysisException(
          'No tasks registered to compute $result for $target');
    }
    return _findBestTask(descriptors, target);
  }

  /**
   * Remove the given [result] from the list of results that are to be computed
   * for all sources within an analysis root.
   */
  void removeGeneralResult(ResultDescriptor result) {
    generalResults.remove(result);
  }

  /**
   * Remove the given [result] from the list of results that are to be computed
   * for priority sources.
   */
  void removePriorityResult(ResultDescriptor result) {
    priorityResults.remove(result);
  }

  /**
   * Given a list of task [descriptors] that can be used to compute some
   * unspecified result for the given [target], return the descriptor that
   * should be used to compute the result.
   */
  TaskDescriptor _findBestTask(
      List<TaskDescriptor> descriptors, AnalysisTarget target) {
    TaskDescriptor best = null;
    for (TaskDescriptor descriptor in descriptors) {
      TaskSuitability suitability = descriptor.suitabilityFor(target);
      if (suitability == TaskSuitability.HIGHEST) {
        return descriptor;
      } else if (best == null && suitability == TaskSuitability.LOWEST) {
        best = descriptor;
      }
    }
    return best;
  }
}
