//=============================================================================
// Copyright 2006-2010 Daniel W. Dyer
//
// 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.uncommons.watchmaker.swing;

import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import javax.swing.JComboBox;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.EvaluatedCandidate;
import org.uncommons.watchmaker.framework.SelectionStrategy;
import org.uncommons.watchmaker.framework.selection.RankSelection;
import org.uncommons.watchmaker.framework.selection.RouletteWheelSelection;
import org.uncommons.watchmaker.framework.selection.SigmaScaling;
import org.uncommons.watchmaker.framework.selection.StochasticUniversalSampling;
import org.uncommons.watchmaker.framework.selection.TournamentSelection;
import org.uncommons.watchmaker.framework.selection.TruncationSelection;

/**
 * An evolution control for selecting between different {@link SelectionStrategy} implementations.
 * This control provides a proxy selection strategy that delegates to the currently selected
 * strategy.  Using this proxy strategy with an {@link org.uncommons.watchmaker.framework.EvolutionEngine}
 * means that any change to the combo-box selection is immediately reflected in the selection used
 * by the running evolution engine.
 * @param <T> A generic type that matches the type associated with the selection strategies.
 * @author Daniel Dyer
 */
public class SelectionStrategyControl<T> implements EvolutionControl
{
    private final JComboBox control;
    private final ProxySelectionStrategy selectionStrategy;


    /**
     * Creates a control for choosing between a specified set of selection strategies.
     * @param options The selection strategies to choose from.
     */
    public SelectionStrategyControl(List<SelectionStrategy<? super T>> options)
    {
        this.control = new JComboBox(new Vector<SelectionStrategy<? super T>>(options));
        this.selectionStrategy = new ProxySelectionStrategy(options.get(0));
        this.control.addItemListener(new ItemListener()
        {
            public void itemStateChanged(ItemEvent ev)
            {
                if (ev.getStateChange() == ItemEvent.SELECTED)
                {
                    @SuppressWarnings("unchecked")
                    SelectionStrategy<? super T> delegate = (SelectionStrategy<? super T>) control.getSelectedItem();
                    selectionStrategy.setDelegate(delegate);
                }
            }
        });
    }


    /**
     * Creates a list containing one instance of each of the standard selection strategies.
     * These strategies are {@link RankSelection}, {@link RouletteWheelSelection},
     * {@link StochasticUniversalSampling}, {@link TournamentSelection} and {@link TruncationSelection}.
     * @param tournamentProbability The probability parameter for {@link TournamentSelection}.
     * @param truncationRatio The ratio parameter for {@link TruncationSelection}.
     * @return A list of selection strategies.
     */
    public static <T> List<SelectionStrategy<? super T>> createDefaultOptions(Probability tournamentProbability,
                                                                              double truncationRatio)
    {
        List<SelectionStrategy<? super T>> options = new LinkedList<SelectionStrategy<? super T>>();
        options.add(new RankSelection());
        options.add(new RouletteWheelSelection());
        options.add(new SigmaScaling());
        options.add(new StochasticUniversalSampling());
        options.add(new TournamentSelection(tournamentProbability));
        options.add(new TruncationSelection(truncationRatio));
        return options;
    }


    /**
     * {@inheritDoc}
     */
    public JComboBox getControl()
    {
        return control;
    }


    /**
     * {@inheritDoc}
     */
    public void reset()
    {
        control.setSelectedIndex(0);
    }


    /**
     * {@inheritDoc}
     */
    public void setDescription(String description)
    {
        control.setToolTipText(description);
    }    


    /**
     * @return A proxied {@link SelectionStrategy} that delegates to whichever
     * concrete selection strategy is currently selected.
     */
    public SelectionStrategy<T> getSelectionStrategy()
    {
        return selectionStrategy;
    }


    /**
     * A {@link SelectionStrategy} implementation that simply delegates to the selection strategy
     * currently selected by the combobox control.
     */
    private class ProxySelectionStrategy implements SelectionStrategy<T>
    {
        private volatile SelectionStrategy<? super T> delegate;

        ProxySelectionStrategy(SelectionStrategy<? super T> delegate)
        {
            this.delegate = delegate;
        }


        public void setDelegate(SelectionStrategy<? super T> delegate)
        {
            this.delegate = delegate;
        }


        /**
         * {@inheritDoc}
         */
        public <S extends T> List<S> select(List<EvaluatedCandidate<S>> population,
                                            boolean naturalFitnessScores,
                                            int selectionSize,
                                            Random rng)
        {
            return delegate.select(population, naturalFitnessScores, selectionSize, rng);
        }
    }
}
