/**
 * Copyright 2015 Peti Koch, All rights reserved.
 *
 * Project Info:
 * https://github.com/Petikoch/feedback_control_bookexamples_in_java
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ch.petikoch.examples.feedbackControlInJava.ui.plotting;

import ch.petikoch.examples.feedbackControlInJava.simulationFramework.plotting.TimeMonitoringPlotItem;
import ch.petikoch.examples.feedbackControlInJava.simulationFramework.plotting.TimeSetpointActualPlotItem;
import ch.petikoch.examples.feedbackControlInJava.ui.util.SwingUtils;
import com.google.common.collect.Lists;
import net.jcip.annotations.ThreadSafe;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

@ThreadSafe
public class JFreeChartPlotter {

    private XYSeriesCollection setpointActualDataset;
    private XYSeriesCollection monitoringDataset;

    public JFreeChartPlotter(String chartTitle, String monitoringSeriesName) {
        try {
            SwingUtils.executeBlockingOnEdt(() -> {
                JPanelDisplayer.clearDisplay();
                setpointActualDataset = createSetpointActualDataset();
                monitoringDataset = createMonitoringDataset(monitoringSeriesName);
                JFreeChart chart = createChart(
                        Lists.newArrayList(setpointActualDataset, monitoringDataset),
                        chartTitle);
                JPanel panel = createPanel(chart);
                JPanelDisplayer.displayPanel(panel);
            });
        } catch (InterruptedException | InvocationTargetException ex) {
            SwingUtils.printStackTraceAndDisplayToUser(ex);
        }
    }

    private XYSeriesCollection createSetpointActualDataset() {
        XYSeriesCollection dataset = new XYSeriesCollection();
        XYSeries setpointSeries = new XYSeries("Setpoint");
        XYSeries actualSeries = new XYSeries("Actual");
        dataset.addSeries(setpointSeries);
        dataset.addSeries(actualSeries);
        return dataset;
    }

    private XYSeriesCollection createMonitoringDataset(final String monitoringSeriesName) {
        XYSeriesCollection dataset = new XYSeriesCollection();
        XYSeries monitoringSeries = new XYSeries(monitoringSeriesName);
        dataset.addSeries(monitoringSeries);
        return dataset;
    }

    private JFreeChart createChart(List<XYDataset> datasets,
                                   String chartTitle) {
        JFreeChart chart = ChartFactory.createXYLineChart(
                chartTitle,
                "Time steps",
                chartTitle,
                datasets.get(0),
                PlotOrientation.VERTICAL,
                true,                     // include legend
                true,                     // tooltips
                false                     // urls
        );

        XYPlot plot = (XYPlot) chart.getPlot();
        plot.setDomainPannable(true);
        plot.setRangePannable(true);

        XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer(true, false);
        renderer1.setSeriesPaint(0, Color.blue);
        renderer1.setSeriesPaint(1, Color.red);
        plot.setRenderer(0, renderer1);

        NumberAxis monitoringRangeAxis = new NumberAxis("Actual cache size");
        plot.setRangeAxis(1, monitoringRangeAxis);
        plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
        plot.setDataset(1, datasets.get(1));
        plot.mapDatasetToRangeAxis(1, 1);

        XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(true, false);
        renderer2.setSeriesPaint(0, Color.green.darker());
        plot.setRenderer(1, renderer2);

        return chart;
    }

    private JPanel createPanel(JFreeChart chart) {
        ChartPanel panel = new ChartPanel(chart);
        panel.setMouseWheelEnabled(true);
        return panel;
    }

    public void plot(TimeSetpointActualPlotItem timeSetpointActualPlotItem,
                     TimeMonitoringPlotItem timeMonitoringPlotItem) {
        if (!Thread.currentThread().isInterrupted()) {
            try {
                SwingUtils.executeBlockingOnEdt(() -> {
                            XYSeries setpointSeries = setpointActualDataset.getSeries(0);
                            XYSeries actualSeries = setpointActualDataset.getSeries(1);

                            setpointSeries.add(timeSetpointActualPlotItem.getTime(), timeSetpointActualPlotItem.getSetpoint());
                            actualSeries.add(timeSetpointActualPlotItem.getTime(), timeSetpointActualPlotItem.getActual());

                            if (isDouble(timeMonitoringPlotItem.getMonitoring())) {
                                XYSeries monitoringSeries = monitoringDataset.getSeries(0);
                                monitoringSeries.add(timeMonitoringPlotItem.getTime(), Double.valueOf(timeMonitoringPlotItem.getMonitoring()));
                            }
                        }
                );
            } catch (InvocationTargetException ex) {
                SwingUtils.printStackTraceAndDisplayToUser(ex);
            } catch (InterruptedException ignored) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private boolean isDouble(String monitoring) {
        try {
            //noinspection ResultOfMethodCallIgnored
            Double.parseDouble(monitoring);
            return true;
        } catch (Exception nfe) {
            return false;
        }
    }
}

@@ -2348,19 +2348,65 @@
 artTitle
+,%0A                        monitoringSeriesName
 );%0A
-
         
@@ -3439,16 +3439,80 @@
 artTitle
+,%0A                                   String monitoringSeriesName
 ) %7B%0A    
@@ -4309,29 +4309,126 @@
 xis(
-%22Actual cache size%22);
+monitoringSeriesName);%0A        monitoringRangeAxis.setUpperMargin(1.0); // 100%25 -%3E to force it to be in the lower half
 %0A   
@@ -5891,32 +5891,32 @@
 t.getSeries(0);%0A
-
                 
@@ -5985,16 +5985,56 @@
 tTime(),
+%0A                                       
  Double.

add an upper margin of 100% for the monitoring axis to force the values to be drawn in the lower half of the chart