/*
Copyright (C) 2009  Diego Darriba

This program 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 2 of the License, or
(at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package es.uvigo.darwin.prottest.exe;

import es.uvigo.darwin.prottest.global.options.ApplicationOptions;
import es.uvigo.darwin.prottest.model.Model;
import es.uvigo.darwin.prottest.observer.ModelUpdaterObserver;
import es.uvigo.darwin.prottest.observer.ObservableModelUpdater;
import es.uvigo.darwin.prottest.util.collection.ModelCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import pal.alignment.Alignment;

/**
 * Allows the execution of many model optimizations in a thread pool
 * 
 * @author Diego Darriba
 * @since 3.0
 */
public class ParallelModelEstimator extends ObservableModelUpdater
        implements ModelUpdaterObserver {

    /** The runtime **/
    private Runtime runtime = Runtime.getRuntime();
    /** The size of parallel tasks **/
    private int maxNumberOfTasks;
    /** The list of model estimators **/
    private List<RunEstimator> estimatorList;
    /** The pool of threads **/
    private ExecutorService threadPool;
    /** Collection of callable objects (tasks) **/
    private Collection<Callable<Object>> c = new ArrayList<Callable<Object>>();

    /**
     * Instantiates a new ParallelModelEstimator with no models to optimize. 
     * The size of the thread pool is equal to the number of available cores in the machine.
     * 
     * @param alignment the common alignment of the models
     */
    public ParallelModelEstimator(Alignment alignment) {

        this(-1, alignment);

    }

    /**
     * Instantiates a new ParallelModelEstimator with a set of models to optimize
     * The size of the thread pool is equal to the number of available cores in the machine.
     * 
     * @param modelCollection the collection of models to optimize in the pool
     */
    public ParallelModelEstimator(ModelCollection modelCollection) {

        this(-1, modelCollection);

    }

    /**
     * Instantiates a new ParallelModelEstimator with no models to optimize and
     * a fixed size of the thread pool
     *
     * @param availableThreads the size of the thread pool 
     * @param alignment the common alignment of the models
     */
    public ParallelModelEstimator(int availableThreads, Alignment alignment) {

        if (availableThreads < 0) {
            availableThreads = runtime.availableProcessors();
        }
        this.maxNumberOfTasks = availableThreads;
        this.estimatorList = new ArrayList<RunEstimator>(maxNumberOfTasks);
        this.threadPool = Executors.newFixedThreadPool(maxNumberOfTasks);
    }

    /**
     * Instantiates a new ParallelModelEstimator with a set of models to optimize and
     * a fixed size of the thread pool
     *
     * @param availableThreads the size of the thread pool
     * @param modelCollection the collection of models to optimize in the pool
     */
    public ParallelModelEstimator(int availableThreads, ModelCollection modelCollection) {

        if (availableThreads < 0) {
            availableThreads = runtime.availableProcessors();
        }
        this.estimatorList = new ArrayList<RunEstimator>(maxNumberOfTasks);

    }

    /**
     * Executes the model optimization
     * 
     * @param estimator the model estimator to execute
     * 
     * @return if succesfully added the task
     */
    public boolean execute(RunEstimator estimator) {
        estimator.addObserver(this);

        boolean added = estimatorList.add(estimator);
        c.add(Executors.callable(estimator));
        Collection<Future<Object>> futures = null;

        threadPool.execute(estimator);

        return added;
    }

    /* (non-Javadoc)
	 * @see es.uvigo.darwin.prottest.observer.ModelUpdaterObserver#update(es.uvigo.darwin.prottest.observer.ObservableModelUpdater, es.uvigo.darwin.prottest.model.Model, es.uvigo.darwin.prottest, es.uvigo.darwin.prottest.global.options.ApplicationOptions)
	 */
    public void update(ObservableModelUpdater o, Model model, ApplicationOptions options) {
        notifyObservers(model, options);
    }

    /**
     * Checks if exist more tasks in the task queue
     * 
     * @return true, if exist more tasks to execute
     */
    public boolean hasMoreTasks() {
        for (RunEstimator estimator : estimatorList) {
            if (!estimator.getModel().isComputed()) {
                return true;
            }
        }
        return false;
    }
}
