/*******************************************************************************
 * Copyright (c) 2010, 2015 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Patrick Tasse - Initial API and implementation
 *   Bernd Hufmann - Updated to use RGB for the tick color
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.ui.views.colors;

import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.Messages;
import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
import org.eclipse.tracecompass.tmf.ui.dialog.TmfFileDialogFactory;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.tracecompass.tmf.ui.views.filter.FilterDialog;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphColorScheme;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.PlatformUI;

/**
 * Color view implementation. This view provides support for managing color settings for filters.
 *
 * @version 1.0
 * @author Patrick Tasse
 *
 */
public class ColorsView extends TmfView {

    /** ID for the color view */
    public static final @NonNull String ID = "org.eclipse.linuxtools.tmf.ui.views.colors"; //$NON-NLS-1$

    private static final Image ADD_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/add_button.gif"); //$NON-NLS-1$
    private static final Image DELETE_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/delete_button.gif"); //$NON-NLS-1$
    private static final Image MOVE_UP_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/moveup_button.gif"); //$NON-NLS-1$
    private static final Image MOVE_DOWN_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/movedown_button.gif"); //$NON-NLS-1$
    private static final Image IMPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/import_button.gif"); //$NON-NLS-1$
    private static final Image EXPORT_IMAGE = Activator.getDefault().getImageFromPath("/icons/elcl16/export_button.gif"); //$NON-NLS-1$

    private static final String THEME_BACKGROUND = "org.eclipse.tracecompass.tmf.ui.BACKGROUND"; //$NON-NLS-1$
    private static final String THEME_FOREGROUND = "org.eclipse.tracecompass.tmf.ui.FOREGROUND"; //$NON-NLS-1$

    // ------------------------------------------------------------------------
    // Main data structures
    // ------------------------------------------------------------------------

    /**
     * The composite shell.
     */
    protected Shell fShell;
    /**
     * The main composite (scrolled composite)
     */
    protected ScrolledComposite fScrolledComposite;
    /**
     * The list composite.
     */
    protected Composite fListComposite;
    /**
     * The filler composite.
     */
    protected Composite fFillerComposite;
    /**
     *  The selected color settings row
     */
    protected ColorSettingRow fSelectedRow = null;
    /**
     *  The color scheme instance for managing colors
     */
    protected TimeGraphColorScheme traceColorScheme = new TimeGraphColorScheme();
    /**
     * An action to add a color settings row
     */
    protected Action fAddAction;
    /**
     * An action to delete a color settings row
     */
    protected Action fDeleteAction;
    /**
     * An action to move up a color settings row in the list.
     */
    protected Action fMoveUpAction;
    /**
     * An action to move down a color settings row in the list.
     */
    protected Action fMoveDownAction;
    /**
     * An action to import color settings from file.
     */
    protected Action fImportAction;
    /**
     * An action to export color settings from file.
     */
    protected Action fExportAction;
    /**
     * The list of existing color settings
     */
    protected List<ColorSetting> fColorSettings;

    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------

    /**
     *  Default Constructor
     */
    public ColorsView() {
        super("Colors"); //$NON-NLS-1$
    }

    @Override
    public void createPartControl(Composite parent) {
        fShell = parent.getShell();

        fScrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
        fScrolledComposite.setExpandHorizontal(true);
        fScrolledComposite.setExpandVertical(true);
        fListComposite = new Composite(fScrolledComposite, SWT.NONE);
        fScrolledComposite.setContent(fListComposite);

        GridLayout gl = new GridLayout();
        gl.marginHeight = 0;
        gl.marginWidth = 0;
        gl.verticalSpacing = 1;
        fListComposite.setLayout(gl);

        fColorSettings = new ArrayList<>(Arrays.asList(ColorSettingsManager.getColorSettings()));
        for (ColorSetting colorSetting : fColorSettings) {
            new ColorSettingRow(fListComposite, colorSetting);
        }

        fFillerComposite = new Composite(fListComposite, SWT.NONE);
        GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
        gd.heightHint = 0;
        fFillerComposite.setLayoutData(gd);
        gl = new GridLayout();
        gl.marginHeight = 1;
        gl.marginWidth = 1;
        fFillerComposite.setLayout(gl);
        Label fillerLabel = new Label(fFillerComposite, SWT.NONE);
        fillerLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        fillerLabel.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

        fFillerComposite.addPaintListener(e -> {
            if (fSelectedRow == null) {
                Color lineColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
                Point p = fFillerComposite.getSize();
                GC gc = e.gc;
                gc.setForeground(lineColor);
                gc.drawLine(0, 0, p.x - 1, 0);
            }
        });

        MouseListener mouseListener = new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                fSelectedRow = null;
                refresh();
            }
        };
        fillerLabel.addMouseListener(mouseListener);

        fScrolledComposite.setMinSize(fListComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));

        fillToolBar();
    }

    @Override
    public void setFocus() {
        fScrolledComposite.setFocus();
    }

    /**
     * Refreshes the view display and updates the view actions enablements.
     */
    public void refresh() {
        fListComposite.layout();
        fScrolledComposite.setMinSize(fListComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        fListComposite.redraw(0, 0, fListComposite.getBounds().width, fListComposite.getBounds().height, true);
        if (fSelectedRow == null) {
            fDeleteAction.setEnabled(false);
            fMoveUpAction.setEnabled(false);
            fMoveDownAction.setEnabled(false);
        } else {
            fDeleteAction.setEnabled(true);
            fMoveUpAction.setEnabled(true);
            fMoveDownAction.setEnabled(true);
        }
    }

    private void fillToolBar() {

        fAddAction = new AddAction();
        fAddAction.setImageDescriptor(ImageDescriptor.createFromImage(ADD_IMAGE));
        fAddAction.setToolTipText(Messages.ColorsView_AddActionToolTipText);

        fDeleteAction = new DeleteAction();
        fDeleteAction.setImageDescriptor(ImageDescriptor.createFromImage(DELETE_IMAGE));
        fDeleteAction.setToolTipText(Messages.ColorsView_DeleteActionToolTipText);
        fDeleteAction.setEnabled(false);

        fMoveUpAction = new MoveUpAction();
        fMoveUpAction.setImageDescriptor(ImageDescriptor.createFromImage(MOVE_UP_IMAGE));
        fMoveUpAction.setToolTipText(Messages.ColorsView_MoveUpActionToolTipText);
        fMoveUpAction.setEnabled(false);

        fMoveDownAction = new MoveDownAction();
        fMoveDownAction.setImageDescriptor(ImageDescriptor.createFromImage(MOVE_DOWN_IMAGE));
        fMoveDownAction.setToolTipText(Messages.ColorsView_MoveDownActionToolTipText);
        fMoveDownAction.setEnabled(false);

        fExportAction = new ExportAction();
        fExportAction.setImageDescriptor(ImageDescriptor.createFromImage(EXPORT_IMAGE));
        fExportAction.setToolTipText(Messages.ColorsView_ExportActionToolTipText);

        fImportAction = new ImportAction();
        fImportAction.setImageDescriptor(ImageDescriptor.createFromImage(IMPORT_IMAGE));
        fImportAction.setToolTipText(Messages.ColorsView_ImportActionToolTipText);

        IActionBars bars = getViewSite().getActionBars();
        IToolBarManager manager = bars.getToolBarManager();
        manager.add(fAddAction);
        manager.add(fDeleteAction);
        manager.add(fMoveUpAction);
        manager.add(fMoveDownAction);
        manager.add(new Separator());
        manager.add(fExportAction);
        manager.add(fImportAction);
    }

    private class AddAction extends Action {
        @Override
        public void run() {
            Color fc = getThemeColor(THEME_FOREGROUND);
            Color bc = getThemeColor(THEME_BACKGROUND);

            if (fc == null) {
                fc = Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
            }

            if (bc == null) {
                bc = Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
            }

            ColorSetting colorSetting = new ColorSetting(
                    checkNotNull(fc.getRGB()),
                    checkNotNull(bc.getRGB()),
                    checkNotNull(fc.getRGB()),
                    null);
            ColorSettingRow row = new ColorSettingRow(fListComposite, colorSetting);
            if (fSelectedRow == null) {
                fColorSettings.add(colorSetting);
                row.moveAbove(fFillerComposite);
            } else {
                fColorSettings.add(fColorSettings.indexOf(fSelectedRow.getColorSetting()), colorSetting);
                row.moveAbove(fSelectedRow);
            }
            fSelectedRow = row;
            refresh();
            ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
        }
    }

    private class DeleteAction extends Action {

        @Override
        public void run() {
            if (fSelectedRow != null) {
                int index = fColorSettings.indexOf(fSelectedRow.getColorSetting());
                fColorSettings.remove(index);
                fSelectedRow.fColorSetting.dispose();
                fSelectedRow.dispose();
                if (index < fColorSettings.size()) {
                    fSelectedRow = (ColorSettingRow) fListComposite.getChildren()[index];
                } else {
                    fSelectedRow = null;
                }
                refresh();
                ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
            }
        }
    }

    private class MoveUpAction extends Action {
        @Override
        public void run() {
            if (fSelectedRow != null) {
                int index = fColorSettings.indexOf(fSelectedRow.getColorSetting());
                if (index > 0) {
                    fColorSettings.add(index - 1, fColorSettings.remove(index));
                    fSelectedRow.moveAbove(fListComposite.getChildren()[index - 1]);
                    refresh();
                    ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                }
            }
        }
    }

    private class MoveDownAction extends Action {
        @Override
        public void run() {
            if (fSelectedRow != null) {
                int index = fColorSettings.indexOf(fSelectedRow.getColorSetting());
                if (index < fColorSettings.size() - 1) {
                    fColorSettings.add(index + 1, fColorSettings.remove(index));

                    fSelectedRow.moveBelow(fListComposite.getChildren()[index + 1]);
                    refresh();
                    ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                }
            }
        }
    }

    private class ExportAction extends Action {
        @Override
        public void run() {
            FileDialog fileDialog = TmfFileDialogFactory.create(fShell, SWT.SAVE);
            fileDialog.setFilterExtensions(new String[] {"*.xml"}); //$NON-NLS-1$
            fileDialog.setOverwrite(true);
            String pathName = fileDialog.open();
            if (pathName != null) {
                ColorSettingsXML.save(pathName, fColorSettings.toArray(new ColorSetting[0]));
            }
        }
    }

    private class ImportAction extends Action {
        @Override
        public void run() {
            FileDialog fileDialog = TmfFileDialogFactory.create(fShell, SWT.OPEN);
            fileDialog.setFilterExtensions(new String[] {"*.xml"}); //$NON-NLS-1$
            String pathName = fileDialog.open();
            if (pathName != null) {
                ColorSetting[] colorSettings = ColorSettingsXML.load(pathName);
                if (colorSettings.length > 0) {
                    if (!fColorSettings.isEmpty()) {
                        boolean overwrite = MessageDialog.openQuestion(fShell,
                                Messages.ColorsView_ImportOverwriteDialogTitle,
                                Messages.ColorsView_ImportOverwriteDialogMessage1 +
                                Messages.ColorsView_ImportOverwriteDialogMessage2);
                        if (overwrite) {
                            for (Control control : fListComposite.getChildren()) {
                                if (control instanceof ColorSettingRow) {
                                    ((ColorSettingRow) control).fColorSetting.dispose();
                                    control.dispose();
                                }
                            }
                            fColorSettings = new ArrayList<>();
                            fSelectedRow = null;
                        }
                    }
                    for (ColorSetting colorSetting : colorSettings) {
                        ColorSettingRow row = new ColorSettingRow(fListComposite, colorSetting);
                        if (fSelectedRow == null) {
                            fColorSettings.add(colorSetting);
                            row.moveAbove(fFillerComposite);
                        } else {
                            fColorSettings.add(fColorSettings.indexOf(fSelectedRow.getColorSetting()), colorSetting);
                            row.moveAbove(fSelectedRow);
                        }
                    }
                    refresh();
                    ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                }
            }
        }
    }

    private class ColorSettingRow extends Composite {

        ColorSetting fColorSetting;

        public ColorSettingRow(final Composite parent, final ColorSetting colorSetting) {
            super(parent, SWT.NONE);
            fColorSetting = colorSetting;

            setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

            setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            GridLayout gl = new GridLayout(7, false);
            gl.marginHeight = 1;
            gl.marginWidth = 1;
            gl.horizontalSpacing = 1;
            gl.verticalSpacing = 0;
            setLayout(gl);

            final Button fgButton = new Button(this, SWT.PUSH);
            fgButton.setText(Messages.ColorsView_ForegroundButtonText);
            fgButton.setSize(fgButton.computeSize(SWT.DEFAULT, 19));
            fgButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

            final Button bgButton = new Button(this, SWT.PUSH);
            bgButton.setText(Messages.ColorsView_BackgroundButtonText);
            bgButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

            final Composite labelComposite = new Composite(this, SWT.NONE);
            labelComposite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
            gl = new GridLayout();
            gl.marginHeight = 0;
            gl.marginWidth = 0;
            labelComposite.setLayout(gl);
            labelComposite.setBackground(colorSetting.getBackgroundColor());

            final Label label = new Label(labelComposite, SWT.NONE);
            label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, true));
            label.setText(" Text "); //$NON-NLS-1$
            label.setForeground(colorSetting.getForegroundColor());
            label.setBackground(colorSetting.getBackgroundColor());

            fgButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    fSelectedRow = ColorSettingRow.this;
                    refresh();
                    ColorDialog dialog = new ColorDialog(fShell);
                    dialog.setRGB(colorSetting.getForegroundRGB());
                    dialog.setText(Messages.ColorsView_ForegroundDialogText);
                    dialog.open();
                    RGB rgb = dialog.getRGB();
                    if (rgb != null) {
                        colorSetting.setForegroundRGB(rgb);
                        ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                        label.setForeground(colorSetting.getForegroundColor());
                    }
                }});

            bgButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    fSelectedRow = ColorSettingRow.this;
                    refresh();
                    ColorDialog dialog = new ColorDialog(fShell);
                    dialog.setRGB(colorSetting.getBackgroundRGB());
                    dialog.setText(Messages.ColorsView_BackgroundDialogText);
                    dialog.open();
                    RGB rgb = dialog.getRGB();
                    if (rgb != null) {
                        colorSetting.setBackgroundRGB(rgb);
                        ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                        labelComposite.setBackground(colorSetting.getBackgroundColor());
                        label.setBackground(colorSetting.getBackgroundColor());
                    }
                }});

            final Button tickButton = new Button(this, SWT.PUSH);
            tickButton.setText(Messages.ColorsView_TickButtonText);
            tickButton.setSize(tickButton.computeSize(SWT.DEFAULT, 19));
            tickButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

            final Canvas tickCanvas = new Canvas(this, SWT.NONE);
            GridData gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
            gd.widthHint = 12;
            gd.heightHint = bgButton.getSize().y;
            tickCanvas.setLayoutData(gd);
            tickCanvas.setBackground(traceColorScheme.getBkColor(false, false, false));
            tickCanvas.addPaintListener(e -> {
                Rectangle bounds = tickCanvas.getBounds();
                e.gc.setForeground(traceColorScheme.getColor(TimeGraphColorScheme.MID_LINE));
                int midy = bounds.y + bounds.height / 2 - 1;
                //int midy = e.y + e.height / 2;
                e.gc.drawLine(e.x, midy, e.x + e.width, midy);
                Rectangle rect = new Rectangle(e.x + 1, bounds.y + 2, 0, bounds.height - 6);
                for (int i = 1; i <= 3; i++) {
                    rect.x += i;
                    rect.width = i;
                    e.gc.setBackground(fColorSetting.getTickColor());
                    e.gc.fillRectangle(rect);
                }
            });

            tickButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    fSelectedRow = ColorSettingRow.this;
                    ColorDialog dialog = new ColorDialog(fShell);
                    dialog.setRGB(colorSetting.getTickColorRGB());
                    dialog.setText(Messages.TickColorDialog_TickColorDialogTitle);
                    dialog.open();
                    RGB rgb = dialog.getRGB();
                    if (rgb != null) {
                        colorSetting.setTickColorRGB(rgb);
                        ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                        refresh();
                    }
                }});

            final Button filterButton = new Button(this, SWT.PUSH);
            filterButton.setText(Messages.ColorsView_FilterButtonText);
            filterButton.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

            final Label filterText = new Label(this, SWT.NONE);
            ITmfFilter filter = colorSetting.getFilter();
            if (filter != null) {
                filterText.setText(filter.toString());
                filterText.setToolTipText(filter.toString());
            }
            filterText.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
            filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

            filterButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    fSelectedRow = ColorSettingRow.this;
                    refresh();
                    FilterDialog dialog = new FilterDialog(fShell);
                    dialog.setFilter(colorSetting.getFilter());
                    dialog.open();
                    if (dialog.getReturnCode() == Window.OK) {
                        if (dialog.getFilter() != null) {
                            colorSetting.setFilter(dialog.getFilter());
                            filterText.setText(dialog.getFilter().toString());
                            filterText.setToolTipText(dialog.getFilter().toString());
                        } else {
                            colorSetting.setFilter(null);
                            filterText.setText(""); //$NON-NLS-1$
                            filterText.setToolTipText(""); //$NON-NLS-1$
                        }
                        ColorSettingsManager.setColorSettings(fColorSettings.toArray(new ColorSetting[0]));
                        refresh();
                    }
                }});

            addPaintListener(e -> {
                if (fSelectedRow == ColorSettingRow.this) {
                    Color borderColor = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
                    Point p = ColorSettingRow.this.getSize();
                    Rectangle rect = new Rectangle(0, 0, p.x - 1, p.y - 1);
                    GC gc = e.gc;
                    gc.setForeground(borderColor);
                    gc.drawRectangle(rect);
                }
            });

            MouseListener mouseListener = new MouseAdapter() {
                @Override
                public void mouseDown(MouseEvent e) {
                    fSelectedRow = ColorSettingRow.this;
                    refresh();
                }
            };
            addMouseListener(mouseListener);
            label.addMouseListener(mouseListener);
            tickCanvas.addMouseListener(mouseListener);
            filterText.addMouseListener(mouseListener);
        }

        /**
         * @return the ColorSetting
         */
        public ColorSetting getColorSetting() {
            return fColorSetting;
        }

    }

    private static Color getThemeColor(String themeColorName) {
        ColorRegistry colorRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry();
        Color c = colorRegistry.get(themeColorName);
        return c;
    }
}
