/*******************************************************************************
 * Copyright (c) 2012, 2020 Original authors and others.
 *
 * 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:
 *     Original authors and others - initial API and implementation
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.ui.matcher;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;

/**
 * Matches a mouse click on an {@link ICellPainter} within a cell in a specified
 * region.
 */
public class CellPainterMouseEventMatcher extends MouseEventMatcher {

    private ICellPainter targetCellPainter;
    private Class<? extends ICellPainter> targetCellPainterClass;

    /**
     * Creates a {@link CellPainterMouseEventMatcher} for a given region name,
     * mouse button and a specific target cellPainter instance. Can be used in
     * case one single painter instance is registered that should be checked
     * for.
     *
     * @param regionName
     *            The name of the region where this matcher should react on.
     *            Typically a {@link GridRegion} if a default grid is used. Can
     *            also be another value in case a custom region label is defined
     *            in a custom composition.
     * @param button
     *            The mouse button this matcher should react on, e.g.
     *            {@link MouseEventMatcher#LEFT_BUTTON} and
     *            {@link MouseEventMatcher#RIGHT_BUTTON}
     * @param targetCellPainter
     *            The {@link ICellPainter} instance that should be used for the
     *            check.
     */
    public CellPainterMouseEventMatcher(String regionName, int button, ICellPainter targetCellPainter) {
        super(regionName, button);
        this.targetCellPainter = targetCellPainter;
    }

    /**
     * Creates a {@link CellPainterMouseEventMatcher} for a given region name,
     * mouse button and a target cellPainter class. Can be used in case every
     * instance of a painter should be treated the same way, e.g. checkboxes.
     *
     * @param regionName
     *            The name of the region where this matcher should react on.
     *            Typically a {@link GridRegion} if a default grid is used. Can
     *            also be another value in case a custom region label is defined
     *            in a custom composition.
     * @param button
     *            The mouse button this matcher should react on, e.g.
     *            {@link MouseEventMatcher#LEFT_BUTTON} and
     *            {@link MouseEventMatcher#RIGHT_BUTTON}
     * @param targetCellPainterClass
     *            The concrete type of the {@link ICellPainter} that should be
     *            used for the check.
     */
    public CellPainterMouseEventMatcher(String regionName, int button, Class<? extends ICellPainter> targetCellPainterClass) {
        super(regionName, button);
        this.targetCellPainterClass = targetCellPainterClass;
    }

    @Override
    public boolean matches(NatTable natTable, MouseEvent event, LabelStack regionLabels) {
        if (super.matches(natTable, event, regionLabels)) {
            int columnPosition = natTable.getColumnPositionByX(event.x);
            int rowPosition = natTable.getRowPositionByY(event.y);

            ILayerCell cell = natTable.getCellByPosition(columnPosition, rowPosition);

            // Bug 407598: only perform a check if the click in the body region
            // was performed on a cell
            // cell == null can happen if the viewport is quite large and
            // contains not enough cells to fill it.
            if (cell != null) {
                IConfigRegistry configRegistry = natTable.getConfigRegistry();
                ICellPainter cellPainter = cell.getLayer().getCellPainter(
                        columnPosition,
                        rowPosition,
                        cell,
                        configRegistry);

                GC gc = new GC(natTable.getDisplay());
                try {
                    Rectangle adjustedCellBounds = natTable.getLayerPainter().adjustCellBounds(
                            columnPosition,
                            rowPosition,
                            cell.getBounds());

                    ICellPainter clickedCellPainter = cellPainter.getCellPainterAt(
                            event.x,
                            event.y,
                            cell,
                            gc,
                            adjustedCellBounds,
                            configRegistry);
                    if (clickedCellPainter != null) {
                        if ((this.targetCellPainter != null && this.targetCellPainter == clickedCellPainter)
                                || (this.targetCellPainterClass != null && this.targetCellPainterClass.isInstance(clickedCellPainter))) {
                            return true;
                        }
                    }
                } finally {
                    gc.dispose();
                }
            }
        }
        return false;
    }

}
