/*
 * MinValueSelection.java
 *
 * Created on 12. říjen 2000, 14:28
 * Documented 18. 7. 2001
 */

package timetable.solver.strategy.value;

import timetable.data.*;
import timetable.solver.strategy.price.*;
import timetable.solver.strategy.*;
import timetable.util.*;

/** Třída implementující implicitní heuristiku výběru umístění. Snahou je vybrat nejlepší umístění. Součástí třídy je použití tabu-listu a náhodný výběr z n nejlepších umístění (pro zabránění cyklaní)
 *
 * @author Tomáš Müller
 * @version 1.0
 */
public class MinValueSelection extends AbstractValueSelection {
    /** Počet nejlepších umístěních, ze kterých se bude vybírat náhosně*/
    protected int TOP10MAX=5;
    /** Výběř N nejlepších umístění */
    protected Top10Value top10 = null;
    /** Tabu-list */
    protected Tabu tabu = null;
    /** Délka tabu-listu */
    protected int tabuSize = -1;
    /** Počet adeptů na umístění */
    protected int nrAdepts = 0;
    /** Minimální nalezená hodnota umístění */
    protected int min = -1;
    /** Nalezené umístění - vybrané zdroje */
    protected ActivityResources solution = null;
    /** Nalezené umístění - konfliktní aktivity */
    protected ActivityGroup conflictActivities = null;
    /** Nalezené umístění - počáteční slot */
    protected int minSlot = -1;
    
    /** Konstruktor.
     * @param config konfigurace
     * @param tabu tabu-list
     * @param evaluation ohodnocovací funkce
     */
    public MinValueSelection(Config config,Tabu tabu,SingleActivityEvaluationInterface evaluation) throws TimetableException {
        super(config,evaluation);
        this.tabu = tabu;
        TOP10MAX=config.getInt("VYBER_HODNOTY_POCET_TOP");
        top10 = new Top10Value(TOP10MAX);        
        tabuSize = config.getInt("DELKA_TABU");
    }
    
    /** Nastavení aktivity, pro kterou se bude umístění hledat. Reset heuristiky.
     * @param activity vybraná aktivita
     */
    public void reset(Activity activity) {
        top10.reset();
        nrAdepts = 0;
        min=-1;minSlot=-1;
        this.activity = activity;
    }
           
    /** Přidání možného umístění do výběru. 
     * @param slot počáteční slot 
     * @param selectedResources vybrané zdroje
     * @param dependences množina všech závislostí
     * @param conflictActivities množina aktivit, které jsou s daným umístěním v konfliktu - budou se muset z rozvrhu odebrat v případě zvolení tohoto umístění
     */
    public void addValue(int slot,ActivityResources selectedResources,ActivityDependenceGroup dependences,ActivityGroup conflictActivities) throws TimetableException {
        if (tabuSize>0 && tabu.isTabuMove(activity,slot,selectedResources)) {
            if (tabu.isTabuMoveTwiceMore(activity,slot,selectedResources)) return;
            int adept = evaluation.value(activity,slot,selectedResources,dependences,conflictActivities);
            if (adept<min || minSlot==-1) {
              minSlot=slot;
              solution = (ActivityResources) selectedResources.clone();
              this.conflictActivities=conflictActivities;
              min = adept;
            };
        } else {
            int adept = evaluation.value(activity,slot,selectedResources,dependences,conflictActivities);
              config.log("      VALUE="+adept);
            if (top10.willBeAdded(adept)) {
                top10.add(adept,slot,(ActivityResources)selectedResources.clone(),conflictActivities);
                config.log("        ADDED");
            }
            nrAdepts++;
        }
    }
    
    /** Metoda provede výběr umístění. */
    public void select() throws TimetableException {
        config.log("    IN TOP10 ARE "+top10.size()+" (INTERESTING "+top10.countInteresting()+") ADEPTS FROM "+nrAdepts);
        if (top10.size()==0 && minSlot<0) return; 
        if (top10.size()>0 && !(minSlot>=0 && min<top10.top(0))) {
            int selected = timetable.util.Math.random(top10.countInteresting());
            solution = top10.resources(selected); 
            conflictActivities = top10.activities(selected);
            minSlot = top10.slot(selected);
            config.log("    SELECTED VALUE "+top10.top(selected)+" ("+(selected+1)+". best adept)");
        } else config.log("    SELECTED VALUE "+min+" (TABU)");
        
        if (tabuSize>0) tabu.add(activity,minSlot,solution);
        config.log("  REMOVED ACTIVITIES : "+conflictActivities.toString());
    }
    
    /** Vrací vybrané umístění.
     * @return počáteční slot
     */
    public int selectedSlot() {
        if (top10.size()==0 && minSlot<0) return -1; 
        return minSlot;
    }

    /** Vrací vybrané umístění.
     * @return vybrané zdroje
     */
    public ActivityResources selectedResources() {
        if (top10.size()==0 && minSlot<0) return null; 
        return solution;
    }
    
    /** Vrací vybrané umístění.
     * @return konfliktní aktivity
     */
    public ActivityGroup selectedConflictActivities() {
        if (top10.size()==0 && minSlot<0) return null; 
        return conflictActivities;
    }
    
}
