/*******************************************************************************
 * Copyright (c) 2017 Pegasystems Inc. All rights reserved.
 *
 * Contributors:
 *     Manu Varghese
 *******************************************************************************/

package com.pega.gcs.tracerviewer.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Arrays;
import java.util.concurrent.CancellationException;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;

import com.pega.gcs.fringecommon.guiutilities.Message;
import com.pega.gcs.fringecommon.guiutilities.ModalProgressMonitor;
import com.pega.gcs.fringecommon.guiutilities.MyColor;
import com.pega.gcs.fringecommon.guiutilities.NavigationPanel;
import com.pega.gcs.fringecommon.guiutilities.NavigationPanelController;
import com.pega.gcs.fringecommon.guiutilities.NavigationTableController;
import com.pega.gcs.fringecommon.guiutilities.RecentFile;
import com.pega.gcs.fringecommon.guiutilities.RecentFileContainer;
import com.pega.gcs.fringecommon.guiutilities.Searchable.SelectedRowPosition;
import com.pega.gcs.fringecommon.guiutilities.TableCompareEntry;
import com.pega.gcs.fringecommon.guiutilities.TableWidthColumnModelListener;
import com.pega.gcs.fringecommon.guiutilities.markerbar.MarkerBar;
import com.pega.gcs.fringecommon.guiutilities.markerbar.MarkerModel;
import com.pega.gcs.fringecommon.guiutilities.search.SearchPanel;
import com.pega.gcs.fringecommon.log4j2.Log4j2Helper;
import com.pega.gcs.fringecommon.utilities.FileUtilities;
import com.pega.gcs.tracerviewer.CompareMarkerModel;
import com.pega.gcs.tracerviewer.TraceNavigationTableController;
import com.pega.gcs.tracerviewer.TraceTable;
import com.pega.gcs.tracerviewer.TraceTableCompareModel;
import com.pega.gcs.tracerviewer.TraceTableCompareMouseListener;
import com.pega.gcs.tracerviewer.TraceTableCompareTask;
import com.pega.gcs.tracerviewer.TraceTableModel;
import com.pega.gcs.tracerviewer.TracerViewer;
import com.pega.gcs.tracerviewer.TracerViewerSetting;
import com.pega.gcs.tracerviewer.model.TraceEventKey;

public abstract class TracerDataCompareView extends TracerDataView {

    private static final long serialVersionUID = -454998733253496709L;

    private static final Log4j2Helper LOG = new Log4j2Helper(TracerDataCompareView.class);

    // search panel for left table
    private SearchPanel<TraceEventKey> searchPanel;

    private RecentFileContainer recentFileContainer;

    private TracerViewerSetting tracerViewerSetting;

    private JButton fileOpenJButton;

    private MarkerBar<TraceEventKey> compareMarkerBar;

    private NavigationPanel<TraceEventKey> navigationPanel;

    private NavigationPanelController<TraceEventKey> navigationPanelController;

    protected abstract TraceTable getTracerDataTableLeft();

    protected abstract TraceTable getTracerDataTableRight();

    private JScrollPane jscrollPaneLeft;

    private JScrollPane jscrollPaneRight;

    public TracerDataCompareView(TraceTableModel traceTableModel, JPanel supplementUtilityJPanel,
            TraceNavigationTableController traceNavigationTableController, RecentFileContainer recentFileContainer,
            TracerViewerSetting tracerViewerSetting) {

        super(traceTableModel, traceNavigationTableController, supplementUtilityJPanel);

        this.recentFileContainer = recentFileContainer;
        this.tracerViewerSetting = tracerViewerSetting;

        TraceTable traceTableLeft = getTracerDataTableLeft();
        traceNavigationTableController.addCustomJTable(traceTableLeft);

        searchPanel = new SearchPanel<TraceEventKey>(traceNavigationTableController, traceTableModel.getSearchModel());

        // SearchModel<TraceEventKey> searchModel =
        // traceTableModel.getSearchModel();
        // searchModel.addSearchModelListener(searchPanel);

        setLayout(new BorderLayout());

        JSplitPane traceCompareJSplitPane = getCompareJSplitPane();
        JPanel compareMarkerJPanel = getCompareMarkerJPanel();

        add(traceCompareJSplitPane, BorderLayout.CENTER);
        add(compareMarkerJPanel, BorderLayout.EAST);

        updateSupplementUtilityJPanel();
    }

    protected RecentFileContainer getRecentFileContainer() {
        return recentFileContainer;
    }

    protected TracerViewerSetting getTracerViewerSetting() {
        return tracerViewerSetting;
    }

    @Override
    protected void updateSupplementUtilityJPanel() {

        JPanel supplementUtilityJPanel = getSupplementUtilityJPanel();

        supplementUtilityJPanel.removeAll();

        LayoutManager layout = new BoxLayout(supplementUtilityJPanel, BoxLayout.LINE_AXIS);
        supplementUtilityJPanel.setLayout(layout);

        Dimension spacer = new Dimension(5, 10);
        Dimension endspacer = new Dimension(15, 10);

        JButton compareFileOpenJButton = getFileOpenJButton();

        JPanel compareFileOpenPanel = new JPanel();
        layout = new BoxLayout(compareFileOpenPanel, BoxLayout.LINE_AXIS);

        compareFileOpenPanel.add(Box.createRigidArea(spacer));
        compareFileOpenPanel.add(compareFileOpenJButton);
        compareFileOpenPanel.add(Box.createRigidArea(spacer));
        // compareFileOpenPanel.setBorder(BorderFactory.createLineBorder(MyColor.LIGHT_GRAY,
        // 1));

        JPanel navigationPanel = getNavigationPanel();

        supplementUtilityJPanel.add(Box.createHorizontalGlue());
        supplementUtilityJPanel.add(Box.createRigidArea(endspacer));
        supplementUtilityJPanel.add(compareFileOpenPanel);
        supplementUtilityJPanel.add(navigationPanel);
        supplementUtilityJPanel.add(Box.createRigidArea(endspacer));

        supplementUtilityJPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));

        supplementUtilityJPanel.revalidate();
        supplementUtilityJPanel.repaint();
    }

    private JScrollPane getjscrollPaneLeft() {

        if (jscrollPaneLeft == null) {

            TraceTable traceTableLeft = getTracerDataTableLeft();

            jscrollPaneLeft = getJScrollPane(traceTableLeft);
        }

        return jscrollPaneLeft;
    }

    private JScrollPane getjscrollPaneRight() {

        if (jscrollPaneRight == null) {

            TraceTable traceTableRight = getTracerDataTableRight();

            jscrollPaneRight = getJScrollPane(traceTableRight);
        }

        return jscrollPaneRight;
    }

    private JSplitPane getCompareJSplitPane() {

        TraceTable traceTableLeft = getTracerDataTableLeft();
        TraceTable traceTableRight = getTracerDataTableRight();

        // set selection model
        traceTableRight.setSelectionModel(traceTableLeft.getSelectionModel());

        TraceTableCompareMouseListener traceTableCompareMouseListener = new TraceTableCompareMouseListener(this);

        // add combined mouse listener
        traceTableCompareMouseListener.addTraceTable(traceTableLeft);
        traceTableCompareMouseListener.addTraceTable(traceTableRight);

        traceTableLeft.addMouseListener(traceTableCompareMouseListener);
        traceTableRight.addMouseListener(traceTableCompareMouseListener);

        // setup column model listener
        TableWidthColumnModelListener tableWidthColumnModelListener;
        tableWidthColumnModelListener = new TableWidthColumnModelListener();
        tableWidthColumnModelListener.addTable(traceTableLeft);
        tableWidthColumnModelListener.addTable(traceTableRight);

        traceTableLeft.getColumnModel().addColumnModelListener(tableWidthColumnModelListener);
        traceTableRight.getColumnModel().addColumnModelListener(tableWidthColumnModelListener);

        // setup JScrollBar
        JScrollPane jscrollPaneLeft = getjscrollPaneLeft();
        JScrollPane jscrollPaneRight = getjscrollPaneRight();

        JPanel traceTablePanelLeft = getSingleTableJPanel(jscrollPaneLeft, traceTableLeft, false);
        JPanel traceTablePanelRight = getSingleTableJPanel(jscrollPaneRight, traceTableRight, true);

        JSplitPane traceCompareJSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, traceTablePanelLeft,
                traceTablePanelRight);

        traceCompareJSplitPane.setContinuousLayout(true);
        // traceCompareJComponent.setDividerLocation(260);
        traceCompareJSplitPane.setResizeWeight(0.5);

        // not movable divider
        // traceCompareJSplitPane.setEnabled(false);

        return traceCompareJSplitPane;
    }

    private JPanel getCompareMarkerJPanel() {

        JPanel compareMarkerJPanel = new JPanel();
        compareMarkerJPanel.setLayout(new BorderLayout());

        Dimension topDimension = new Dimension(16, 60);

        JLabel topSpacer = new JLabel();
        topSpacer.setPreferredSize(topDimension);
        topSpacer.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));

        Dimension bottomDimension = new Dimension(16, 35);

        JLabel bottomSpacer = new JLabel();
        bottomSpacer.setPreferredSize(bottomDimension);
        bottomSpacer.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));

        MarkerBar<TraceEventKey> compareMarkerBar = getCompareMarkerBar();

        compareMarkerJPanel.add(topSpacer, BorderLayout.NORTH);
        compareMarkerJPanel.add(compareMarkerBar, BorderLayout.CENTER);
        compareMarkerJPanel.add(bottomSpacer, BorderLayout.SOUTH);

        return compareMarkerJPanel;

    }

    private JScrollPane getJScrollPane(TraceTable traceTable) {

        JScrollPane jscrollpane = new JScrollPane(traceTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        traceTable.setPreferredScrollableViewportSize(jscrollpane.getPreferredSize());

        return jscrollpane;
    }

    private JPanel getSingleTableJPanel(JScrollPane traceTableScrollpane, TraceTable traceTable, boolean isRightSide) {

        JPanel singleTableJPanel = new JPanel();

        singleTableJPanel.setLayout(new BorderLayout());

        final JTextField statusBar = getStatusBar();

        SearchPanel<TraceEventKey> searchPanel;

        if (isRightSide) {
            searchPanel = getSearchPanel(traceTable);
        } else {
            searchPanel = this.searchPanel;
        }

        TraceTableModel traceTableModel = (TraceTableModel) traceTable.getModel();

        JPanel tracerDataJPanel = getTracerDataJPanel(traceTableModel, traceTableScrollpane);
        JPanel statusBarJPanel = getStatusBarJPanel(statusBar);

        singleTableJPanel.add(searchPanel, BorderLayout.NORTH);
        singleTableJPanel.add(tracerDataJPanel, BorderLayout.CENTER);
        singleTableJPanel.add(statusBarJPanel, BorderLayout.SOUTH);

        traceTableModel.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {

                String propertyName = evt.getPropertyName();

                if ("message".equals(propertyName)) {
                    Message message = (Message) evt.getNewValue();
                    setMessage(statusBar, message);
                }

            }
        });

        return singleTableJPanel;
    }

    private SearchPanel<TraceEventKey> getSearchPanel(TraceTable traceTable) {

        final TraceTableModel traceTableModel = (TraceTableModel) traceTable.getModel();

        NavigationTableController<TraceEventKey> navigationTableController;
        navigationTableController = new NavigationTableController<TraceEventKey>(traceTableModel);

        navigationTableController.addCustomJTable(traceTable);

        SearchPanel<TraceEventKey> searchPanel;
        searchPanel = new SearchPanel<TraceEventKey>(navigationTableController, traceTableModel.getSearchModel());

        return searchPanel;
    }

    private JPanel getStatusBarJPanel(JTextField statusBar) {

        JPanel statusBarJPanel = new JPanel();

        LayoutManager layout = new BoxLayout(statusBarJPanel, BoxLayout.LINE_AXIS);
        statusBarJPanel.setLayout(layout);

        Dimension spacer = new Dimension(5, 16);

        statusBarJPanel.add(Box.createRigidArea(spacer));
        statusBarJPanel.add(statusBar);
        statusBarJPanel.add(Box.createRigidArea(spacer));

        statusBarJPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));

        return statusBarJPanel;

    }

    protected JPanel getTracerDataJPanel(TraceTableModel traceTableModel, JScrollPane traceTableScrollpane) {

        JPanel traceTableJPanel = new JPanel();
        traceTableJPanel.setLayout(new BorderLayout());

        JPanel markerBarPanel = getMarkerBarPanel(traceTableModel);

        traceTableJPanel.add(traceTableScrollpane, BorderLayout.CENTER);
        traceTableJPanel.add(markerBarPanel, BorderLayout.EAST);

        return traceTableJPanel;
    }

    private JTextField getStatusBar() {

        JTextField statusBar = new JTextField();
        statusBar.setEditable(false);
        statusBar.setBackground(null);
        statusBar.setBorder(null);

        return statusBar;
    }

    protected JButton getFileOpenJButton() {

        if (fileOpenJButton == null) {

            fileOpenJButton = new JButton("Open tracer file for compare");

            ImageIcon ii = FileUtilities.getImageIcon(this.getClass(), "open.png");

            fileOpenJButton.setIcon(ii);

            Dimension size = new Dimension(250, 20);
            fileOpenJButton.setPreferredSize(size);
            // compareJButton.setMinimumSize(size);
            fileOpenJButton.setMaximumSize(size);
            fileOpenJButton.setHorizontalTextPosition(SwingConstants.LEADING);

            fileOpenJButton.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent event) {

                    File fileChooserBase = null;

                    TraceTableModel traceTableModel = getTraceTableModel();

                    RecentFile recentFile = traceTableModel.getRecentFile();

                    // check for previous comparison folder
                    String leftPrevComparisionFilePath = (String) recentFile
                            .getAttribute(TracerViewer.RECENT_FILE_PREV_COMPARE_FILE);

                    if ((leftPrevComparisionFilePath != null) && (!"".equals(leftPrevComparisionFilePath))) {

                        File leftPrevComparisionFile = new File(leftPrevComparisionFilePath);

                        if (leftPrevComparisionFile.exists()) {
                            fileChooserBase = leftPrevComparisionFile;
                        }
                    }

                    if (fileChooserBase == null) {
                        // file open on the same folder as left file.
                        String leftFilePath = traceTableModel.getFilePath();

                        if ((leftFilePath != null) && (!"".equals(leftFilePath))) {
                            fileChooserBase = new File(leftFilePath);
                        }
                    }

                    FileFilter fileFilter = TracerViewer.getDefaultFileFilter(TracerViewer.FILE_CHOOSER_FILTER_DESC,
                            Arrays.asList(TracerViewer.FILE_CHOOSER_FILTER_EXT));

                    File file = TracerViewer.openFileChooser(getFileOpenJButton(), TracerViewer.class,
                            TracerViewer.FILE_CHOOSER_DIALOG_TITLE, fileFilter, fileChooserBase);

                    if (file != null) {

                        // for compare tree, the compare tree view should be
                        // inherited from compare table view, so that the same
                        // right hand data get passed over.
                        TraceTable tracerDataTableRight = getTracerDataTableRight();
                        TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) tracerDataTableRight
                                .getModel();

                        RecentFileContainer recentFileContainer = getRecentFileContainer();
                        String charset = getTracerViewerSetting().getCharset();

                        RecentFile compareRecentFile;
                        compareRecentFile = recentFileContainer.getRecentFile(file, charset);

                        // also reset the model and clears old stuff
                        traceTableCompareModel.setRecentFile(compareRecentFile);

                        // save the compare file path to main file
                        recentFile.setAttribute(TracerViewer.RECENT_FILE_PREV_COMPARE_FILE, file.getAbsolutePath());

                        TraceTable tracerDataTableLeft = getTracerDataTableLeft();

                        UIManager.put("ModalProgressMonitor.progressText", "Loading Tracer XML file");

                        final ModalProgressMonitor progressMonitor = new ModalProgressMonitor(
                                TracerDataCompareView.this, "", "Loaded 0 trace events (0%)", 0, 100);

                        progressMonitor.setMillisToDecideToPopup(0);
                        progressMonitor.setMillisToPopup(0);

                        TraceTableCompareTask traceTableCompareTask = new TraceTableCompareTask(traceTableModel,
                                tracerDataTableLeft, tracerDataTableRight, progressMonitor,
                                TracerDataCompareView.this) {

                            @Override
                            protected void done() {

                                try {

                                    get();

                                    if (!isCancelled()) {

                                        TraceTable tracerDataTableLeft = getTracerDataTableLeft();
                                        TraceTable tracerDataTableRight = getTracerDataTableRight();

                                        TraceTableCompareModel traceTableCompareModelLeft;
                                        traceTableCompareModelLeft = (TraceTableCompareModel) tracerDataTableLeft
                                                .getModel();

                                        TraceTableCompareModel traceTableCompareModelRight;
                                        traceTableCompareModelRight = (TraceTableCompareModel) tracerDataTableRight
                                                .getModel();

                                        getNavigationPanel().setEnabled(true);

                                        MarkerModel<TraceEventKey> thisMarkerModel;
                                        thisMarkerModel = new CompareMarkerModel(MyColor.LIGHT_GREEN,
                                                traceTableCompareModelLeft);

                                        MarkerModel<TraceEventKey> otherMarkerModel;
                                        otherMarkerModel = new CompareMarkerModel(Color.LIGHT_GRAY,
                                                traceTableCompareModelRight);

                                        MarkerBar<TraceEventKey> markerBar = getCompareMarkerBar();
                                        markerBar.addMarkerModel(thisMarkerModel);
                                        markerBar.addMarkerModel(otherMarkerModel);

                                        syncScrollBars();
                                        getNavigationPanelController().updateState();
                                    }
                                } catch (CancellationException ce) {
                                    LOG.info("TraceTableCompareTask cancelled: ");

                                } catch (Exception e) {
                                    JOptionPane.showMessageDialog(TracerDataCompareView.this,
                                            "Error in comparing the tracer xmls.", "Compare Tracer XML Error",
                                            JOptionPane.ERROR_MESSAGE);
                                    LOG.error("Exception in TraceTableCompareTask.", e);

                                } finally {
                                    progressMonitor.close();
                                }
                            }
                        };

                        traceTableCompareTask.execute();

                        // TracerDataMainPanel.loadFile(traceTableCompareModel,
                        // TracerDataCompareView.this, true);
                        //
                        // applyTraceModelCompare();
                    }
                }
            });
        }

        return fileOpenJButton;
    }

    protected MarkerBar<TraceEventKey> getCompareMarkerBar() {

        if (compareMarkerBar == null) {
            NavigationPanelController<TraceEventKey> navigationPanelController = getNavigationPanelController();
            compareMarkerBar = new MarkerBar<TraceEventKey>(navigationPanelController, null);
        }

        return compareMarkerBar;
    }

    protected NavigationPanel<TraceEventKey> getNavigationPanel() {

        if (navigationPanel == null) {

            JLabel label = new JLabel("Compare:");

            NavigationPanelController<TraceEventKey> compareNavigationPanelController = getNavigationPanelController();

            navigationPanel = new NavigationPanel<>(label, compareNavigationPanelController);
            navigationPanel.setEnabled(false);

        }

        return navigationPanel;
    }

    protected NavigationPanelController<TraceEventKey> getNavigationPanelController() {

        if (navigationPanelController == null) {

            navigationPanelController = new NavigationPanelController<TraceEventKey>() {

                @Override
                public void navigateToRow(int startRowIndex, int endRowIndex) {

                    TraceTable traceTableLeft = getTracerDataTableLeft();
                    TraceTable traceTableRight = getTracerDataTableRight();

                    traceTableLeft.setRowSelectionInterval(startRowIndex, endRowIndex);
                    traceTableLeft.scrollRowToVisible(startRowIndex);

                    traceTableRight.setRowSelectionInterval(startRowIndex, endRowIndex);
                    traceTableRight.scrollRowToVisible(startRowIndex);

                    traceTableLeft.updateUI();
                    traceTableRight.updateUI();
                }

                @Override
                public void updateState() {

                    NavigationPanel<TraceEventKey> navigationPanel = getNavigationPanel();

                    JLabel dataJLabel = navigationPanel.getDataJLabel();
                    JButton firstJButton = navigationPanel.getFirstJButton();
                    JButton prevJButton = navigationPanel.getPrevJButton();
                    JButton nextJButton = navigationPanel.getNextJButton();
                    JButton lastJButton = navigationPanel.getLastJButton();

                    TraceTable traceTableLeft = getTracerDataTableLeft();
                    int[] selectedrows = traceTableLeft.getSelectedRows();

                    int selectedRow = -1;

                    if (selectedrows.length > 0) {
                        selectedRow = selectedrows[selectedrows.length - 1];
                    }

                    TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) traceTableLeft.getModel();

                    int compareCount = traceTableCompareModel.getCompareCount();
                    int compareNavIndex = traceTableCompareModel.getCompareNavIndex();

                    SelectedRowPosition selectedRowPosition = SelectedRowPosition.NONE;

                    if (compareCount == 0) {
                        selectedRowPosition = SelectedRowPosition.NONE;
                    } else if (traceTableCompareModel.isCompareResultsWrap()) {
                        selectedRowPosition = SelectedRowPosition.BETWEEN;
                    } else {

                        selectedRow = (selectedRow >= 0) ? (selectedRow) : 0;

                        selectedRowPosition = traceTableCompareModel.getCompareSelectedRowPosition(selectedRow);
                    }

                    switch (selectedRowPosition) {

                    case FIRST:
                        firstJButton.setEnabled(false);
                        prevJButton.setEnabled(false);
                        nextJButton.setEnabled(true);
                        lastJButton.setEnabled(true);
                        break;

                    case LAST:
                        firstJButton.setEnabled(true);
                        prevJButton.setEnabled(true);
                        nextJButton.setEnabled(false);
                        lastJButton.setEnabled(false);
                        break;

                    case BETWEEN:
                        firstJButton.setEnabled(true);
                        prevJButton.setEnabled(true);
                        nextJButton.setEnabled(true);
                        lastJButton.setEnabled(true);
                        break;

                    case NONE:
                        firstJButton.setEnabled(false);
                        prevJButton.setEnabled(false);
                        nextJButton.setEnabled(false);
                        lastJButton.setEnabled(false);
                        break;
                    default:
                        break;
                    }

                    String text = String.format(SearchPanel.PAGES_FORMAT_STR, compareNavIndex, compareCount);

                    dataJLabel.setText(text);

                }

                @Override
                public void previous() {

                    TraceTable traceTableLeft = getTracerDataTableLeft();
                    int[] selectedrows = traceTableLeft.getSelectedRows();

                    int selectedRow = -1;

                    if (selectedrows.length > 0) {
                        selectedRow = selectedrows[selectedrows.length - 1];
                    }

                    TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) traceTableLeft.getModel();

                    TableCompareEntry tableCompareEntry;
                    tableCompareEntry = traceTableCompareModel.comparePrevious(selectedRow);

                    int startEntry = tableCompareEntry.getStartEntry();
                    int endEntry = tableCompareEntry.getEndEntry();

                    navigateToRow(startEntry, endEntry);

                    updateState();

                }

                @Override
                public void next() {

                    TraceTable traceTableLeft = getTracerDataTableLeft();
                    int[] selectedrows = traceTableLeft.getSelectedRows();

                    int selectedRow = -1;

                    if (selectedrows.length > 0) {
                        selectedRow = selectedrows[selectedrows.length - 1];
                    }

                    TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) traceTableLeft.getModel();

                    TableCompareEntry tableCompareEntry;
                    tableCompareEntry = traceTableCompareModel.compareNext(selectedRow);

                    int startEntry = tableCompareEntry.getStartEntry();
                    int endEntry = tableCompareEntry.getEndEntry();

                    navigateToRow(startEntry, endEntry);

                    updateState();

                }

                @Override
                public void last() {

                    TraceTable traceTableLeft = getTracerDataTableLeft();

                    TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) traceTableLeft.getModel();

                    TableCompareEntry tableCompareEntry;
                    tableCompareEntry = traceTableCompareModel.compareLast();

                    int startEntry = tableCompareEntry.getStartEntry();
                    int endEntry = tableCompareEntry.getEndEntry();

                    navigateToRow(startEntry, endEntry);

                    updateState();

                }

                @Override
                public void first() {

                    TraceTable traceTableLeft = getTracerDataTableLeft();

                    TraceTableCompareModel traceTableCompareModel = (TraceTableCompareModel) traceTableLeft.getModel();

                    TableCompareEntry tableCompareEntry;
                    tableCompareEntry = traceTableCompareModel.compareFirst();

                    int startEntry = tableCompareEntry.getStartEntry();
                    int endEntry = tableCompareEntry.getEndEntry();

                    navigateToRow(startEntry, endEntry);

                    updateState();

                }

                @Override
                public void scrollToKey(TraceEventKey traceEventKey) {
                    // TODO Auto-generated method stub

                }
            };
        }

        return navigationPanelController;
    }

    protected void syncScrollBars() {

        JScrollPane jscrollPaneLeft = getjscrollPaneLeft();
        JScrollPane jscrollPaneRight = getjscrollPaneRight();

        JScrollBar jscrollBarLeftH = jscrollPaneLeft.getHorizontalScrollBar();
        JScrollBar jscrollBarLeftV = jscrollPaneLeft.getVerticalScrollBar();
        JScrollBar jscrollBarRightH = jscrollPaneRight.getHorizontalScrollBar();
        JScrollBar jscrollBarRightV = jscrollPaneRight.getVerticalScrollBar();

        jscrollBarRightH.setModel(jscrollBarLeftH.getModel());
        jscrollBarRightV.setModel(jscrollBarLeftV.getModel());
    }

}