/*******************************************************************************
 * 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.painter.cell;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.layer.event.CellVisualChangeEvent;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.BeveledBorderDecorator;
import org.eclipse.nebula.widgets.nattable.ui.NatEventData;
import org.eclipse.nebula.widgets.nattable.ui.action.IMouseAction;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;

/**
 * Paints a button and simulates a button click. It also notifies its listeners
 * when it is clicked.
 */
public class ButtonCellPainter extends AbstractCellPainter implements
        IMouseAction {
    private final ICellPainter buttonRaisedPainter;
    private final ICellPainter buttonPressedPainter;

    private int buttonFlashTime = 150;

    private int columnPosClicked;
    private int rowPosClicked;
    private boolean recentlyClicked;
    private final List<IMouseAction> clickLiseners = new ArrayList<>();

    /**
     * @param interiorPainter
     *            to paint the contents of the cell. This will be decorated with
     *            a button like look and feel.
     */
    public ButtonCellPainter(ICellPainter interiorPainter) {
        this.buttonPressedPainter = new BeveledBorderDecorator(interiorPainter,
                false);
        this.buttonRaisedPainter = new BeveledBorderDecorator(interiorPainter);
    }

    /**
     * @param buttonRaisedPainter
     *            cell painter to use for painting the button raised state.
     * @param buttonPressedPainter
     *            cell painter to use for painting the button pressed state.
     */
    public ButtonCellPainter(ICellPainter buttonRaisedPainter,
            ICellPainter buttonPressedPainter) {
        this.buttonRaisedPainter = buttonRaisedPainter;
        this.buttonPressedPainter = buttonPressedPainter;
    }

    @Override
    public void paintCell(final ILayerCell cell, final GC gc,
            final Rectangle bounds, final IConfigRegistry configRegistry) {
        if (this.recentlyClicked && this.columnPosClicked == cell.getColumnPosition()
                && this.rowPosClicked == cell.getRowPosition()) {
            this.buttonPressedPainter.paintCell(cell, gc, bounds, configRegistry);
        } else {
            this.buttonRaisedPainter.paintCell(cell, gc, bounds, configRegistry);
        }
    }

    @Override
    public int getPreferredHeight(ILayerCell cell, GC gc,
            IConfigRegistry configRegistry) {
        return cell.getBounds().height;
    }

    @Override
    public int getPreferredWidth(ILayerCell cell, GC gc,
            IConfigRegistry configRegistry) {
        return cell.getBounds().width;
    }

    private TimerTask getButtonFlashTimerTask(final ILayer layer) {
        return new TimerTask() {
            @Override
            public void run() {
                ButtonCellPainter.this.recentlyClicked = false;
                Display.getDefault().asyncExec(() -> layer.fireLayerEvent(new CellVisualChangeEvent(layer,
                        ButtonCellPainter.this.columnPosClicked, ButtonCellPainter.this.rowPosClicked)));
            }
        };
    }

    /**
     * Respond to mouse click. Simulate button press.
     */
    @Override
    public void run(final NatTable natTable, MouseEvent event) {
        NatEventData eventData = (NatEventData) event.data;
        this.columnPosClicked = eventData.getColumnPosition();
        this.rowPosClicked = eventData.getRowPosition();
        this.recentlyClicked = true;

        new Timer()
                .schedule(getButtonFlashTimerTask(natTable), this.buttonFlashTime);
        natTable.fireLayerEvent(new CellVisualChangeEvent(natTable,
                this.columnPosClicked, this.rowPosClicked));

        for (IMouseAction listener : this.clickLiseners) {
            listener.run(natTable, event);
        }
    }

    public void addClickListener(IMouseAction mouseAction) {
        this.clickLiseners.add(mouseAction);
    }

    public void removeClickListener(IMouseAction mouseAction) {
        this.clickLiseners.remove(mouseAction);
    }

    public void setButtonFlashTime(int flashTimeInMS) {
        this.buttonFlashTime = flashTimeInMS;
    }
}
