/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : GraphPanel.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.graph;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Properties;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

import org.apache.log4j.Logger;
import org.freehep.util.export.ExportDialog;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.CombinedRangeXYPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.general.DefaultPieDataset;

import ciss.phase_viewer.common.Drawable;
import ciss.phase_viewer.common.Resources;
import ciss.phase_viewer.graph.chart.ChartCreator;
import ciss.phase_viewer.graph.dataset.PlotProperties;
import ciss.phase_viewer.graph.export.PDFExportFileType;
import ciss.phase_viewer.graph.export.SVGExportFileType;
import ciss.phase_viewer.graph.plotattributes.PlotAttributesEditor;
import ciss.phase_viewer.mainpanel.InternalFrameChase;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

// public class GraphPanel extends JInternalFrame {
public class GraphPanel extends InternalFrameChase implements Drawable,
        MouseListener, MouseMotionListener {
    private Logger logger = Logger.getLogger(GraphPanel.class.getName());

    private JFreeChart chart;

    private ChartPanel chartPanel;

    private DefaultPieDataset datasetpie = new DefaultPieDataset();

    private ChartCreator chartCreator;

    private PlotProperties plotProperties;

    private Vector childrenPanel = new Vector();

    private String baseDir;

    private JLabel labelxy;

    private double translate_factor = 0.33;

    private double zoom_factor = 1.3;

    public GraphPanel(SimpleChart simpleChart) {
        super(simpleChart.getFrameTitle(), true, true, true, true, simpleChart
                .getSize());
        chart = simpleChart.getChart();
        plotProperties = new PlotProperties();
        baseDir = System.getProperty("user.dir");
        if (chart == null) {
            logger.error("plot failed.");
            dispose();
            return;
        }
        createGUI();
        setSize(simpleChart.getSize());
    }

    public GraphPanel(String title, PlotProperties properties, String baseDir) {
        super(title, true, true, true, true, new Dimension(512, 512));
        this.plotProperties = properties;
        if (baseDir == null) {
            baseDir = System.getProperty("user.dir");
        }
        this.baseDir = baseDir;
        chartCreator = new ChartCreator();
        chart = chartCreator.getChart(properties);
        if (chart == null) {
            logger.error("plot failed.");
            dispose();
            return;
        }
        createGUI();
    }

    private void createGUI() {
        Container container = getContentPane();
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
        JPanel pbtn = new JPanel();
        pbtn.setLayout(new BorderLayout());
        pbtn.add(createButtonPanel(), BorderLayout.WEST);
        container.add(pbtn);
        chartPanel = new ChartPanel(chart, true, true, true, true, true);
        chartPanel.setPreferredSize(getSize());
        container.add(chartPanel);
        chartPanel.addMouseMotionListener(this);
        chartPanel.addMouseListener(this);

        labelxy = new JLabel("x: y: ");
        if (plotProperties.getPlotType() == PlotProperties.XYPLOT) {
            JPanel p = new JPanel();
            p.setLayout(new BorderLayout());
            p.add(labelxy, BorderLayout.WEST);
            getContentPane().add(p, "South");
        }
        GlobalProperties props = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER);
        String trans = props.getProperty("graph_mouse_motion_translate_factor");
        String zoom = props.getProperty("graph_mouse_motion_zoom_factor");
        try {
            translate_factor = Double.parseDouble(trans);
            zoom_factor = Double.parseDouble(zoom);
        } catch (Exception exc) {
        }
    }

    public GraphPanel(String title, PlotProperties properties) {
        this(title, properties, System.getProperty("user.dir"));
    }

    public JFreeChart getChart() {
        return this.chart;
    }

    public ChartPanel getChartPanel() {
        return chartPanel;
    }

    public PlotProperties getPlotProperties() {
        return this.plotProperties;
    }

    public String toString() {
        return "title: " + title;
    }

    public void addChildrenPanel(JInternalFrame frame) {
        childrenPanel.addElement(frame);
    }

    private void addDestroyer() {
        addInternalFrameListener(new InternalFrameListener() {
            public void internalFrameClosed(InternalFrameEvent ec) {
            }

            public void internalFrameActivated(InternalFrameEvent ea) {
            }

            public void internalFrameClosing(InternalFrameEvent eclosing) {
                destroy();
            }

            public void internalFrameDeactivated(InternalFrameEvent edea) {
            }

            public void internalFrameDeiconified(InternalFrameEvent edei) {
            }

            public void internalFrameIconified(InternalFrameEvent ei) {
            }

            public void internalFrameOpened(InternalFrameEvent eo) {
            }
        });
    }

    private void destroy() {
        for (int i = 0; i < childrenPanel.size(); i++) {
            ((JInternalFrame) childrenPanel.get(i)).dispose();
        }
        this.dispose();
    }

    private JToggleButton btntoggle;

    private JPanel createButtonPanel() {
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));

        JButton btnproperty = new JButton(Resources.getIcon(Resources.PROP16));
        btnproperty.setToolTipText("boot property editor");

        JButton btnexport = new JButton(Resources.getIcon(Resources.EXPORT16));
        btnexport.setToolTipText("export plot to graphics file");

        JButton btnclose = new JButton(Resources.getIcon(Resources.EXIT16));
        btnclose.setToolTipText("exit");

        btntoggle = new JToggleButton(Resources.getIcon(Resources.BLUECIRCLE16));
        btntoggle.setToolTipText("toggle mouse-motion listener");

        p.add(btnproperty);
        p.add(btnexport);
        p.add(btntoggle);
        p.add(btnclose);

        btnclose.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                chart = null;
                chartPanel = null;
                destroy();
            }
        });

        btnexport.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                export();
            }
        });

        btnproperty.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bootPropertyEditor();
            }
        });

        btntoggle.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                toggleMouseMotionListener();
            }
        });

        return p;
    }

    private boolean disableDrag = true;

    private void toggleMouseMotionListener() {
        if (btntoggle.isSelected()) {
            chartPanel.removeMouseMotionListener(chartPanel);
            chartPanel.removeMouseListener(chartPanel);
            disableDrag = false;
        } else {
            chartPanel.addMouseMotionListener(chartPanel);
            chartPanel.addMouseListener(chartPanel);
            disableDrag = true;
        }
    }

    private PlotAttributesEditor paeditor;

    private void bootPropertyEditor() {
        paeditor = new PlotAttributesEditor(this);
    }

    public PlotAttributesEditor getEditor() {
        return this.paeditor;
    }

    /* export graphics, use FreeHEP */
    private void export() {
        String inidir = new String();

        PDFExportFileType pdfexport = new PDFExportFileType(this);
        SVGExportFileType svgexport = new SVGExportFileType(this);
        // EPSExportFileType epsexport = new EPSExportFileType(this);

        // --- freeHEP ---
        ExportDialog exportD = new ExportDialog();

        Properties prop = new Properties();
        prop.setProperty("org.freehep.util.export.ExportDialog.SaveAsFile",
                baseDir + System.getProperty("file.separator") + title);
        exportD.setUserProperties(prop);
        exportD.addExportFileType(pdfexport);
        // exportD.addExportFileType(epsexport);
        exportD.addExportFileType(svgexport);

        exportD.showExportDialog((Component) this, "export view as ...",
                chartPanel, title);
        // --- freeHEP ---

    }

    public void draw(Graphics2D g2d, Rectangle2D rec) {
        chart.draw(g2d, rec);
    }

    private java.text.Format format = ciss.phase_viewer.common.ConstParameters.formater_float;

    public void mouseMoved(MouseEvent e) {
        float[] pos = mousePos2GraphPos(e);
        if (pos != null) {
            labelxy.setText(" x:  " + format.format(new Float(pos[0]))
                    + "  y:  " + format.format(new Float(pos[1])));
        }
    }

    private float[] prevPos = null;

    private int ignoreCount = 0;

    public void mouseDragged(MouseEvent e) {
        if (disableDrag) {
            return;
        }
        if (dragging3 || dragging1) {
            changeGraph(e);
        }
    }

    private void changeGraph(MouseEvent e) {
        if (prevPos == null) {
            prevPos = mousePos2GraphPos(e);
            return;
        }

        float[] currentPos = mousePos2GraphPos(e);
        if (currentPos == null) {
            return;
        }
        logger.debug("currentPos: x=" + currentPos[0] + ", y=" + currentPos[1]);
        logger.debug("previousPos: x=" + prevPos[0] + ", y=" + prevPos[1]);

        float[] difxs = new float[numdomain];
        float[] difys = new float[numrange];
        for (int i = 0; i < numdomain; i++) {
            difxs[i] = prevPos[i] - currentPos[i];
        }
        for (int i = numdomain; i < numrange + numdomain; i++) {
            difys[i - numdomain] = prevPos[i] - currentPos[i];
        }

        Plot plot = getChart().getPlot();
        if (!(plot instanceof CombinedDomainXYPlot)
                && !(plot instanceof CombinedRangeXYPlot)
                && !(plot instanceof XYPlot)) {
            return;
        }

        PlotRenderingInfo plotinfo = getChartPanel().getChartRenderingInfo()
                .getPlotInfo();
        XYPlot xyplot = null;
        if (plot instanceof CombinedDomainXYPlot) {
            xyplot = ((CombinedDomainXYPlot) plot).findSubplot(plotinfo,
                    new Point2D.Double((double) e.getX(), (double) e.getY()));
        } else if (plot instanceof CombinedRangeXYPlot) {
            xyplot = ((CombinedRangeXYPlot) plot).findSubplot(plotinfo,
                    new Point2D.Double((double) e.getX(), (double) e.getY()));
        } else if (plot instanceof XYPlot)
            xyplot = (XYPlot) plot;

        if (xyplot == null) {
            return;
        }

        double factormin = 0;
        double factormax = 0;
        if (dragging3) {
            factormin = translate_factor;
            factormax = translate_factor;
        } else if (dragging1) {
            factormin = -zoom_factor;
            factormax = zoom_factor;
        }

        for (int i = 0; i < numdomain; i++) {
            double newxmin = xmins[i] + difxs[i] * factormin;
            double newxmax = xmaxs[i] + difxs[i] * factormax;
            domains[i].setLowerBound(newxmin);
            domains[i].setUpperBound(newxmax);
        }
        for (int i = 0; i < numrange; i++) {
            double newymin = ymins[i] + difys[i] * factormin;
            double newymax = ymaxs[i] + difys[i] * factormax;
            ranges[i].setLowerBound(newymin);
            ranges[i].setUpperBound(newymax);
        }
        prevPos = currentPos;
    }

    private ValueAxis[] domains;

    private ValueAxis[] ranges;

    private double[] xmins;

    private double[] xmaxs;

    private double[] ymins;

    private double[] ymaxs;

    private int numdomain;

    private int numrange;

    private float[] mousePos2GraphPos(MouseEvent e) {
        Plot plot = getChart().getPlot();
        if (!(plot instanceof CombinedDomainXYPlot)
                && !(plot instanceof CombinedRangeXYPlot)
                && !(plot instanceof XYPlot)) {
            return null;
        }

        PlotRenderingInfo plotinfo = getChartPanel().getChartRenderingInfo()
                .getPlotInfo();
        XYPlot xyplot = null;
        if (plot instanceof CombinedDomainXYPlot) {
            xyplot = ((CombinedDomainXYPlot) plot).findSubplot(plotinfo,
                    new Point2D.Double((double) e.getX(), (double) e.getY()));
        } else if (plot instanceof CombinedRangeXYPlot) {
            xyplot = ((CombinedRangeXYPlot) plot).findSubplot(plotinfo,
                    new Point2D.Double((double) e.getX(), (double) e.getY()));
        } else if (plot instanceof XYPlot) {
            xyplot = (XYPlot) plot;
        }
        if (xyplot == null) {
            return null;
        }

        numdomain = xyplot.getDomainAxisCount();
        domains = new ValueAxis[numdomain];
        xmins = new double[numdomain];
        xmaxs = new double[numdomain];
        for (int i = 0; i < numdomain; i++) {
            domains[i] = xyplot.getDomainAxis(i);
            xmins[i] = domains[i].getLowerBound();
            xmaxs[i] = domains[i].getUpperBound();
        }

        numrange = xyplot.getRangeAxisCount();
        ranges = new ValueAxis[numrange];
        ymins = new double[numrange];
        ymaxs = new double[numrange];
        for (int i = 0; i < numrange; i++) {
            ranges[i] = xyplot.getRangeAxis(i);
            ymins[i] = ranges[i].getLowerBound();
            ymaxs[i] = ranges[i].getUpperBound();
        }

        Rectangle2D dataArea = getChartPanel().getScreenDataArea(e.getX(),
                e.getY());
        if (dataArea == null) {
            return null;
        }
        double x = (double) e.getX() - dataArea.getX();
        double y = dataArea.getY() + dataArea.getHeight() - (double) e.getY();

        float[] posxs = new float[numdomain];
        for (int i = 0; i < numdomain; i++) {
            double xhh = (xmaxs[i] - xmins[i]) / dataArea.getWidth();
            posxs[i] = (float) (xmins[i] + x * xhh);
        }

        float[] posys = new float[numrange];
        for (int i = 0; i < numrange; i++) {
            double yhh = (ymaxs[i] - ymins[i]) / dataArea.getHeight();
            posys[i] = (float) (ymins[i] + y * yhh);
        }

        float[] ret = new float[numdomain + numrange];
        for (int i = 0; i < numdomain; i++) {
            ret[i] = posxs[i];
        }
        for (int i = numdomain; i < numrange + numdomain; i++) {
            ret[i] = posys[i - numdomain];
        }

        return ret;
    }

    private boolean dragging1 = false;

    private boolean dragging3 = false;

    public void mouseReleased(MouseEvent e) {
        prevPos = null;
        dragging3 = false;
        dragging1 = false;
    }

    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {
            dragging3 = true;
        } else if (e.getButton() == MouseEvent.BUTTON1) {
            dragging1 = true;
        }
        prevPos = null;
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
    }

}
