/*******************************************************************************
 * 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
 *     Dirk Fauth <dirk.fauth@googlemail.com - Bug 449764
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.resize.mode;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.painter.IOverlayPainter;
import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand;
import org.eclipse.nebula.widgets.nattable.ui.action.IDragMode;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeDetectUtil;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;

/**
 * Drag mode that will implement the row resizing process.
 */
public class RowResizeDragMode implements IDragMode {

    private static final int DEFAULT_ROW_HEIGHT_MINIMUM = 18;

    protected int gridRowPositionToResize;
    protected int originalRowHeight;
    protected int startY;
    protected int currentY;
    protected int lastY = -1;
    protected int gridRowStartY;

    protected boolean checkMinimumWidth = true;

    protected final IOverlayPainter overlayPainter = new RowResizeOverlayPainter();

    @Override
    public void mouseDown(NatTable natTable, MouseEvent event) {
        natTable.forceFocus();
        this.gridRowPositionToResize = CellEdgeDetectUtil.getRowPositionToResize(natTable, new Point(event.x, event.y));
        if (this.gridRowPositionToResize > 0) {
            this.gridRowStartY = natTable.getStartYOfRowPosition(this.gridRowPositionToResize);
            this.originalRowHeight = natTable.getRowHeightByPosition(this.gridRowPositionToResize);
            this.startY = event.y;
            natTable.addOverlayPainter(this.overlayPainter);
        }
    }

    @Override
    public void mouseMove(NatTable natTable, MouseEvent event) {
        this.currentY = event.y;
        // redraw the space after the bottom of the last row
        // to be able to render the resize drag indicator
        natTable.repaintVerticalLeftOver();

        if (this.checkMinimumWidth
                && this.currentY < this.gridRowStartY + getRowHeightMinimum()) {
            this.currentY = this.gridRowStartY + getRowHeightMinimum();
        } else {
            int overlayExtent = RowResizeOverlayPainter.ROW_RESIZE_OVERLAY_HEIGHT / 2;

            Set<Integer> rowsToRepaint = new HashSet<>();

            rowsToRepaint.add(natTable.getRowPositionByY(this.currentY - overlayExtent));
            rowsToRepaint.add(natTable.getRowPositionByY(this.currentY + overlayExtent));

            if (this.lastY >= 0) {
                rowsToRepaint.add(natTable.getRowPositionByY(this.lastY - overlayExtent));
                rowsToRepaint.add(natTable.getRowPositionByY(this.lastY + overlayExtent));
            }

            for (Integer rowToRepaint : rowsToRepaint) {
                natTable.repaintRow(rowToRepaint.intValue());
            }

            this.lastY = this.currentY;
        }
    }

    @Override
    public void mouseUp(NatTable natTable, MouseEvent event) {
        natTable.removeOverlayPainter(this.overlayPainter);
        updateRowHeight(natTable, event);
    }

    private void updateRowHeight(ILayer natLayer, MouseEvent e) {
        int dragHeight = e.y - this.startY;
        int newRowHeight = this.originalRowHeight + dragHeight;
        if (newRowHeight < getRowHeightMinimum()) {
            newRowHeight = getRowHeightMinimum();
        }
        // trigger the RowResizeCommand with downScaling enabled
        // as the user performed a drag operation the newRowHeight is the value
        // on the screen, which needs to be down scaled in the DataLayer
        natLayer.doCommand(
                new RowResizeCommand(
                        natLayer,
                        this.gridRowPositionToResize,
                        newRowHeight,
                        true));
    }

    // XXX: should ask the layer for its minimum row height
    public int getRowHeightMinimum() {
        return DEFAULT_ROW_HEIGHT_MINIMUM;
    }

    private class RowResizeOverlayPainter implements IOverlayPainter {

        static final int ROW_RESIZE_OVERLAY_HEIGHT = 2;

        @Override
        public void paintOverlay(GC gc, ILayer layer) {
            Color originalBackgroundColor = gc.getBackground();
            gc.setBackground(GUIHelper.COLOR_DARK_GRAY);
            gc.fillRectangle(
                    0,
                    RowResizeDragMode.this.currentY - (ROW_RESIZE_OVERLAY_HEIGHT / 2),
                    layer.getWidth(),
                    ROW_RESIZE_OVERLAY_HEIGHT);
            gc.setBackground(originalBackgroundColor);
        }
    }
}
