/*
 * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
 */
package ch.psi.plot;

import ch.psi.plot.LinePlotSeries.LinePlotSeriesListener;
import ch.psi.plot.utils.IO;
import ch.psi.plot.utils.SwingUtils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

/**
 * TODO: FDA calls to .update -> requestUpdate()
 */
abstract public class LinePlotBase extends PlotBase<LinePlotSeries> implements LinePlot {

    private String xAxisLabel = "X";
    private String yAxisLabel = "Y";

    protected LinePlotBase() {
        this(null);
    }

    protected LinePlotBase(String title) {
        super(LinePlotSeries.class, title);
        createAxis(AxisId.X, "X");
        createAxis(AxisId.Y, "Y");

    }

    //TODO: Improve it to share the same X rows
    @Override
    public void saveData(String filename) throws IOException {
        StringBuilder str = new StringBuilder(1024);

        int numberSeries = getAllSeries().length;

        str.append(getAxis(AxisId.X).getLabel().isEmpty() ? "X" : getAxis(AxisId.X).getLabel()).append(FIELD_SEPARATOR);
        for (LinePlotSeries series : getAllSeries()) {
            str.append(series.getName()).append(FIELD_SEPARATOR);
        }
        str.append(LINE_SEPARATOR);

        int seriesIndex = 0;
        for (LinePlotSeries series : getAllSeries()) {
            double[][] data = getSeriesData(series);
            for (int i = 0; i < data[0].length; i++) {
                double x = data[0][i];
                double y = data[1][i];
                str.append(String.format("%1.6f", x)).append(FIELD_SEPARATOR);
                for (int j = 0; j < seriesIndex; j++) {
                    str.append(String.format("NaN", x)).append(FIELD_SEPARATOR);
                }
                str.append(String.format("%1.6f", y)).append(FIELD_SEPARATOR);
                for (int j = seriesIndex + 1; j < numberSeries; j++) {
                    str.append(String.format("NaN", x)).append(FIELD_SEPARATOR);
                }
                str.append(LINE_SEPARATOR);
            }
            seriesIndex++;
        }
        IO.writeStringToFile(filename, str.toString());
    }

    final LinePlotSeriesListener seriesListener = new LinePlotSeriesListener() {
        @Override
        public void onSeriesAppendData(LinePlotSeries series, double x, double y) {
            onAppendData(series, x, y);
            if (getRequireUpdateOnAppend() && isUpdatesEnabled()) {
                requestSeriesUpdate(series);
            }
        }

        @Override
        public void onSeriesSetData(LinePlotSeries series, double[] x, double[] y) {
            if (y == null) {
                y = new double[0];
            }
            if ((x == null) || (y.length != x.length)) {
                x = new double[y.length];
                for (int i = 0; i < x.length; i++) {
                    x[i] = i;
                }
            }
            onSetData(series, x, y);
            if (isUpdatesEnabled()) {
                requestSeriesUpdate(series);
            }
        }
    };

    /**
     * Returns a token to reference underlying data from LinePlotSeries
     */
    abstract protected void onAppendData(LinePlotSeries series, double x, double y);

    abstract protected void onSetData(LinePlotSeries series, double[] x, double[] y);

    //Injecting specific entries in popup menu
    @Override
    protected void onCreatedPanel() {
        addPopupMenuItem(null);//Separator
        // Detach plot into a separate frame
        JMenu detachPlotMenu = new JMenu("Detach");
        String[] implementations = getImplementations();
        JMenuItem[] detachPlotMenuItems = new JMenuItem[implementations.length];
        
        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    LinePlot p = newPlot(e.getActionCommand());
                    p.setTitle(getTitle());
                    p.getAxis(AxisId.X).setLabel(getAxis(AxisId.X).getLabel());
                    p.getAxis(AxisId.Y).setLabel(getAxis(AxisId.Y).getLabel());
                    for (LinePlotSeries series : getAllSeries()) {
                        LinePlotSeries detachedSeries = new LinePlotSeries(series.getName(), series.getColor());
                        p.addSeries(detachedSeries);
                        detachedSeries.setData(series.getX(), series.getY());
                    }

                    JFrame frame = new JFrame(getTitle());
                    frame.setContentPane(p.getChartPanel());
                    frame.pack();
                    SwingUtils.centerOnScreen(frame);
                    frame.setVisible(true);
                } catch (Exception ex) {
                    SwingUtils.showException(getChartPanel(), ex);
                }
            }
        };
        
        for (int i=0;i<implementations.length;i++){
            detachPlotMenuItems[i] = new JMenuItem(implementations[i]);
            detachPlotMenuItems[i].addActionListener(listener);
            detachPlotMenu.add(detachPlotMenuItems[i]);
        }
        addPopupMenuItem(detachPlotMenu);
    }
    
    // Known Implemenmtations
    //TODO: Should add reflection & dynamic including classes? 
    public static LinePlot newPlot(String impl) throws ClassNotFoundException, InstantiationException, IllegalAccessException{    
        return (LinePlot) (Class.forName( "ch.psi.plot." + impl + ".LinePlot")).newInstance();
    }

    public static String[] getImplementations(){    
        return new String[]{"jfree", "jlchart", "javafx"};
    }
    
}
