/FileExtraction/Java_unseen/crisis-economics_CRISIS/CRISIS/src/eu/crisis_economics/abm/dashboard/GASearchPanel.java
/*
 * This file is part of CRISIS, an economics simulator.
 * 
 * Copyright (C) 2015 AITIA International, Inc.
 * Copyright (C) 2015 John Kieran Phillips
 *
 * CRISIS 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 3 of the License, or
 * (at your option) any later version.
 *
 * CRISIS 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 CRISIS.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.crisis_economics.abm.dashboard;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.jdesktop.swingx.JXLabel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ai.aitia.meme.paramsweep.batch.IModelInformation.ModelInformationException;
import ai.aitia.meme.paramsweep.batch.output.RecordableInfo;
import ai.aitia.meme.paramsweep.gui.info.MasonChooserParameterInfo;
import ai.aitia.meme.paramsweep.gui.info.ParameterInfo;
import ai.aitia.meme.paramsweep.gui.info.SubmodelInfo;
import ai.aitia.meme.paramsweep.intellisweepPlugin.utils.ga.GeneInfo;
import ai.aitia.meme.paramsweep.internal.platform.PlatformSettings;
import ai.aitia.meme.paramsweep.plugin.IStatisticsPlugin;
import ai.aitia.meme.paramsweep.utils.Utilities;
import ai.aitia.meme.utils.GUIUtils;
import ai.aitia.meme.utils.Utils;
import aurelienribon.ui.css.Style;

import com.jgoodies.forms.layout.CellConstraints;

import eu.crisis_economics.abm.dashboard.GASearchPanelModel.FitnessFunctionDirection;
import eu.crisis_economics.abm.dashboard.GASearchPanelModel.ModelListener;
import eu.crisis_economics.abm.dashboard.Page_Parameters.ComboBoxIsTooFullException;
import eu.crisis_economics.abm.dashboard.ga.IGAConfigurator;
import eu.crisis_economics.abm.dashboard.ga.IGAOperatorConfigurator;
import eu.crisis_economics.abm.dashboard.ga.IGASelectorConfigurator;

/**
 * @author Tamás Máhr
 *
 */
public class GASearchPanel extends JPanel {
	
	//====================================================================================================
	// members
   
	private static final long serialVersionUID = 7957280032992002080L;
	
	private static final String NUMBER_OF_TURNS_IN_A_SIMULATION = "Number of turns in a simulation";
	
	private static final String GENE_SETTINGS_INT_RANGE_VALUE_PANEL = "int_range";
	private static final String GENE_SETTINGS_DOUBLE_RANGE_VALUE_PANEL = "double_range";
	private static final String GENE_SETTINGS_LIST_VALUE_PANEL = "list";
	private static final String PARAM_SETTINGS_CONSTANT_VALUE_PANEL = "constant";
	private static final String PARAM_SETTINGS_ENUM_VALUE_PANEL = "enum";
	private static final String PARAM_SETTINGS_FILE_VALUE_PANEL = "file";
	private static final String PARAM_SETTINGS_SUBMODEL_VALUE_PANEL = "submodel";
	
	private static final String DESCRIPTION_PANEL = "Description";
	private static final String OPERATOR_SETTINGS_PANEL_DEFAULT_PANEL = "DEFAULT";
	private static final String OPERATOR_SETTINGS_PANEL_TITLE_POSTFIX = "Operator settings";
	
	private static final Logger log = LoggerFactory.getLogger(GASearchPanel.class);
	private static final NumberFormat numberFormatter = NumberFormat.getInstance(Locale.US);
	
	static { // print double numbers rounded to 2 decimal digits using US format
		numberFormatter.setMaximumFractionDigits(2);
		numberFormatter.setMinimumFractionDigits(0);
		numberFormatter.setGroupingUsed(false);
	}
	
	private Page_Parameters parentPage;
	private JTextField numberOfTurnsField;
	private JSpinner populationSizeField;
	private JTextField populationRandomSeedField;
	private JRadioButton numberOfGenerationsRadioButton;
	private JSpinner numberOfGenerationsField;
	private JRadioButton fitnessLimitRadioButton;
	private JTextField fitnessLimitField;
	private JRadioButton fitnessMinimizeRadioButton;
	private JRadioButton fitnessMaximizeRadioButton;
	private JComboBox fitnessFunctionList;
	private JComboBox fitnessStatistics;
	private JSpinner fitnessTimeSeriesLength;
	private JTree geneTree;
	private JLabel numberLabel;
	protected Popup errorPopup;
	private JTextField constantField;
	private JTextField intMinField;
	private JTextField intMaxField;
	private JTextField doubleMinField;
	private JTextField doubleMaxField;
	private JTextArea valuesArea;
	private JComboBox enumDefBox;
	private JComboBox submodelTypeBox;
	private JTextField fileTextField;
	private JButton fileBrowseButton;

	private JRadioButton constantRButton;
	private JRadioButton doubleGeneValueRangeRButton;
	private JRadioButton intGeneValueRangeRButton;
	private JRadioButton listGeneValueRButton;

	private JPanel geneSettingsValuePanel;
	private JXLabel descriptionLabel;
	private JButton modifyButton;
	
	private DefaultMutableTreeNode editedNode;

	private JButton cancelButton;

	private JScrollPane mainScrPane;

	private JPanel operatorSettingsPanel;

	private CheckList selectorList;

	private CheckList operatorList;

	private GASearchPanelModel model;

	//====================================================================================================
	// methods
	
	//----------------------------------------------------------------------------------------------------
	public GASearchPanel(final GASearchPanelModel model, final Page_Parameters parentPage) {
		this.model = model;
		this.parentPage = parentPage;
		initSearchPanel(model);
	}
	
	//----------------------------------------------------------------------------------------------------
	public double getNumberOfRequestedTurns() {
		return Double.parseDouble(numberOfTurnsField.getText());
	}
	
	//----------------------------------------------------------------------------------------------------
	public void reset(final GASearchPanelModel model) {
		numberOfTurnsField.setText(double2String(model.getNumberOfTurns()));
		populationSizeField.setValue(model.getPopulationSize());
		populationRandomSeedField.setText(String.valueOf(model.getPopulationRandomSeed()));
		
		// create the stopping conditions panel
		numberOfGenerationsRadioButton.setSelected(true);
		numberOfGenerationsField.setValue(model.getNumberOfGenerations());
		fitnessLimitField.setText(String.valueOf(model.getFitnessLimitCriterion()));
		
		if (model.getNumberOfGenerations() < 0) {
			fitnessLimitRadioButton.setSelected(true);
			numberOfGenerationsField.setEnabled(false);
		} else {
			fitnessLimitField.setEnabled(false);
			numberOfGenerationsRadioButton.setSelected(true);
		}
		
		if (model.getFitnessFunctionDirection() == FitnessFunctionDirection.MINIMIZE) 
			fitnessMinimizeRadioButton.setSelected(true);
		else 
			fitnessMaximizeRadioButton.setSelected(true);
		
		fitnessFunctionList.setSelectedItem(model.getSelectedFitnessFunction());
		fitnessStatistics.setSelectedItem(model.getSelectedFitnessStatistics());
		fitnessTimeSeriesLength.setValue(model.getFitnessTimeSeriesLength());
		
		final CardLayout settingslayout = (CardLayout) operatorSettingsPanel.getLayout();
		settingslayout.show(operatorSettingsPanel,OPERATOR_SETTINGS_PANEL_DEFAULT_PANEL);
		((TitledBorder)operatorSettingsPanel.getBorder()).setTitle(OPERATOR_SETTINGS_PANEL_TITLE_POSTFIX);
		descriptionLabel.setText("");
		
		final List<IGASelectorConfigurator> selectionOperators = model.getSelectionOperators();
		final List<IGASelectorConfigurator> selectedSelectionOperators = model.getSelectedSelectionOperators();
		
		int i = 0;
		for (final IGASelectorConfigurator selectionOperator : selectionOperators) {
			selectorList.setChecked(i,selectedSelectionOperators.contains(selectionOperator));
			++i;
		}
		selectorList.clearSelection();

		final List<IGAOperatorConfigurator> geneticOperators = model.getGeneticOperators();
		final List<IGAOperatorConfigurator> selectedGeneticOperators = model.getSelectedGeneticOperators();

		i = 0;
		for (final IGAOperatorConfigurator operator : geneticOperators){
			operatorList.setChecked(i,selectedGeneticOperators.contains(operator));
			++i;
		}
		operatorList.clearSelection();

		numberLabel.setText("<html><b>Number of genes:</b> 0</html>");
		
		for (int j = 0;j < geneTree.getRowCount();++j) 
			geneTree.expandRow(j);
		
		resetSettings();
		enableDisableSettings(false);
	}
	
	//----------------------------------------------------------------------------------------------------
//	public void setSelectedFitnessFunction(final GASearchPanelModel gaSearchHandler) {
//		fitnessFunctionList.setSelectedItem(gaSearchHandler.getSelectedFitnessFunction());
//	}
	
	
	//====================================================================================================
	// assistant methods
		
	//----------------------------------------------------------------------------------------------------
	private void initSearchPanel(final GASearchPanelModel model) {
		model.addModelListener(new ModelListener() {
			public void fitnessFunctionAdded() {
				fitnessFunctionList.removeAllItems();
				final List<?> list = model.getFitnessFunctions();
				for (final Object fitnessFunction : list) {
					fitnessFunctionList.addItem(fitnessFunction);
				}				
			}
			
			public void fitnessFunctionsRemoved() {
				fitnessFunctionList.removeAllItems();
			}

			public void parameterAdded() {
				final DefaultTreeModel treeModel = model.getChromosomeTree();
				final DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
 				geneTree.expandPath(new TreePath(treeModel.getPathToRoot(root)));
			}

			public void parametersRemoved() {
				parameterAdded(); // :)
			}
		});
		
		numberOfTurnsField = new JTextField(double2String(model.getNumberOfTurns()));
		numberOfTurnsField.setInputVerifier(new InputVerifier() {

			public boolean verify(final JComponent input) {
				if (errorPopup != null) {
					errorPopup.hide();
					errorPopup = null;
				}

				try {
					final double value = Double.parseDouble(numberOfTurnsField.getText());
					model.setNumberOfTurns(value);
					return true;
				} catch (final NumberFormatException e) {
					final PopupFactory popupFactory = PopupFactory.getSharedInstance();
					final Point locationOnScreen = numberOfTurnsField.getLocationOnScreen();
					final JLabel message = new JLabel("Please specify a (possibly floating point) number!");
					message.setBorder(new LineBorder(Color.RED, 2, true));
					errorPopup = popupFactory.getPopup(numberOfTurnsField, message, locationOnScreen.x - 10, locationOnScreen.y - 30);
					errorPopup.show();
					
					return false;
				}
			}
		});
		
		final JPanel numberOfTurnsPanel = FormsUtils.build("p ' f:p:g", "01", NUMBER_OF_TURNS_IN_A_SIMULATION, numberOfTurnsField).getPanel();
		
		// create the population parameters panel
		populationSizeField = new JSpinner(new SpinnerNumberModel(model.getPopulationSize(),2,Integer.MAX_VALUE,1));
		populationSizeField.addChangeListener(new ChangeListener() {
			public void stateChanged(final ChangeEvent e) {
				model.setPopulationSize((Integer)((JSpinner)e.getSource()).getValue());
			}
		});
		
		populationRandomSeedField = new JTextField(String.valueOf(model.getPopulationRandomSeed()));
		populationRandomSeedField.setInputVerifier(new InputVerifier() {
			
			public boolean verify(final JComponent input) {
				if (errorPopup != null) {
					errorPopup.hide();
					errorPopup = null;
				}
				
				try {
					final int value = Integer.parseInt(populationRandomSeedField.getText());
					model.setPopulationRandomSeed(value);
					return true;
				} catch (final NumberFormatException e) {
					final PopupFactory popupFactory = PopupFactory.getSharedInstance();
					final Point locationOnScreen = populationRandomSeedField.getLocationOnScreen();
					final JLabel message = new JLabel("Please specify an integer number!");
					message.setBorder(new LineBorder(Color.RED, 2, true));
					errorPopup = popupFactory.getPopup(populationRandomSeedField, message, locationOnScreen.x - 10, locationOnScreen.y - 30);
					errorPopup.show();
					
					return false;					
				}
			}
		});
		
		final JPanel populationPanel = FormsUtils.build("p ~ f:p:g", 
				"01 ||"+
				"23", 
				"Size",populationSizeField,
				"Random seed", populationRandomSeedField).getPanel();
		populationPanel.setBorder(BorderFactory.createTitledBorder(null,"Population",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(populationPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		// create the stopping conditions panel
		numberOfGenerationsRadioButton = new JRadioButton("Number of generations");
		numberOfGenerationsField = new JSpinner(new SpinnerNumberModel(model.getNumberOfGenerations(),1,Integer.MAX_VALUE,1));
		numberOfGenerationsField.addChangeListener(new ChangeListener() {
			
			public void stateChanged(final ChangeEvent e) {
				model.setNumberOfGenerations((Integer)((JSpinner)e.getSource()).getValue());				
			}
		});
		numberOfGenerationsRadioButton.addChangeListener(new ChangeListener() {
			
			public void stateChanged(final ChangeEvent e) {
				if (numberOfGenerationsRadioButton.isSelected()) {
					numberOfGenerationsField.setEnabled(true);
					model.setFixNumberOfGenerations(true);
				} else 
					numberOfGenerationsField.setEnabled(false);
			}
		});
		
		fitnessLimitRadioButton = new JRadioButton("Fitness limit");
		fitnessLimitField = new JTextField(String.valueOf(model.getFitnessLimitCriterion()));
		fitnessLimitField.setInputVerifier(new InputVerifier() {
			
			public boolean verify(final JComponent input) {
				if (errorPopup != null){
					errorPopup.hide();
					errorPopup = null;
				}

				try {
					final double value = Double.parseDouble(fitnessLimitField.getText());
					if (value < 0)
						throw new NumberFormatException();
					model.setFitnessLimitCriterion(value);
					return true;
				} catch (final NumberFormatException e) {
					final PopupFactory popupFactory = PopupFactory.getSharedInstance();
					final Point locationOnScreen = fitnessLimitField.getLocationOnScreen();
					final JLabel message = new JLabel("Please specify a non-negative (possibly floating point) number!");
					message.setBorder(new LineBorder(Color.RED, 2, true));
					errorPopup = popupFactory.getPopup(fitnessLimitField, message, locationOnScreen.x - 10, locationOnScreen.y - 30);
					errorPopup.show();
					
					return false;
				}
			}
		});
		fitnessLimitRadioButton.addChangeListener(new ChangeListener() {

			public void stateChanged(final ChangeEvent e) {
				if (fitnessLimitRadioButton.isSelected()) {
					fitnessLimitField.setEnabled(true);
					model.setFixNumberOfGenerations(false);
				} else 
					fitnessLimitField.setEnabled(false);
			}
		});
		
		GUIUtils.createButtonGroup(numberOfGenerationsRadioButton,fitnessLimitRadioButton);
		
		if (model.getNumberOfGenerations() < 0) {
			fitnessLimitRadioButton.setSelected(true);
			numberOfGenerationsField.setEnabled(false);
			model.setFixNumberOfGenerations(false);
		} else {
			fitnessLimitField.setEnabled(false);
			numberOfGenerationsRadioButton.setSelected(true);
			model.setFixNumberOfGenerations(true);
		}
		
		final JPanel stoppingPanel = FormsUtils.build("p ~ f:p:g", 
				"01 |"+
				"23", 
				numberOfGenerationsRadioButton,numberOfGenerationsField,
				fitnessLimitRadioButton,fitnessLimitField).getPanel();
		stoppingPanel.setBorder(BorderFactory.createTitledBorder(null,"Stopping",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(stoppingPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		// create the fitness function panel
		fitnessMinimizeRadioButton = new JRadioButton("Minimize");
		fitnessMaximizeRadioButton = new JRadioButton("Maximize");
		
		GUIUtils.createButtonGroup(fitnessMinimizeRadioButton,fitnessMaximizeRadioButton);
		GUIUtils.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (fitnessMinimizeRadioButton.isSelected())
					model.setFitnessFunctionDirection(FitnessFunctionDirection.MINIMIZE);
				else if (fitnessMaximizeRadioButton.isSelected())
					model.setFitnessFunctionDirection(FitnessFunctionDirection.MAXIMIZE);
			}
		},fitnessMinimizeRadioButton,fitnessMaximizeRadioButton);
		
		if (model.getFitnessFunctionDirection() == FitnessFunctionDirection.MINIMIZE) 
			fitnessMinimizeRadioButton.setSelected(true);
		else 
			fitnessMaximizeRadioButton.setSelected(true);
		
		final JPanel fitnessButtonsPanel = new JPanel();
		Style.registerCssClasses(fitnessButtonsPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		fitnessButtonsPanel.add(fitnessMinimizeRadioButton);
		fitnessButtonsPanel.add(fitnessMaximizeRadioButton);
		
		fitnessFunctionList = new JComboBox(model.getFitnessFunctions().toArray(new Object[0]));
		fitnessFunctionList.setSelectedItem(model.getSelectedFitnessFunction());
		fitnessFunctionList.addItemListener(new ItemListener() {
			
			public void itemStateChanged(final ItemEvent e) {
				if (ItemEvent.SELECTED == e.getStateChange())
					model.setSelectedFitnessFunction((RecordableInfo) e.getItem());
			}
		});
		
		initializeStatistics(model);
		final SpinnerNumberModel spinnerNumberModel = new SpinnerNumberModel();
		spinnerNumberModel.setStepSize(10);
		spinnerNumberModel.setValue(model.getFitnessTimeSeriesLength());
		fitnessTimeSeriesLength = new JSpinner(spinnerNumberModel);
		spinnerNumberModel.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) {
				model.setFitnessTimeSeriesLength((Integer)spinnerNumberModel.getValue());
			}
		});
		
		JXLabel fitnessMessage = new JXLabel("Please specify a function that always returns with non-negative value!");
		fitnessMessage.setLineWrap(true);
		final JPanel fitnessPanel = FormsUtils.build("p ~ f:p:g ~ p", 
				"000 |" +
				"122 ||" +
				"345 ||" +
				"677 ||" +
				"888",
				fitnessButtonsPanel,
				"Statistics", fitnessStatistics,
				"of the last", fitnessTimeSeriesLength, "values of",
				"Fitness function", fitnessFunctionList,
				fitnessMessage).getPanel();
		fitnessPanel.setBorder(BorderFactory.createTitledBorder(null,"Fitness",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(fitnessPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		operatorSettingsPanel = new JPanel(new CardLayout());
		operatorSettingsPanel.setBorder(BorderFactory.createTitledBorder(null,OPERATOR_SETTINGS_PANEL_TITLE_POSTFIX,TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(operatorSettingsPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		operatorSettingsPanel.add(new JLabel("Here come the operator settings!"), OPERATOR_SETTINGS_PANEL_DEFAULT_PANEL);
		
		final JPanel operatorDescriptionPanel = new JPanel(new BorderLayout());
		operatorDescriptionPanel.setBorder(BorderFactory.createTitledBorder(null,DESCRIPTION_PANEL,TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(operatorDescriptionPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		operatorDescriptionPanel.add(initDescriptionPanel(),BorderLayout.CENTER);
		
		final List<IGASelectorConfigurator> selectionOperators = model.getSelectionOperators();
		final List<IGASelectorConfigurator> selectedSelectionOperators = model.getSelectedSelectionOperators();
		final CheckListModel selectorListModel = new CheckListModel(selectionOperators);
		selectorList = new CheckList(selectorListModel);
		Style.registerCssClasses(selectorList, Dashboard.CSS_CLASS_COMMON_PANEL);
		selectorList.setBorder(BorderFactory.createTitledBorder(null,"Selection",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		selectorList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		
		int i = 0;
		for (final IGASelectorConfigurator selectionOperator : selectionOperators) {
			final JPanel settingspanel = selectionOperator.getSettingspanel();
			Style.registerCssClasses(settingspanel, Dashboard.CSS_CLASS_COMMON_PANEL);
			
			String description = selectionOperator.getDescription();
			Style.registerCssClasses(description, Dashboard.CSS_CLASS_COMMON_PANEL);
			operatorSettingsPanel.add(settingspanel,selectionOperator.getName());
			
			if (selectedSelectionOperators.contains(selectionOperator))
				selectorList.setChecked(i, true);
			
			++i;
		}
		
		selectorList.addListSelectionListener(new ListSelectionListener() {
			
			public void valueChanged(final ListSelectionEvent e) {
				final IGASelectorConfigurator operator = (IGASelectorConfigurator) selectorList.getSelectedValue();
				showOperatorDetails(operatorSettingsPanel, operatorDescriptionPanel,operator);
			}
		});
		
		selectorListModel.addListDataListener(new ListDataListener() {
			
			public void intervalRemoved(final ListDataEvent e) {}
			public void intervalAdded(final ListDataEvent e) {}
			
			public void contentsChanged(final ListDataEvent e) {
				final IGASelectorConfigurator operator = (IGASelectorConfigurator) selectorList.getSelectedValue();
				
				if (selectorList.getModel().getCheckedState(e.getIndex0()))
					model.setSelectedSelectionOperator(operator);
				else 
					model.unsetSelectedSelectionOperator(operator);
			}
		});

		List<IGAOperatorConfigurator> geneticOperators = model.getGeneticOperators();
		List<IGAOperatorConfigurator> selectedGeneticOperators = model.getSelectedGeneticOperators();
		CheckListModel operatorListModel = new CheckListModel(geneticOperators);
		operatorList = new CheckList(operatorListModel);
		Style.registerCssClasses(operatorList, Dashboard.CSS_CLASS_COMMON_PANEL);
		operatorList.setBorder(BorderFactory.createTitledBorder(null,"Genetic operators",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		operatorList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		i = 0;
		for (final IGAOperatorConfigurator operator : geneticOperators){
			final JPanel settingsPanel = operator.getSettingspanel();
			Style.registerCssClasses(settingsPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
			
			String description = operator.getDescription();
			Style.registerCssClasses(description, Dashboard.CSS_CLASS_COMMON_PANEL);
			operatorSettingsPanel.add(settingsPanel,operator.getName());
			
			if (selectedGeneticOperators.contains(operator))
				operatorList.setChecked(i, true);
			
			++i;
		}

		operatorList.addListSelectionListener(new ListSelectionListener() {
			
			public void valueChanged(final ListSelectionEvent e) {
				final IGAOperatorConfigurator operator = (IGAOperatorConfigurator) operatorList.getSelectedValue();
				showOperatorDetails(operatorSettingsPanel, operatorDescriptionPanel, operator);
			}
		});
		
		operatorListModel.addListDataListener(new ListDataListener() {
			
			public void intervalRemoved(final ListDataEvent e) {}
			public void intervalAdded(final ListDataEvent e) {}
			
			public void contentsChanged(final ListDataEvent e) {
				final IGAOperatorConfigurator operator = (IGAOperatorConfigurator) operatorList.getSelectedValue();
				
				if (operatorList.getModel().getCheckedState(e.getIndex0()))
					model.setSelectedGeneticOperator(operator);
				else
					model.unsetSelectedGeneticOperator(operator);
			}
		});		

		final JPanel operatorDetailsPanel = FormsUtils.build("f:p:g(0.5) f:p:g(0.5)", 
				"01", operatorSettingsPanel, operatorDescriptionPanel).getPanel();
		Style.registerCssClasses(operatorDetailsPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel gaSettingsPanel = FormsUtils.build("f:p:g", 
				"0||" +
				"1||" +
				"2||" +
				"3||" +
				"4||" + 
				"5||" + 
				"6||" +
				"7",
				new FormsUtils.Separator("General parameters"),
				numberOfTurnsPanel,
				new FormsUtils.Separator("Genetic algorithm settings"),
				populationPanel,
				stoppingPanel,
				fitnessPanel,
				selectorList,
				operatorList).getPanel();
		Style.registerCssClasses(gaSettingsPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		// the chromosome panel
	    final JScrollPane treeScrPane = new JScrollPane();
	    geneTree = new JTree(model.getChromosomeTree()); 
		geneTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		geneTree.setCellRenderer(new ChromosomeTreeRenderer());
		
		geneTree.addTreeSelectionListener(new TreeSelectionListener() {
			public void valueChanged(final TreeSelectionEvent e) {
				final TreePath selectionPath = geneTree.getSelectionPath();
				
				boolean success = true;
				if (editedNode != null && (selectionPath == null || !editedNode.equals(selectionPath.getLastPathComponent()))) 
					success = modify();
				
				final DefaultTreeModel treeModel = model.getChromosomeTree();
				if (success) {
					if (selectionPath != null) {
						final DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectionPath.getLastPathComponent();
						final ParameterOrGene userObj = (ParameterOrGene) node.getUserObject();
						if (userObj.getInfo().getDescription() != null && !userObj.getInfo().getDescription().trim().isEmpty())
							descriptionLabel.setText(userObj.getInfo().getDescription());
						else
							descriptionLabel.setText("");
						
						if (!node.equals(editedNode)) {
							if (!node.isRoot() && selectionPath.getPathCount() == treeModel.getPathToRoot(node).length) {
								editedNode = node;
								edit(userObj);
							} else {
								editedNode = null;
								geneTree.setSelectionPath(null);
								resetSettings();
								enableDisableSettings(false);
								descriptionLabel.setText("");
							}
						}
					} else {
						editedNode = null;
						resetSettings();
						enableDisableSettings(false);
						descriptionLabel.setText("");
					}
				} else 
					geneTree.setSelectionPath(new TreePath(treeModel.getPathToRoot(editedNode)));
				
			}
		});

	    treeScrPane.setViewportView(geneTree);
	    treeScrPane.setBorder(null);
	    treeScrPane.setViewportBorder(null);
		
		numberLabel = new JLabel("<html><b>Number of genes:</b> 0</html>");
		final JPanel treePanel = FormsUtils.build("~ f:p:g",
				  "0||" +
				  "1||" +
				  "2 f:p:g", 
				  numberLabel,
				  new FormsUtils.Separator(""),
				  treeScrPane).getPanel(); 


		treePanel.setBorder(BorderFactory.createTitledBorder(""));
		Style.registerCssClasses(treePanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		constantRButton = new JRadioButton("Constant");
		doubleGeneValueRangeRButton = new JRadioButton("Double value range");
		intGeneValueRangeRButton = new JRadioButton("Integer value range");
		listGeneValueRButton = new JRadioButton("Value list");
		GUIUtils.createButtonGroup(constantRButton,doubleGeneValueRangeRButton,intGeneValueRangeRButton,listGeneValueRButton);

		geneSettingsValuePanel = new JPanel(new CardLayout());
		Style.registerCssClasses(geneSettingsValuePanel, Dashboard.CSS_CLASS_COMMON_PANEL);

		constantField = new JTextField();
		constantField.setActionCommand("CONST_FIELD");
		
		final JPanel constantPanel = FormsUtils.build("p ~ p:g",
								 		"[DialogBorder]01 p",
								 		"Constant value",constantField).getPanel();
		Style.registerCssClasses(constantPanel,Dashboard.CSS_CLASS_COMMON_PANEL);

		
		enumDefBox = new JComboBox(new DefaultComboBoxModel());
		
		final JPanel enumDefPanel = FormsUtils.build("p ~ p:g", 
								"[DialogBorder]01 p", 
								"Constant value",enumDefBox).getPanel();
		Style.registerCssClasses(enumDefPanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		submodelTypeBox = new JComboBox();
		
		final JPanel submodelTypePanel = FormsUtils.build("p ~ p:g",
											"[DialogBorder]01",
											"Constant value",submodelTypeBox).getPanel();
		Style.registerCssClasses(submodelTypePanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		fileTextField = new JTextField();
		fileTextField.setActionCommand("FILE_FIELD");
		fileTextField.addKeyListener(new KeyAdapter() {
			public void keyTyped(final KeyEvent e) { 
				final char character = e.getKeyChar();
				final File file = new File(Character.isISOControl(character) ? fileTextField.getText() : fileTextField.getText() + character);
				fileTextField.setToolTipText(file.getAbsolutePath());
			}
		});
		fileBrowseButton = new JButton(Page_Parameters.BROWSE_BUTTON_TEXT);
		fileBrowseButton.setActionCommand("FILE_BROWSE");
		
		final JPanel fileDefPanel = FormsUtils.build("p ~ p:g ~ p",
									   "[DialogBorder]012",
									   "File",fileTextField,fileBrowseButton).getPanel();
		Style.registerCssClasses(fileDefPanel,Dashboard.CSS_CLASS_COMMON_PANEL);

		intMinField = new JTextField();
		intMinField.setActionCommand("INT_MIN");
		intMaxField = new JTextField();
		intMaxField.setActionCommand("INT_MAX");
		doubleMinField = new JTextField();
		doubleMinField.setActionCommand("D_MIN");
		doubleMaxField = new JTextField();
		doubleMaxField.setActionCommand("D_MAX");
		valuesArea = new JTextArea();
		valuesArea.setToolTipText("Please specify ' ' (space) separated values!");
		valuesArea.setLineWrap(true);
		valuesArea.setWrapStyleWord(true);
		final JScrollPane valuesDefScr = new JScrollPane(valuesArea,ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		final JPanel doublePanel = FormsUtils.build("p ' f:p:g",
									  "[DialogBorder]01 p ||" +
											  		"23 p",
											  		"Minimum value",doubleMinField,
											  		"Maximum value",doubleMaxField).getPanel();
		Style.registerCssClasses(doublePanel,Dashboard.CSS_CLASS_COMMON_PANEL);

		final JPanel intPanel = FormsUtils.build("p ' f:p:g",
								   "[DialogBorder]01 p ||" +
										   		 "23 p",
										   		 "Minimum value",intMinField,
										   		 "Maximum value",intMaxField).getPanel();
		Style.registerCssClasses(intPanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel valuesPanel = FormsUtils.build("p ' p:g",
									  "[DialogBorder]01 f:p:g",
								      "Value list",CellConstraints.TOP,valuesDefScr).getPanel();
		Style.registerCssClasses(valuesPanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel emptyPanel = new JPanel();
		Style.registerCssClasses(emptyPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		geneSettingsValuePanel.add(emptyPanel,"NULL");
		geneSettingsValuePanel.add(constantPanel,PARAM_SETTINGS_CONSTANT_VALUE_PANEL);
		geneSettingsValuePanel.add(enumDefPanel,PARAM_SETTINGS_ENUM_VALUE_PANEL);
		geneSettingsValuePanel.add(fileDefPanel,PARAM_SETTINGS_FILE_VALUE_PANEL);
		geneSettingsValuePanel.add(submodelTypePanel,PARAM_SETTINGS_SUBMODEL_VALUE_PANEL);
		geneSettingsValuePanel.add(doublePanel,GENE_SETTINGS_DOUBLE_RANGE_VALUE_PANEL);
		geneSettingsValuePanel.add(intPanel,GENE_SETTINGS_INT_RANGE_VALUE_PANEL);
		geneSettingsValuePanel.add(valuesPanel,GENE_SETTINGS_LIST_VALUE_PANEL);
		
		final ItemListener itemListener = new ItemListener() {
			public void itemStateChanged(final ItemEvent _) {
				if (editedNode != null) {
					final CardLayout layout = (CardLayout) geneSettingsValuePanel.getLayout();
					if (doubleGeneValueRangeRButton.isSelected())
						layout.show(geneSettingsValuePanel,GENE_SETTINGS_DOUBLE_RANGE_VALUE_PANEL);
					else if (intGeneValueRangeRButton.isSelected())
						layout.show(geneSettingsValuePanel,GENE_SETTINGS_INT_RANGE_VALUE_PANEL);
					else if (listGeneValueRButton.isSelected())
						layout.show(geneSettingsValuePanel,GENE_SETTINGS_LIST_VALUE_PANEL);
					else if (constantRButton.isSelected()) {
						final ParameterOrGene param = (ParameterOrGene) editedNode.getUserObject();
						if (param.getInfo() instanceof SubmodelInfo) 
							layout.show(geneSettingsValuePanel,PARAM_SETTINGS_SUBMODEL_VALUE_PANEL);
						else if (param.getInfo().isEnum() || param.getInfo() instanceof MasonChooserParameterInfo)
							layout.show(geneSettingsValuePanel,PARAM_SETTINGS_ENUM_VALUE_PANEL);
						else if (param.getInfo().isFile()) 
							layout.show(geneSettingsValuePanel,PARAM_SETTINGS_FILE_VALUE_PANEL);
						else
							layout.show(geneSettingsValuePanel,PARAM_SETTINGS_CONSTANT_VALUE_PANEL);
					}
				} else {
					resetSettings();
					enableDisableSettings(false);
				}
			}
		};
		constantRButton.addItemListener(itemListener);
		doubleGeneValueRangeRButton.addItemListener(itemListener);
		intGeneValueRangeRButton.addItemListener(itemListener);
		listGeneValueRButton.addItemListener(itemListener);
		
		final JPanel selectPanel = FormsUtils.build("f:p:g",
													"0||" +
													"1||" +
													"2||" +
													"3",
													constantRButton,
													doubleGeneValueRangeRButton,
													intGeneValueRangeRButton,
													listGeneValueRButton ).getPanel();
		Style.registerCssClasses(selectPanel,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel buttonsPanel = new JPanel();
		modifyButton = new JButton("Modify");
		modifyButton.setActionCommand("EDIT");
		cancelButton = new JButton("Cancel");
		cancelButton.setActionCommand("CANCEL");
		GUIUtils.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				final String cmd = e.getActionCommand();
				if ("EDIT".equals(cmd)) 
					edit();
				else if ("CANCEL".equals(cmd)) 
					cancel();
				else if ("CONST_FIELD".equals(cmd) || "INT_MAX".equals(cmd) || "D_MAX".equals(cmd))
					modifyButton.doClick();
				else if ("INT_MIN".equals(cmd))
					intMaxField.grabFocus();
				else if ("D_MIN".equals(cmd))
					doubleMaxField.grabFocus();
				else if ("FILE_BROWSE".equals(cmd)) 
					chooseFile();
			}
			
			private void edit() {
				if (editedNode != null)
					geneTree.setSelectionPath(null);
			}
			
			private void cancel() {
				resetSettings();
				enableDisableSettings(false);
				editedNode = null;
				geneTree.setSelectionPath(null);
			}
			
			private void chooseFile() {
				final JFileChooser fileDialog = new JFileChooser(!"".equals(fileTextField.getToolTipText()) ? fileTextField.getToolTipText() :
					parentPage.getCurrentDirectory());
				if (!"".equals(fileTextField.getToolTipText()))
					fileDialog.setSelectedFile(new File(fileTextField.getToolTipText()));
				int dialogResult = fileDialog.showOpenDialog(parentPage.getWizard());
				if (dialogResult == JFileChooser.APPROVE_OPTION) {
					final File selectedFile = fileDialog.getSelectedFile();
					if (selectedFile != null) {
						parentPage.setCurrentDirectory(selectedFile.getAbsoluteFile().getParent());
						fileTextField.setText(selectedFile.getName());
						fileTextField.setToolTipText(selectedFile.getAbsolutePath());
					}
				}
			}
		},modifyButton,cancelButton,constantField,fileTextField,intMinField,intMaxField,doubleMinField,doubleMaxField,fileBrowseButton);
		buttonsPanel.add(modifyButton);
		buttonsPanel.add(cancelButton);
		
		final JPanel geneSetterPanel = FormsUtils.build("f:p:g",
														"0||" +
														"1 f:p:g||" +
														"2 p",
														selectPanel,
														geneSettingsValuePanel,
														buttonsPanel).getPanel();
		
		geneSetterPanel.setBorder(BorderFactory.createTitledBorder(null,"Gene/parameter settings",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(geneSetterPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel rightTop = FormsUtils.build("f:p:g p",
												 "01 f:p:g",
												 treePanel,geneSetterPanel).getPanel();
		rightTop.setBorder(BorderFactory.createTitledBorder(null,"Chromosome",TitledBorder.LEADING,TitledBorder.BELOW_TOP));
		Style.registerCssClasses(rightTop,Dashboard.CSS_CLASS_COMMON_PANEL);
		
		final JPanel mainPanel = FormsUtils.build("p f:p:g", 
				"01 f:p:g||" +
				"02 p",
				gaSettingsPanel,rightTop,
				operatorDetailsPanel).getPanel();
		mainPanel.setBorder(BorderFactory.createTitledBorder(""));
		Style.registerCssClasses(mainPanel, Dashboard.CSS_CLASS_COMMON_PANEL);
		
		
		setLayout(new BorderLayout());
		mainScrPane = new JScrollPane(mainPanel);
		mainScrPane.setBorder(null);
		mainScrPane.setViewportBorder(null);
		add(mainScrPane, BorderLayout.CENTER);
		
		resetSettings();
		enableDisableSettings(false);
	}

	private void initializeStatistics(final GASearchPanelModel model) {
		fitnessStatistics = new JComboBox(model.getFitnessStatistics().toArray(new Object[0]));
		fitnessStatistics.setRenderer(new DefaultListCellRenderer() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
				super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
				IStatisticsPlugin plugin = (IStatisticsPlugin) value;
				
				this.setText(plugin.getName());
				this.setToolTipText(plugin.getDescription());
				
				return this;
			}
		});
		fitnessStatistics.setSelectedItem(model.getSelectedFitnessStatistics());
		fitnessStatistics.addItemListener(new ItemListener() {
			
			public void itemStateChanged(final ItemEvent e) {
				if (ItemEvent.SELECTED == e.getStateChange())
					model.setSelectedFitnessStatistics((IStatisticsPlugin) e.getItem());
			}
		});

	}
	
	//----------------------------------------------------------------------------------------------------
	@SuppressWarnings("unused")
    private JCheckBox createOperatorCheckBox(final JPanel operatorSettingsPanel, final JPanel operatorDescriptionPanel, final IGASelectorConfigurator operator, final GASearchPanelModel model) {
		JCheckBox checkBox = new JCheckBox(operator.getName());
		operatorSettingsPanel.add(operator.getSettingspanel(), operator.getName());
		JXLabel descriptionLabel = new JXLabel(operator.getDescription());
		descriptionLabel.setLineWrap(true);
		descriptionLabel.setVerticalAlignment(SwingConstants.TOP);
		descriptionLabel.setPreferredSize(new Dimension(200, 200));
		operatorDescriptionPanel.add(descriptionLabel, operator.getName());
		
		checkBox.addItemListener(new ItemListener() {
			
			@Override
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED){
					CardLayout settingslayout = (CardLayout) operatorSettingsPanel.getLayout();
					settingslayout.show(operatorSettingsPanel, operator.getName());
					((TitledBorder)operatorSettingsPanel.getBorder()).setTitle(operator.getName());
					operatorSettingsPanel.repaint();
					
					CardLayout descriptionLayout = (CardLayout) operatorDescriptionPanel.getLayout();
					descriptionLayout.show(operatorDescriptionPanel, operator.getName());
					
//					model.setSelectedOperator(operator);
				} else {
					CardLayout settingslayout = (CardLayout) operatorSettingsPanel.getLayout();
					settingslayout.show(operatorSettingsPanel, OPERATOR_SETTINGS_PANEL_DEFAULT_PANEL);
					((TitledBorder)operatorSettingsPanel.getBorder()).setTitle(OPERATOR_SETTINGS_PANEL_TITLE_POSTFIX);
					operatorSettingsPanel.repaint();

					CardLayout descriptionLayout = (CardLayout) operatorDescriptionPanel.getLayout();
					descriptionLayout.show(operatorDescriptionPanel, OPERATOR_SETTINGS_PANEL_DEFAULT_PANEL);
					
//					model.removeSelectedOperator(operator);
				}
			}
		});
		
		checkBox.addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {
				CardLayout settingslayout = (CardLayout) operatorSettingsPanel.getLayout();
				settingslayout.show(operatorSettingsPanel, operator.getName());
				((TitledBorder)operatorSettingsPanel.getBorder()).setTitle(operator.getName());
				operatorSettingsPanel.repaint();
				
				CardLayout descriptionLayout = (CardLayout) operatorDescriptionPanel.getLayout();
				descriptionLayout.show(operatorDescriptionPanel, operator.getName());				
			}
		});
		
		return checkBox;
	}
	
	//----------------------------------------------------------------------------------------------------
	private JScrollPane initDescriptionPanel() {
		descriptionLabel = new JXLabel("");
		descriptionLabel.setLineWrap(true);
		descriptionLabel.setVerticalAlignment(SwingConstants.TOP);
		
		JScrollPane scrollPane = new JScrollPane(descriptionLabel);
		Style.registerCssClasses(scrollPane, ".borderless");
		scrollPane.getViewportBorder();
		scrollPane.setViewportBorder(null);
		scrollPane.setPreferredSize(new Dimension(200, 100));
		
		return scrollPane;
	}

	//----------------------------------------------------------------------------------------------------
	protected void showOperatorDetails(final JPanel operatorSettingsPanel, final JPanel operatorDescriptionPanel, final IGAConfigurator operator) {
		if (operator != null) {
			CardLayout settingslayout = (CardLayout) operatorSettingsPanel.getLayout();
			settingslayout.show(operatorSettingsPanel, operator.getName());
			((TitledBorder)operatorSettingsPanel.getBorder()).setTitle(operator.getName());
			operatorSettingsPanel.repaint();
			descriptionLabel.setText(operator.getDescription());
		} else
			descriptionLabel.setText("");

	}
	
	//-------------------------------------------------------------------------------------------------
	/**
	 * Utility method that returns the string representation of a double value (rounded to 2 decimal
	 * digits using US format) 
	 */
	private static String double2String(final double d) {
		BigDecimal bd = new BigDecimal(d);
		bd = bd.setScale(2,BigDecimal.ROUND_HALF_UP);
		return numberFormatter.format(bd);
	}
	
	//------------------------------------------------------------------------------
	/** Enables/disables the parameter editing components of the page according to
	 *  the value of <code>enabled</code>.
	 */
	private void enableDisableSettings(final boolean enabled) {
		constantRButton.setEnabled(enabled);
		intGeneValueRangeRButton.setEnabled(enabled);
		doubleGeneValueRangeRButton.setEnabled(enabled);
		listGeneValueRButton.setEnabled(enabled);
		constantField.setEnabled(enabled);
		enumDefBox.setEnabled(enabled);
		submodelTypeBox.setEnabled(enabled);
		fileTextField.setEnabled(enabled);
		fileBrowseButton.setEnabled(enabled);
		intMinField.setEnabled(enabled);
		intMaxField.setEnabled(enabled);
		doubleMinField.setEnabled(enabled);
		doubleMaxField.setEnabled(enabled);
		valuesArea.setEnabled(enabled);
		modifyButton.setEnabled(enabled);
		cancelButton.setEnabled(enabled);
	}

	//------------------------------------------------------------------------------
	/** Resets the parameter editing related components. */
	private void resetSettings() { 
		constantRButton.setSelected(false);
		intGeneValueRangeRButton.setSelected(false);
		doubleGeneValueRangeRButton.setSelected(false);;
		listGeneValueRButton.setSelected(false);;
		constantField.setText("");
		fileTextField.setText("");
		intMinField.setText("");
		intMaxField.setText("");
		doubleMinField.setText("");
		doubleMaxField.setText("");
		valuesArea.setText("");
		modifyButton.setEnabled(false);
		cancelButton.setEnabled(false);
		
		final CardLayout cl = (CardLayout) geneSettingsValuePanel.getLayout();
		cl.show(geneSettingsValuePanel,"NULL");
	}
	
	//----------------------------------------------------------------------------------------------------
	private void edit(final ParameterOrGene param) {
		enableDisableSettings(true);
		String view = PARAM_SETTINGS_CONSTANT_VALUE_PANEL;
		if (param.isGene()) {
			editGene(param.getGeneInfo());
			return;
		} else if (param.getInfo() instanceof SubmodelInfo) {
			final SubmodelInfo sInfo = (SubmodelInfo) param.getInfo();
			editSubmodelInfo(sInfo);
			return;
		} else if (param.getInfo().isEnum() || param.getInfo() instanceof MasonChooserParameterInfo) {
			view = PARAM_SETTINGS_ENUM_VALUE_PANEL;
			intGeneValueRangeRButton.setEnabled(false);
			doubleGeneValueRangeRButton.setEnabled(false);
			listGeneValueRButton.setEnabled(false);
			constantRButton.setSelected(true);
			
			final ParameterInfo info = param.getInfo();
			final Object selected = info.getValue();
			Object[] elements = null;
			if (info.isEnum()) {
				@SuppressWarnings("unchecked")
				final Class<Enum<?>> type = (Class<Enum<?>>) info.getJavaType();
				elements = type.getEnumConstants();
			} else {
				final MasonChooserParameterInfo chooserInfo = (MasonChooserParameterInfo) info;
				elements = chooserInfo.getValidStrings();
			}
			
			final DefaultComboBoxModel model = (DefaultComboBoxModel) enumDefBox.getModel();
			model.removeAllElements();
			for (final Object object : elements) 
				model.addElement(object);
				
			if (info.isEnum())
				enumDefBox.setSelectedItem(selected);
			else 
				enumDefBox.setSelectedIndex((Integer)selected);
		} else if (param.getInfo().isFile()) {
			view = PARAM_SETTINGS_FILE_VALUE_PANEL;
			constantRButton.setSelected(true);
			final File file = (File) param.getInfo().getValue();
			if (file != null) {
				fileTextField.setText(file.getName());
				fileTextField.setToolTipText(file.getAbsolutePath());
			}
		} else {
			constantRButton.setSelected(true);
			constantField.setText(param.getInfo().valuesToString());
		}
		
		final CardLayout cl = (CardLayout) geneSettingsValuePanel.getLayout();
		cl.show(geneSettingsValuePanel,view);
		if (!param.getInfo().isNumeric()) {
			intGeneValueRangeRButton.setEnabled(false);
			doubleGeneValueRangeRButton.setEnabled(false);
			listGeneValueRButton.setEnabled(false);
		}
	}

	//----------------------------------------------------------------------------------------------------
	private void editSubmodelInfo(final SubmodelInfo sInfo) {
		constantRButton.setSelected(true);
		intGeneValueRangeRButton.setEnabled(false);
		doubleGeneValueRangeRButton.setEnabled(false);
		listGeneValueRButton.setEnabled(false);
		final Class<?> actualType = sInfo.getActualType();
		final DefaultComboBoxModel model = (DefaultComboBoxModel) submodelTypeBox.getModel();
		model.removeAllElements();
		model.addElement("Loading class information...");
		final CardLayout cl = (CardLayout) geneSettingsValuePanel.getLayout();
		cl.show(geneSettingsValuePanel,PARAM_SETTINGS_SUBMODEL_VALUE_PANEL);
		
		try {
			final ClassElement[] elements = parentPage.fetchPossibleTypes(sInfo);
			model.removeAllElements();
			for (final ClassElement classElement : elements) 
				model.addElement(classElement);
			
			if (actualType != null)
				submodelTypeBox.setSelectedItem(new ClassElement(actualType, sInfo.getInstance()));
			submodelTypeBox.setEnabled(true);
		} catch (final ComboBoxIsTooFullException e) {
			String errorMsg = "Too much possibilities for " + sInfo.getName() + ". Please narrow down the domain by " + 
					  		  "defining the possible classes in the @Submodel annotation.";
			JOptionPane.showMessageDialog(parentPage.getWizard(),new JLabel(errorMsg),"Error while analyizing model",JOptionPane.ERROR_MESSAGE);
			
			resetSettings();
			enableDisableSettings(false);
			editedNode = null;
		}
	}
	
	//----------------------------------------------------------------------------------------------------
	private void editGene(final GeneInfo geneInfo) {
		String view = null;
		if (GeneInfo.LIST.equals(geneInfo.getValueType())) {
			view = GENE_SETTINGS_LIST_VALUE_PANEL;
			listGeneValueRButton.setSelected(true);
			valuesArea.setText(Utils.join(geneInfo.getValueRange()," "));
		} else {
			if (geneInfo.isIntegerVals()) {
				view = GENE_SETTINGS_INT_RANGE_VALUE_PANEL;
				intGeneValueRangeRButton.setSelected(true);
				intMinField.setText(String.valueOf(geneInfo.getMinValue()));
				intMaxField.setText(String.valueOf(geneInfo.getMaxValue()));
			} else {
				view = GENE_SETTINGS_DOUBLE_RANGE_VALUE_PANEL;
				doubleGeneValueRangeRButton.setSelected(true);
				doubleMinField.setText(String.valueOf(geneInfo.getMinValue()));
				doubleMaxField.setText(String.valueOf(geneInfo.getMaxValue()));
			}
		}
		
		final CardLayout cl = (CardLayout) geneSettingsValuePanel.getLayout();
		cl.show(geneSettingsValuePanel,view);
	}
	
	//----------------------------------------------------------------------------------------------------
	private String[] checkInput(final ParameterOrGene param) {
		final List<String> errors = new ArrayList<String>();
		
		if (constantRButton.isSelected()) {
			final ParameterInfo info = param.getInfo();
			
			if (info.isEnum() || info instanceof MasonChooserParameterInfo) { 
				// well, the combo box must contain a valid value
			} else if (info instanceof SubmodelInfo) {
				final Object selectedObject = submodelTypeBox.getSelectedItem();
				if (!(selectedObject instanceof ClassElement))
					errors.add("Invalid type is selected for parameter " + info.getName());
				else {
					final ClassElement selectedElement = (ClassElement) selectedObject;
					if (selectedElement.clazz == null)
						errors.add("'Constant value' cannot be empty. Please select a type.");
				}
			} else if (info.isFile()) {
				if (fileTextField.getText().trim().isEmpty())
					log.warn("Empty string was specified as file parameter " + info.getName().replaceAll("([A-Z])"," $1"));

				final File file = new File(fileTextField.getToolTipText());
				if (!file.exists()) 
					errors.add("File " + fileTextField.getToolTipText() + " does not exist.");
			} else if (constantField.getText().trim().equals(""))
				errors.add("'Constant value' cannot be empty.");
			else {
				final boolean valid = ParameterInfo.isValid(constantField.getText().trim(),info.getType());
				if (!valid) 
					errors.add("'Constant value' must be a valid " + parentPage.getTypeText(info.getType()) + ".");
				
				errors.addAll(PlatformSettings.additionalParameterCheck(info,new String[] { constantField.getText().trim() },ParameterInfo.CONST_DEF));
			}
		} else if (intGeneValueRangeRButton.isSelected()) {
			final String minFieldText = intMinField.getText();
			final String maxFieldText = intMaxField.getText();
			Long minValue = null;
			Long maxValue = null;
			
			if (minFieldText == null || minFieldText.trim().isEmpty()) 
				errors.add("'Minimum value' cannot be empty.");
			else {
				try {
					minValue = new Long(minFieldText.trim());
					
					final boolean valid = ParameterInfo.isValid(minFieldText,param.getInfo().getType());
					if (!valid) 
						errors.add("'Minimum value' must be a valid " + parentPage.getTypeText(param.getInfo().getType()) + ".");
					
					errors.addAll(PlatformSettings.additionalParameterCheck(param.getInfo(),new String[] { minFieldText.trim() },ParameterInfo.CONST_DEF));
				} catch (final NumberFormatException _) {
					errors.add("'Minimum value' must be a valid integer number.");
				}
			}
			
			if (maxFieldText == null || maxFieldText.trim().isEmpty()) 
				errors.add("'Maximum value' cannot be empty.");
			else {
				try {
					maxValue = new Long(maxFieldText.trim());
					
					final boolean valid = ParameterInfo.isValid(maxFieldText,param.getInfo().getType());
					if (!valid) 
						errors.add("'Maximum value' must be a valid " + parentPage.getTypeText(param.getInfo().getType()) + ".");
					
					errors.addAll(PlatformSettings.additionalParameterCheck(param.getInfo(),new String[] { maxFieldText.trim() },ParameterInfo.CONST_DEF));
				} catch (final NumberFormatException _) {
					errors.add("'Maximum value' must be a valid integer number.");
				}
			}
			
			if (minValue != null && maxValue != null) {
				if (minValue.longValue() > maxValue.longValue())
					errors.add("Please specify a non-empty interval!");
			}
		} else if (doubleGeneValueRangeRButton.isSelected()) {
			final String minFieldText = doubleMinField.getText();
			final String maxFieldText = doubleMaxField.getText();
			Double minValue = null;
			Double maxValue = null;
			
			if (minFieldText == null || minFieldText.trim().isEmpty()) 
				errors.add("'Minimum value' cannot be empty.");
			else {
				try {
					minValue = new Double(minFieldText.trim());
					
					final boolean valid = ParameterInfo.isValid(minFieldText,param.getInfo().getType());
					if (!valid) 
						errors.add("'Minimum value' must be a valid " + parentPage.getTypeText(param.getInfo().getType()) + ".");
					
					errors.addAll(PlatformSettings.additionalParameterCheck(param.getInfo(),new String[] { minFieldText.trim() },ParameterInfo.CONST_DEF));
				} catch (final NumberFormatException _) {
					errors.add("'Minimum value' must be a valid number.");
				}
			}
			
			if (maxFieldText == null || maxFieldText.trim().isEmpty()) 
				errors.add("'Maximum value' cannot be empty.");
			else {
				try {
					maxValue = new Double(maxFieldText.trim());
					
					final boolean valid = ParameterInfo.isValid(maxFieldText,param.getInfo().getType());
					if (!valid) 
						errors.add("'Maximum value' must be a valid " + parentPage.getTypeText(param.getInfo().getType()) + ".");
					
					errors.addAll(PlatformSettings.additionalParameterCheck(param.getInfo(),new String[] { maxFieldText.trim() },ParameterInfo.CONST_DEF));
				} catch (final NumberFormatException _) {
					errors.add("'Maximum value' must be a valid number.");
				}
			}
			
			if (minValue != null && maxValue != null) {
				if (minValue.doubleValue() > maxValue.doubleValue())
					errors.add("Please specify a non-empty interval!");
			}

		} else if (listGeneValueRButton.isSelected()) {
			if (valuesArea.getText() == null || valuesArea.getText().trim().isEmpty())
				errors.add("'Value list' cannot be empty.");
			else {
				try {
					final List<Object> valueList = new ArrayList<Object>();
					String values = valuesArea.getText().trim();
					values = values.replaceAll("\\s+"," ");
					final StringTokenizer tokenizer = new StringTokenizer(values, " ");
					while (tokenizer.hasMoreTokens()) {
						final String strValue = tokenizer.nextToken();
						
						final boolean valid = ParameterInfo.isValid(strValue,param.getInfo().getType());
						if (!valid) 
							errors.add("'" + strValue + "' is not a valid " + parentPage.getTypeText(param.getInfo().getType()) + ".");
						
						valueList.add(parseListElement(param.getInfo().getJavaType(),strValue));
					}
					
					errors.addAll(PlatformSettings.additionalParameterCheck(param.getInfo(),values.split(" "),ParameterInfo.LIST_DEF));
				} catch (final NumberFormatException _) {
					errors.add("All element of the value list must be a valid number.");
				}
			}
		}
		
		return errors.isEmpty() ? null : errors.toArray(new String[0]);
	}
	
	//----------------------------------------------------------------------------------------------------
	private Object parseListElement(final Class<?> type, final String strValue) {
		if (type.equals(Double.class) || type.equals(Float.class) || type.equals(Double.TYPE) || type.equals(Float.TYPE))
			return Double.parseDouble(strValue);
		
		return Long.parseLong(strValue);
	}
	
	//----------------------------------------------------------------------------------------------------
	private boolean modify() {
		final ParameterOrGene userObj = (ParameterOrGene) editedNode.getUserObject();
		
		final String[] errors = checkInput(userObj);
		if (errors != null && errors.length != 0) {
			Utilities.userAlert(mainScrPane,(Object)errors);
			return false;
		} else {
			modifyParameterOrGene(userObj);
			resetSettings();
			
			if (!(userObj.info instanceof SubmodelInfo)) {
				final DefaultTreeModel model = (DefaultTreeModel) geneTree.getModel();
				model.nodeStructureChanged(editedNode);
			}
			
			editedNode = null;
			
			updateNumberOfGenes();
			enableDisableSettings(false);
			return true;
		}
	}
	
	//----------------------------------------------------------------------------------------------------
	public boolean closeActiveModification() {
		return editedNode != null ? modify() : true;
	}
	
	//------------------------------------------------------------------------------
	private void modifyParameterOrGene(final ParameterOrGene param) {
		if (constantRButton.isSelected()) {
			final ParameterInfo info = param.getInfo();
			if (info.isEnum()) 
				param.setConstant(enumDefBox.getSelectedItem());
			else if (info instanceof MasonChooserParameterInfo) 
				param.setConstant(enumDefBox.getSelectedIndex());				
			else if (info instanceof SubmodelInfo) {
				final SubmodelInfo sInfo = (SubmodelInfo) info;
				modifySubmodelParameterInfo(sInfo);
			} else if (info.isFile()) 
				param.setConstant(ParameterInfo.getValue(fileTextField.getToolTipText(),info.getType()));
			else
				param.setConstant(ParameterInfo.getValue(constantField.getText().trim(),info.getType()));
		} else if (intGeneValueRangeRButton.isSelected()) {
			final Long minValue = new Long(intMinField.getText().trim());
			final Long maxValue = new Long(intMaxField.getText().trim());
			param.setGene(minValue,maxValue);
		} else if (doubleGeneValueRangeRButton.isSelected()) {
			final Double minValue = new Double(doubleMinField.getText().trim());
			final Double maxValue = new Double(doubleMaxField.getText().trim());
			param.setGene(minValue,maxValue);
		} else if (listGeneValueRButton.isSelected()) {
			final List<Object> valueList = new ArrayList<Object>();
			String values = valuesArea.getText().trim();
			values = values.replaceAll("\\s+"," ");
			final StringTokenizer tokenizer = new StringTokenizer(values, " ");
			while (tokenizer.hasMoreTokens()) 
				valueList.add(parseListElement(param.getInfo().getJavaType(),tokenizer.nextToken()));
			param.setGene(valueList);
		}
	}

	//----------------------------------------------------------------------------------------------------
	private void modifySubmodelParameterInfo(final SubmodelInfo sInfo) {
		final Object selectedItem = submodelTypeBox.getSelectedItem();
		if (selectedItem instanceof ClassElement) {
			ClassElement aClassElement = (ClassElement)selectedItem;
			final Class<?> newActualType = aClassElement.clazz;
			
			if (!parentPage.classEquals(newActualType,sInfo.getActualType())) {
				sInfo.setActualType(newActualType, aClassElement.instance);
				addSubParametersToTree(sInfo, editedNode);
			} // else do nothing
		}
	}
	
	//----------------------------------------------------------------------------------------------------
	private void addSubParametersToTree(final SubmodelInfo sInfo, final DefaultMutableTreeNode node) { 
		try {
			final DefaultTreeModel treeModel = (DefaultTreeModel) geneTree.getModel();

			model.addSubmodelParameter(sInfo, node);

			geneTree.expandPath(new TreePath(treeModel.getPathToRoot(node)));
		} catch (final ModelInformationException e) {
			sInfo.setActualType(null, null);
			JOptionPane.showMessageDialog(parentPage.getWizard(),new JLabel(e.getMessage()), "Error while analyizing model", JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
			submodelTypeBox.setSelectedIndex(0);
		}
	}
	
	//----------------------------------------------------------------------------------------------------
	private void updateNumberOfGenes() {
		int count = 0;
		
		final DefaultTreeModel treeModel = (DefaultTreeModel) geneTree.getModel();
		final DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
		@SuppressWarnings("rawtypes")
		final Enumeration nodes = root.breadthFirstEnumeration();
		nodes.nextElement();
		
		while (nodes.hasMoreElements()) {
			final DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes.nextElement();
			final ParameterOrGene userObj = (ParameterOrGene) node.getUserObject();
			if (userObj.isGene())
				count++;
		}
		
		numberLabel.setText("<html><b>Number of genes:</b> " + count + "</html>");
	}
}