package org.lucci.madhoc.simulation.monitor;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.lucci.madhoc.gui.MonitorView;
import org.lucci.madhoc.simulation.Monitor;
import org.lucci.madhoc.simulation.measure.MeasureHistory;
import org.lucci.madhoc.simulation.measure.NumericalSensor;
import org.lucci.math.Utilities;
import org.lucci.up.GNUPlotFilePlotter;
import org.lucci.up.InteractiveSwingPlotter;
import org.lucci.up.SwingPlotter;
import org.lucci.up.data.Figure;
import org.lucci.up.data.FigureFactory;
import org.lucci.up.data.rendering.DataElementRenderer;
import org.lucci.up.data.rendering.figure.BSplineFigureRenderer;
import org.lucci.up.data.rendering.figure.BezierCurveFigureRenderer;
import org.lucci.up.data.rendering.figure.ConnectedLineFigureRenderer;
import org.lucci.up.data.rendering.point.CirclePointRenderer;
import org.lucci.up.data.rendering.point.HistogramPointRenderer;
import org.lucci.up.system.Space;

public class NumericalMeasures2DGraphicalView extends MonitorView
{
    private NumericalSensor xSensor, ySensor;

    private SwingPlotter plotter = new InteractiveSwingPlotter();
    private JCheckBox sortXCheckBox = new JCheckBox("Sort on X values", true);
    private JList rendererList = new JList();
    private ChangeListener changeListener = new ChangeHandler();
    private JCheckBox selectXCheckbox;
    private JCheckBox selectYCheckbox;
    private JButton invertXY;
    private JComboBox whatToShowComboBox;
    private JTextField intervalSizeTextField;

    
    public void setMonitor(Monitor application)
    {
        super.setMonitor(application);

        Space space = plotter.getGraphics2DPlotter().getSpace();
        space.setColor(Color.black);
        space.setBackgroundColor(Color.white);
        space.setArrowsVisible(false);
        space.getLegend().setVisible(false);
        space.getXDimension().getOriginAxis().getGraduation().setVisible(false);
        space.getYDimension().getOriginAxis().getGraduation().setVisible(false);
        space.getXDimension().getOriginAxis().setColor(Color.gray);
        space.getYDimension().getOriginAxis().setColor(Color.gray);
        plotter.setImageBufferedUsed(false);
        plotter.getGraphics2DPlotter().setFigure(new Figure());

        setLayout(new BorderLayout());      
        add(BorderLayout.CENTER, plotter);
        add(BorderLayout.EAST, new JScrollPane(createConfPanel()));

    }

    private JPanel createConfPanel()
    {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBorder(new TitledBorder("Parameters"));
        
        {
            JPanel xyPanel = new JPanel(new GridLayout(1, 2));
            selectXCheckbox = new JCheckBox("X", true);
            selectXCheckbox.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            selectYCheckbox.setSelected(!selectYCheckbox.isSelected());
                        }
                    });
            selectYCheckbox = new JCheckBox("Y", false);
            selectYCheckbox.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            selectXCheckbox.setSelected(!selectXCheckbox.isSelected());
                        }
                    });
            xyPanel.add(selectXCheckbox);
            xyPanel.add(selectYCheckbox);
            GridBagConstraints c = new GridBagConstraints();
            c.anchor = GridBagConstraints.CENTER;
            c.gridy = 0;
            panel.add(xyPanel, c);
        }

        {
            invertXY = new JButton("Swap X/Y");
            invertXY.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            NumericalSensor tmp = xSensor;
                            xSensor = ySensor;
                            ySensor = tmp;
                            updateViewContent();
                        }
                    });
            GridBagConstraints c = new GridBagConstraints();
            c.anchor = GridBagConstraints.CENTER;
            c.gridy = 1;
            panel.add(invertXY, c);
        }

        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 2;
            c.insets = new Insets(10, 10, 10, 10);
            panel.add(sortXCheckBox, c);
            sortXCheckBox.addChangeListener(changeListener);
        }
        {
            CirclePointRenderer cp = new CirclePointRenderer();
            cp.setFillColor(null);
            
            rendererList.setListData(new Object[] {
                    new ConnectedLineFigureRenderer(),
                    new HistogramPointRenderer(),
                    new BezierCurveFigureRenderer(),
                    new BSplineFigureRenderer(),
                    cp,
            });

            rendererList.setSelectedIndices(new int[] {0});
            rendererList.setCellRenderer(new ListRenderer());
            rendererList.setBorder(new TitledBorder("Renderers"));
            rendererList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 3;
            c.insets = new Insets(10, 10, 10, 10);
            panel.add(rendererList, c);
            rendererList.addListSelectionListener(new ListSelectionListener()
                    {
                        /* (non-Javadoc)
                         * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
                         */
                        public void valueChanged(ListSelectionEvent arg0)
                        {
                            updateViewContent();
                        }
                    });
        }
        {
            whatToShowComboBox = new JComboBox();
            whatToShowComboBox.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            updateViewContent();
                        }
                    });
            whatToShowComboBox.addItem("All");
            whatToShowComboBox.addItem("Average");
            whatToShowComboBox.addItem("Standard deviation");
            whatToShowComboBox.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            intervalSizeTextField.setEnabled(!whatToShowComboBox.getSelectedItem().equals("All"));
                        }
                    });
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 4;
            c.insets = new Insets(10, 10, 0, 10);
            panel.add(whatToShowComboBox, c);
        }
        {
            intervalSizeTextField = new JTextField("10");
            intervalSizeTextField.addActionListener(new ActionListener()
                    {
                        public void actionPerformed(ActionEvent arg0)
                        {
                            updateViewContent();
                        }
                    });
            intervalSizeTextField.setEnabled(false);
            JPanel p = new JPanel(new GridLayout(1,2));
            p.add(new JLabel("Show last: "));
            p.add(intervalSizeTextField);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 5;
            c.insets = new Insets(0, 10, 10, 10);
            panel.add(p, c);
        }

        return panel;
    }


    public void saveState(File directory)
    {
        File file = new File(directory.getAbsolutePath() + "/" + getName() + ".dat");
        GNUPlotFilePlotter gnuPlotter = new GNUPlotFilePlotter();
        gnuPlotter.setGraphics2DPlotter(plotter.getGraphics2DPlotter());
        String text = gnuPlotter.getGNUplotData();

        try
        {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(text.getBytes());
            fos.close();
        }
        catch (IOException ex)
        {
            System.err.println("Error while writing " + file.getAbsolutePath());
        }
    }


    public String getName()
    {
        return "2D plotter";
    }


    /* (non-Javadoc)
     * @see org.lucci.madhoc.ui.ApplicationView#updateView()
     */
    public void updateViewContent()
    {
        if (xSensor != null)
        {
            String legend = org.lucci.madhoc.util.Utilities.getLegend(xSensor);
            
            if (legend != null)
            {
                plotter.getGraphics2DPlotter().getSpace().getXDimension().getLegend().setText(legend);
                plotter.getGraphics2DPlotter().getSpace().getXDimension().getLegend().setColor(xSensor.getMonitor().getColor());
            }
        }

        if (ySensor != null)
        {
            String legend = org.lucci.madhoc.util.Utilities.getLegend(ySensor);

            if (legend != null)
            {
                plotter.getGraphics2DPlotter().getSpace().getYDimension().getLegend().setText(legend);
                plotter.getGraphics2DPlotter().getSpace().getYDimension().getLegend().setColor(ySensor.getMonitor().getColor());
            }
        }

        if (xSensor != null && ySensor != null)
        {
            Figure fig = createFigure();
            Iterator rendererIterator = Arrays.asList(rendererList.getSelectedValues()).iterator();

            while (rendererIterator.hasNext())
            {
                DataElementRenderer renderer = (DataElementRenderer) rendererIterator.next();
                
                if (renderer instanceof CirclePointRenderer)
                {
                    CirclePointRenderer r = (CirclePointRenderer) renderer;
                    
                    if (fig.getPointCount() < 50)
                    {
                        r.setRadius(5);
                    }
                    else if (fig.getPointCount() < 100)
                    {
                        r.setRadius(4);
                    }
                    else if (fig.getPointCount() < 150)
                    {
                        r.setRadius(3);
                    }
                    else
                    {
                        r.setRadius(2);
                    }
                }
                
                renderer.setColor(ySensor.getMonitor().getColor());
                fig.addRenderer(renderer);
            }
            
            plotter.getGraphics2DPlotter().setFigure(fig);
        }
        else
        {
            plotter.getGraphics2DPlotter().setFigure(new Figure());
        }

        plotter.repaint(0);
    }
    

    /**
     * @return
     */
    private Figure createFigure()
    {
        MeasureHistory x = (MeasureHistory) getProjectionComponent().getProjection().getMeasureHistoryMap().get(xSensor);
        MeasureHistory y = (MeasureHistory) getProjectionComponent().getProjection().getMeasureHistoryMap().get(ySensor);

        List yValues = getYValues(y);
        List xValues = new Vector(x.getValues());
        
        if (xValues.size() != yValues.size())
        {
            xValues = xValues.subList(xValues.size() - yValues.size(), xValues.size());
        }
        
        Figure fig = FigureFactory.createFigure(xValues, yValues);

        if (sortXCheckBox.isSelected())
        {
            fig.sortX();
        }
        
        return fig;
    }

    private List getYValues(MeasureHistory yr)
    {
        String whatToShow = (String) whatToShowComboBox.getSelectedItem();
        
        if (whatToShow.equalsIgnoreCase("all"))
        {
            return yr.getValues();
        }
        else
        {
            List values = new Vector();
            
            try
            {
                Integer.valueOf(intervalSizeTextField.getText()).intValue();
            }
            catch (NumberFormatException ex)
            {
                intervalSizeTextField.setText("20");
            }
            
            int interval = Integer.valueOf(intervalSizeTextField.getText()).intValue();
            int iteration = getMonitor().getNetwork().getSimulation().getIteration();
            
            for (int i = 0; i < iteration; ++i)
            {
                if (i - interval >= 0)
                {
                    List subList = yr.getValues().subList(i - interval, i);

                    if (whatToShow.equalsIgnoreCase("average"))
                    {
                        values.add(new Double(Utilities.getAverage(subList)));
                    }
                    else if (whatToShow.equalsIgnoreCase("standard deviation"))
                    {
                        values.add(new Double(Utilities.getStandardDeviation(subList)));
                    }
                    else
                    {
                        throw new IllegalStateException();
                    }
                }
            }

            return values;
        }
    }
    
    
    
    public class ChangeHandler implements ChangeListener
    {

        /* (non-Javadoc)
         * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
         */
        public void stateChanged(ChangeEvent arg0)
        {
            updateViewContent();
        }
    }
        

    
    public class ListRenderer extends DefaultListCellRenderer
    {
        /* (non-Javadoc)
         * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
         */
        public Component getListCellRendererComponent(JList arg0, Object arg1, int arg2, boolean arg3, boolean arg4)
        {
            super.getListCellRendererComponent(arg0, arg1, arg2, arg3, arg4);
            setText(cleanRendererName(arg1.getClass().getName()));
            return this;
        }

        /**
         * @param name
         * @return
         */
        private String cleanRendererName(String name)
        {
            name = name.substring(name.lastIndexOf(".") + 1);
            
            if (name.endsWith("Renderer"))
            {
                name = name.substring(0, name.length() - "Renderer".length());
            }

            if (name.endsWith("Figure"))
            {
                name = name.substring(0, name.length() - "Figure".length());
            }
            if (name.endsWith("Point"))
            {
                name = name.substring(0, name.length() - "Point".length());
            }

            return name;
        }
    }
    public NumericalSensor getXMeasure()
    {
        return xSensor;
    }
    public void setXMeasure(NumericalSensor measure)
    {
        xSensor = measure;
    }
    public NumericalSensor getYMeasure()
    {
        return ySensor;
    }
    public void setYMeasure(NumericalSensor measure)
    {
        ySensor = measure;
    }
    public JCheckBox getSelectXCheckbox()
    {
        return selectXCheckbox;
    }
    public JCheckBox getSelectYCheckbox()
    {
        return selectYCheckbox;
    }

    public void configure() throws Throwable
    {
        // TODO Auto-generated method stub
        
    }
}