/*******************************************************************************
 * Copyright (c) 2008, 2018 Red Hat, Inc.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Elliott Baron <ebaron@redhat.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.massif.charting;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.linuxtools.internal.valgrind.massif.MassifSnapshot;
import org.eclipse.linuxtools.valgrind.ui.IValgrindToolView;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;
import org.swtchart.Chart;
import org.swtchart.IAxis;
import org.swtchart.IAxisSet;
import org.swtchart.ILineSeries;
import org.swtchart.ILineSeries.PlotSymbolType;
import org.swtchart.ISeries.SeriesType;
import org.swtchart.ITitle;
import org.swtchart.LineStyle;
import org.swtchart.Range;

public class ChartEditor extends EditorPart {
    private Chart control;

    @Override
    public void doSave(IProgressMonitor monitor) {
    }

    @Override
    public void doSaveAs() {
    }

    @Override
    public void init(IEditorSite site, IEditorInput input)
            throws PartInitException {
        if (input instanceof ChartEditorInput) {
            setInput(input);
            setSite(site);
            setPartName(NLS.bind(Messages.getString("ChartEditor.Heap_Chart"), input.getName())); //$NON-NLS-1$
        }
        else {
            throw new PartInitException(NLS.bind(Messages.getString("ChartEditor.Editor_input_must_be"), ChartEditorInput.class.getName())); //$NON-NLS-1$
        }
    }

    @Override
    public boolean isDirty() {
        return false;
    }

    @Override
    public boolean isSaveAsAllowed() {
        return false;
    }

    @Override
    public void createPartControl(Composite parent) {
        final ChartEditorInput input = (ChartEditorInput) getEditorInput();
        final HeapChart heapChart = input.getChart();
        control = new Chart(parent, SWT.FILL);
        heapChart.setChartControl(control);

        final Color LIGHTYELLOW = new Color(Display.getDefault(), 255, 255, 225);
        final Color WHITE = Display.getDefault().getSystemColor(SWT.COLOR_WHITE);
        final Color BLACK = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
        final Color RED = Display.getDefault().getSystemColor(SWT.COLOR_RED);
        final Color ORANGE = new Color(Display.getDefault(), 255, 165, 0);
        final Color GREEN = Display.getDefault().getSystemColor(SWT.COLOR_GREEN);
        final Color DARK_BLUE = new Color(Display.getDefault(), 64, 128, 128);
        final int TICK_GAP = 40;

        control.setBackground(WHITE);
        control.setBackgroundInPlotArea(LIGHTYELLOW);

        FontData fd = JFaceResources.getDialogFont().getFontData()[0];
        fd.setStyle(SWT.BOLD);

        Font font = new Font(Display.getDefault(), fd);
        fd.setHeight(fd.getHeight() + 2);
        Font titleFont = new Font(Display.getDefault(), fd);

        ITitle title = control.getTitle();
        title.setFont(titleFont);
        title.setForeground(BLACK);
        title.setText(heapChart.title);

        IAxis xAxis = control.getAxisSet().getXAxis(0);
        xAxis.getGrid().setStyle(LineStyle.NONE);
        xAxis.getTick().setForeground(BLACK);
        ITitle xTitle = xAxis.getTitle();
        xTitle.setFont(font);
        xTitle.setForeground(BLACK);
        xTitle.setText(heapChart.xUnits);

        IAxis yAxis = control.getAxisSet().getYAxis(0);
        yAxis.getGrid().setStyle(LineStyle.SOLID);
        yAxis.getTick().setForeground(BLACK);
        yAxis.getTick().setTickMarkStepHint(TICK_GAP);
        ITitle yTitle = yAxis.getTitle();
        yTitle.setFont(font);
        yTitle.setText(heapChart.yUnits);
        yTitle.setForeground(BLACK);

        control.getLegend().setPosition(SWT.BOTTOM);

        // data
        final ILineSeries lsUseful = (ILineSeries) control.getSeriesSet().
                createSeries(SeriesType.LINE, Messages.getString("HeapChart.Useful_Heap")); //$NON-NLS-1$;
        lsUseful.setXSeries(heapChart.time);
        lsUseful.setYSeries(heapChart.dataUseful);
        lsUseful.setSymbolType(PlotSymbolType.DIAMOND);
        lsUseful.setSymbolColor(RED);
        lsUseful.setLineColor(RED);

        final ILineSeries lsExtra = (ILineSeries) control.getSeriesSet().
                createSeries(SeriesType.LINE, Messages.getString("HeapChart.Extra_Heap")); //$NON-NLS-1$;
        lsExtra.setXSeries(heapChart.time);
        lsExtra.setYSeries(heapChart.dataExtra);
        lsExtra.setSymbolType(PlotSymbolType.DIAMOND);
        lsExtra.setSymbolColor(ORANGE);
        lsExtra.setLineColor(ORANGE);

        if (heapChart.dataStacks != null){
            final ILineSeries lsStack = (ILineSeries) control.getSeriesSet().
                    createSeries(SeriesType.LINE, Messages.getString("HeapChart.Stacks")); //$NON-NLS-1$;
            lsStack.setXSeries(heapChart.time);
            lsStack.setYSeries(heapChart.dataStacks);
            lsStack.setSymbolType(PlotSymbolType.DIAMOND);
            lsStack.setSymbolColor(DARK_BLUE);
            lsStack.setLineColor(DARK_BLUE);
        }

        final ILineSeries lsTotal = (ILineSeries) control.getSeriesSet().
                createSeries(SeriesType.LINE, Messages.getString("HeapChart.Total_Heap")); //$NON-NLS-1$;
        lsTotal.setXSeries(heapChart.time);
        lsTotal.setYSeries(heapChart.dataTotal);
        lsTotal.setSymbolType(PlotSymbolType.DIAMOND);
        lsTotal.setSymbolColor(GREEN);
        lsTotal.setLineColor(GREEN);

        // adjust axes
        control.getAxisSet().adjustRange();

        IAxisSet axisSet = control.getAxisSet();
        Range xRange = axisSet.getXAxis(0).getRange();
        Range yRange = axisSet.getYAxis(0).getRange();

        double xExtra = 0.05 * (xRange.upper - xRange.lower);
        double yExtra = 0.05 * (yRange.upper - yRange.lower);

        axisSet.getXAxis(0).setRange(new Range(xRange.lower, xRange.upper + xExtra));
        axisSet.getYAxis(0).setRange(new Range(yRange.lower, yRange.upper + yExtra));

        // listeners
        control.getPlotArea().addMouseListener(new MouseAdapter() {

            @Override
            public void mouseDown(MouseEvent e) {
                showView();
                TableViewer viewer = input.getView().getTableViewer();
                input.getView().setTopControl(viewer.getControl());

                Point p = new Point(e.x, e.y);

                int closest = 0;
                double d1, d2, d3, currMin;
                double globalMin = Double.MAX_VALUE;
                for (int i = 0; i < heapChart.time.length; i++){
                    // get distance from click event to data points for the given index
                    d1 = distance(lsUseful.getPixelCoordinates(i), p);
                    d2 = distance(lsExtra.getPixelCoordinates(i), p);
                    d3 = distance(lsTotal.getPixelCoordinates(i), p);
                    // find the closest data point to the click event
                    currMin = Math.min(Math.min(d1, d2), d3);
                    if (currMin < globalMin){
                        closest = i;
                        globalMin = currMin;
                    }
                }

                MassifSnapshot snapshot = (MassifSnapshot) viewer.getElementAt(closest);
                viewer.setSelection(new StructuredSelection(snapshot), true);

                if (e.count == 2 && snapshot.isDetailed()) {
                    ChartLocationsDialog dialog = new ChartLocationsDialog(Display.getCurrent().getActiveShell());
                    dialog.setInput(snapshot);

                    if (dialog.open() == Window.OK) {
                        dialog.openEditorForResult();
                    }
                }

            }
        });

    }

    public Chart getControl() {
        return control;
    }

    @Override
    public void setFocus() {
        if (control != null) {
            control.setFocus();
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        control.dispose();
    }


    /**
     * Shows the Valgrind view in the active page and gives it focus.
     */
    private void showView() {
        Display.getDefault().syncExec(() -> {
		    try {
		        IWorkbenchPage activePage = PlatformUI.getWorkbench()
		                .getActiveWorkbenchWindow().getActivePage();
		        activePage.showView(IValgrindToolView.VIEW_ID);
		    } catch (PartInitException e) {
		        e.printStackTrace();
		    }
		});
    }

    /**
     * Calculate Euclidean distance between two points (R2).
     */
    private double distance (Point p1, Point p2) {
        return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
    }

}
