/*
 *  Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
 *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *  This code is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License version 2 only, as
 *  published by the Free Software Foundation.  Oracle designates this
 *  particular file as subject to the "Classpath" exception as provided
 *  by Oracle in the LICENSE file that accompanied this code.
 *
 *  This code is distributed in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  version 2 for more details (a copy is included in the LICENSE file that
 *  accompanied this code).
 *
 *  You should have received a copy of the GNU General Public License version
 *  2 along with this work; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 *  or visit www.oracle.com if you need additional information or have any
 *  questions.
 */

package org.graalvm.visualvm.modules.tracer.impl.timeline;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 *
 * @author Jiri Sedlacek
 */
public final class TimelinePanel extends JPanel {

    private final ChartPanel chartPanel;
    private final RowMouseHandler mouseHandler;


    // --- Constructor ---------------------------------------------------------
    
    public TimelinePanel(TimelineSupport support) {
        super(new BorderLayout());
        setOpaque(false);

        ProbesPanel probesPanel = new ProbesPanel(support);
        chartPanel = new ChartPanel(support.getChart(), support);

        add(probesPanel, BorderLayout.WEST);
        add(chartPanel, BorderLayout.CENTER);

        new ProbesWheelHandler(chartPanel, probesPanel).register();
        mouseHandler = new RowMouseHandler(support, probesPanel);
        mouseHandler.register();
    }


    // --- Public interface ----------------------------------------------------

    public void reset() {
        chartPanel.resetChart();
        resetSelection();
    }

    public void resetSelection() {
        if (chartPanel.resetSelection()) mouseHandler.updateSelection();
    }

    public Action zoomInAction() {
        return chartPanel.zoomInAction();
    }

    public Action zoomOutAction() {
        return chartPanel.zoomOutAction();
    }

    public Action toggleViewAction() {
        return chartPanel.toggleViewAction();
    }

    public AbstractButton mouseZoom() {
        return chartPanel.mouseZoom();
    }

    public AbstractButton mouseHScroll() {
        return chartPanel.mouseHScroll();
    }

    public AbstractButton mouseVScroll() {
        return chartPanel.mouseVScroll();
    }


    // --- Private implementation ----------------------------------------------

    private static class ProbesWheelHandler implements MouseWheelListener {

        private final ChartPanel chartPanel;
        private final ProbesPanel probesPanel;

        ProbesWheelHandler(ChartPanel chartPanel, ProbesPanel probesPanel) {
            this.chartPanel = chartPanel;
            this.probesPanel = probesPanel;
        }

        void register() {
            probesPanel.addMouseWheelListener(this);
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            chartPanel.vScroll(e);
        }

    }

    private static class RowMouseHandler extends MouseAdapter {

        private static final int RESIZE_RANGE = 3;

        private final TimelineSupport support;
        private final TimelineChart chart;
        private final ProbesPanel probesPanel;

        private int baseY;
        private int baseHeight;
        private TimelineChart.Row draggingRow;


        RowMouseHandler(TimelineSupport support, ProbesPanel probesPanel) {
            this.support = support;
            this.chart = support.getChart();
            this.probesPanel = probesPanel;
        }


        void register() {
            chart.addMouseListener(this);
            chart.addMouseMotionListener(this);
            probesPanel.getMouseTarget().addMouseListener(this);
            probesPanel.getMouseTarget().addMouseMotionListener(this);
        }


        public void mousePressed(MouseEvent e) {
            updateRowState(e, SwingUtilities.isLeftMouseButton(e));
            if (draggingRow != null) chart.updateSelection(false, this);
            updateCursor();
        }

        public void mouseReleased(MouseEvent e) {
            if (draggingRow != null) chart.updateSelection(true, this);
            updateRowState(e, false);
            updateCursor();
        }

//        public void mouseClicked(MouseEvent e) {
//            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2)
//                if (e.getSource() == chart) ; // TODO: select row in Details
//        }

        public void mouseMoved(MouseEvent e) {
            updateRowState(e, false);
            updateCursor();
        }

        public void mouseDragged(MouseEvent e){
            if (draggingRow != null) {
                boolean checkStep = (e.getModifiers() & Toolkit.getDefaultToolkit().
                                     getMenuShortcutKeyMask()) == 0;
                chart.setRowHeight(draggingRow.getIndex(), baseHeight + e.getY() - baseY, checkStep);
            }
        }


        private void updateRowState(MouseEvent e, boolean updateSelection) {
            baseY = e.getY();
            draggingRow = chart.getNearestRow(baseY, RESIZE_RANGE, true);
            if (draggingRow != null) {
                baseHeight = draggingRow.getHeight();
            } else if (updateSelection) {
                TimelineChart.Row row = chart.getRowAt(baseY);
                if ((e.getModifiers() & Toolkit.getDefaultToolkit().
                        getMenuShortcutKeyMask()) == 0) {
                    if (chart.setSelectedRow(row)) updateSelection();
                } else {
                    if (row != null && chart.toggleRowSelection(row)) updateSelection();
                }
            }
        }

        void updateSelection() {
            probesPanel.updateSelection();
            support.rowSelectionChanged();
        }

        private void updateCursor() {
            if (draggingRow != null) {
                Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
                chart.setCursor(resizeCursor);
                probesPanel.setCursor(resizeCursor);
            } else {
                Cursor defaultCursor = Cursor.getDefaultCursor();
                chart.setCursor(defaultCursor);
                probesPanel.setCursor(defaultCursor);
            }
        }

    }

}
