package hudson.model.queue;

import static hudson.init.InitMilestone.JOB_CONFIG_ADAPTED;

import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.init.Initializer;
import hudson.model.LoadBalancer;
import hudson.model.Queue;
import hudson.model.Queue.BuildableItem;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
import jenkins.model.Jenkins;

/**
 * Singleton extension point for sorting buildable items
 *
 * @since 1.343
 */
public abstract class QueueSorter implements ExtensionPoint {
    /**
     * A comparator that sorts {@link Queue.BlockedItem} instances based on how long they have been in the queue.
     * (We want the time since in queue by default as blocking on upstream/downstream considers waiting items
     * also and thus the blocking starts once the task is in the queue not once the task is buildable)
     *
     * @since 1.618
     */
    public static final Comparator<Queue.BlockedItem> DEFAULT_BLOCKED_ITEM_COMPARATOR = Comparator.comparingLong(Queue.Item::getInQueueSince);

    /**
     * Sorts the buildable items list. The items at the beginning will be executed
     * before the items at the end of the list.
     *
     * @param buildables
     *      List of buildable items in the queue. Never null.
     */
    public abstract void sortBuildableItems(List<BuildableItem> buildables);

    /**
     * Sorts the blocked items list. The items at the beginning will be considered for removal from the blocked state
     * before the items at the end of the list.
     *
     * @param blockedItems
     *      List of blocked items in the queue. Never null.
     * @since 1.618
     */
    public void sortBlockedItems(List<Queue.BlockedItem> blockedItems) {
        blockedItems.sort(DEFAULT_BLOCKED_ITEM_COMPARATOR);
    }

    /**
     * All registered {@link QueueSorter}s. Only the first one will be picked up,
     * unless explicitly overridden by {@link Queue#setSorter(QueueSorter)}.
     */
    public static ExtensionList<QueueSorter> all() {
        return ExtensionList.lookup(QueueSorter.class);
    }

    /**
     * Installs the default queue sorter.
     *
     * {@link Queue#Queue(LoadBalancer)} is too early to do this
     */
    @Initializer(after = JOB_CONFIG_ADAPTED)
    public static void installDefaultQueueSorter() {
        ExtensionList<QueueSorter> all = all();
        if (all.isEmpty())  return;

        Queue q = Jenkins.get().getQueue();
        if (q.getSorter() != null)        return; // someone has already installed something. leave that alone.

        q.setSorter(all.get(0));
        if (all.size() > 1)
            LOGGER.warning("Multiple QueueSorters are registered. Only the first one is used and the rest are ignored: " + all);
    }

    private static final Logger LOGGER = Logger.getLogger(QueueSorter.class.getName());
}
