package kernel.ui;

import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.JScrollPane;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JSplitPane;

import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;

import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.util.Collection;
import java.util.List;

import kernel.WorldModelCreator;
import kernel.Perception;
import kernel.CommunicationModel;
import kernel.KernelStartupOptions;

import rescuecore2.config.Config;
import rescuecore2.components.Component;
import rescuecore2.misc.gui.ConfigTree;

/**
   A JPanel for displaying and editing kernel startup options.
*/
public class KernelStartupPanel extends JPanel {
    private static final String AUTO_SUFFIX = ".auto";

    /**
       Create a kernel launch GUI.
       @param config The system configuration.
       @param options The kernel startup options.
    */
    public KernelStartupPanel(Config config, final KernelStartupOptions options) {
        super(new BorderLayout());
        final JComboBox gis = createComboBox(options.getAvailableWorldModelCreators(), options.getWorldModelCreator());
        final JComboBox perception = createComboBox(options.getAvailablePerceptions(), options.getPerception());
        final JComboBox comms = createComboBox(options.getAvailableCommunicationModels(), options.getCommunicationModel());
        CheckboxPanel simulators = new CheckboxPanel(options.getAvailableSimulators(), options);
        CheckboxPanel viewers = new CheckboxPanel(options.getAvailableViewers(), options);
        SpinnerPanel agents = new SpinnerPanel(options.getAvailableAgents(), options);
        CheckboxPanel otherComponents = new CheckboxPanel(options.getAvailableComponents(), options);
        JScrollPane simulatorsScroll = new JScrollPane(simulators);
        simulatorsScroll.setBorder(BorderFactory.createTitledBorder("Simulators"));
        JScrollPane viewersScroll = new JScrollPane(viewers);
        viewersScroll.setBorder(BorderFactory.createTitledBorder("Viewers"));
        JScrollPane agentsScroll = new JScrollPane(agents);
        agentsScroll.setBorder(BorderFactory.createTitledBorder("Agents"));
        JScrollPane componentsScroll = new JScrollPane(otherComponents);
        componentsScroll.setBorder(BorderFactory.createTitledBorder("Other components"));
        ConfigTree configTree = new ConfigTree(config);
        JScrollPane configTreeScroll = new JScrollPane(configTree);
        configTreeScroll.setBorder(BorderFactory.createTitledBorder("Config"));

        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        JPanel optionsPanel = new JPanel(layout);
        JSplitPane top = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, optionsPanel, configTreeScroll);
        add(top, BorderLayout.CENTER);

        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.weightx = 0;
        c.weighty = 0;
        c.fill = GridBagConstraints.BOTH;
        c.anchor = GridBagConstraints.CENTER;
        JLabel l = new JLabel("GIS:");
        layout.setConstraints(l, c);
        optionsPanel.add(l);
        c.gridy = 1;
        l = new JLabel("Perception:");
        layout.setConstraints(l, c);
        optionsPanel.add(l);
        c.gridy = 2;
        l = new JLabel("Communication model:");
        layout.setConstraints(l, c);
        optionsPanel.add(l);
        c.gridy = 0;
        c.gridx = 1;
        c.weightx = 1;
        layout.setConstraints(gis, c);
        optionsPanel.add(gis);
        c.gridy = 1;
        layout.setConstraints(perception, c);
        optionsPanel.add(perception);
        c.gridy = 2;
        layout.setConstraints(comms, c);
        optionsPanel.add(comms);

        // Simulators, viewers, agents, other components
        c.gridx = 0;
        ++c.gridy;
        c.gridwidth = 2;
        c.weightx = 1;
        c.weighty = 1;
        layout.setConstraints(simulatorsScroll, c);
        optionsPanel.add(simulatorsScroll);
        ++c.gridy;
        layout.setConstraints(viewersScroll, c);
        optionsPanel.add(viewersScroll);
        ++c.gridy;
        layout.setConstraints(agentsScroll, c);
        optionsPanel.add(agentsScroll);
        ++c.gridy;
        layout.setConstraints(componentsScroll, c);
        optionsPanel.add(componentsScroll);

        // Event listeners
        gis.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    options.setWorldModelCreator((WorldModelCreator)gis.getSelectedItem());
                }
            });
        perception.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    options.setPerception((Perception)perception.getSelectedItem());
                }
            });
        comms.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    options.setCommunicationModel((CommunicationModel)comms.getSelectedItem());
                }
            });
    }

    private <T> JComboBox createComboBox(List<T> options, T selected) {
        Object[] choices = options.toArray();
        JComboBox result = new JComboBox(choices);
        result.setSelectedItem(selected);
        result.setEnabled(choices.length > 1);
        return result;
    }

    private static final class CheckboxPanel extends JPanel {
        private CheckboxPanel(Collection<? extends Component> available, final KernelStartupOptions options) {
            GridBagLayout layout = new GridBagLayout();
            GridBagConstraints c = new GridBagConstraints();
            c.gridx = 0;
            c.gridy = 0;
            c.weightx = 1;
            c.weighty = 1;
            c.gridwidth = 1;
            c.gridheight = 1;
            c.fill = GridBagConstraints.BOTH;
            c.anchor = GridBagConstraints.CENTER;
            setLayout(layout);
            for (Component t : available) {
                c.gridx = 0;
                c.weightx = 1;
                JLabel l = new JLabel(t.getName());
                layout.setConstraints(l, c);
                add(l);
                c.gridx = 1;
                c.weightx = 0;
                final JCheckBox check = new JCheckBox();
                check.setSelected(options.getInstanceCount(t) > 0);
                options.setInstanceCount(t, check.isSelected() ? 1 : 0);
                layout.setConstraints(check, c);
                add(check);
                ++c.gridy;
                final Component comp = t;
                check.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            options.setInstanceCount(comp, check.isSelected() ? 1 : 0);
                        }
                    });
            }
        }
    }

    private static final class SpinnerPanel extends JPanel {
        private SpinnerPanel(Collection<? extends Component> available, final KernelStartupOptions options) {
            GridBagLayout layout = new GridBagLayout();
            GridBagConstraints c = new GridBagConstraints();
            c.gridx = 0;
            c.gridy = 0;
            c.weightx = 1;
            c.weighty = 1;
            c.fill = GridBagConstraints.BOTH;
            c.anchor = GridBagConstraints.CENTER;
            setLayout(layout);
            for (Component t : available) {
                c.gridx = 0;
                c.weightx = 1;
                JLabel l = new JLabel(t.getName());
                layout.setConstraints(l, c);
                add(l);
                int count = options.getInstanceCount(t);
                boolean all = count == Integer.MAX_VALUE;
                final JSpinner spinner = new JSpinner(new SpinnerNumberModel(count == Integer.MAX_VALUE ? 0 : count, 0, Integer.MAX_VALUE, 1));
                final JCheckBox check = new JCheckBox("Maximum");
                check.setSelected(all);
                spinner.setEnabled(!all);
                final Component comp = t;
                check.addChangeListener(new ChangeListener() {
                        @Override
                        public void stateChanged(ChangeEvent e) {
                            spinner.setEnabled(!check.isSelected());
                            if (check.isSelected()) {
                                options.setInstanceCount(comp, Integer.MAX_VALUE);
                            }
                            else {
                                options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue());
                            }
                        }
                    });
                spinner.addChangeListener(new ChangeListener() {
                        @Override
                        public void stateChanged(ChangeEvent e) {
                            options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue());
                        }
                    });
                c.gridx = 1;
                c.weightx = 0;
                layout.setConstraints(spinner, c);
                add(spinner);
                c.gridx = 2;
                layout.setConstraints(check, c);
                add(check);
                ++c.gridy;
            }
        }
    }
}