/*******************************************************************************
 * 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
 *     Dirk Fauth <dirk.fauth@googlemail.com> - Bug 460077
 ******************************************************************************/
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.ColumnResizeCommand;
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 column resizing process.
 */
public class ColumnResizeDragMode implements IDragMode {

    private static final int DEFAULT_COLUMN_WIDTH_MINIMUM = 25;

    protected int columnPositionToResize;
    protected int originalColumnWidth;
    protected int startX;
    protected int currentX;
    protected int lastX = -1;
    protected int gridColumnStartX;

    protected boolean checkMinimumWidth = true;

    protected final IOverlayPainter overlayPainter = new ColumnResizeOverlayPainter();

    @Override
    public void mouseDown(NatTable natTable, MouseEvent event) {
        natTable.forceFocus();
        this.columnPositionToResize = CellEdgeDetectUtil.getColumnPositionToResize(natTable, new Point(event.x, event.y));
        if (this.columnPositionToResize >= 0) {
            this.gridColumnStartX = natTable.getStartXOfColumnPosition(this.columnPositionToResize);
            this.originalColumnWidth = natTable.getColumnWidthByPosition(this.columnPositionToResize);
            this.startX = event.x;
            natTable.addOverlayPainter(this.overlayPainter);
        }
    }

    @Override
    public void mouseMove(NatTable natTable, MouseEvent event) {
        this.currentX = event.x;
        // redraw the space to the right of the last column
        // to be able to render the resize drag indicator
        natTable.repaintHorizontalLeftOver();

        if (this.checkMinimumWidth
                && this.currentX < this.gridColumnStartX + getColumnWidthMinimum()) {
            this.currentX = this.gridColumnStartX + getColumnWidthMinimum();
        } else {
            int overlayExtent = ColumnResizeOverlayPainter.COLUMN_RESIZE_OVERLAY_WIDTH / 2;

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

            columnsToRepaint.add(natTable.getColumnPositionByX(this.currentX - overlayExtent));
            columnsToRepaint.add(natTable.getColumnPositionByX(this.currentX + overlayExtent));

            if (this.lastX >= 0) {
                columnsToRepaint.add(natTable.getColumnPositionByX(this.lastX - overlayExtent));
                columnsToRepaint.add(natTable.getColumnPositionByX(this.lastX + overlayExtent));
            }

            for (Integer columnToRepaint : columnsToRepaint) {
                if (columnToRepaint >= 0) {
                    natTable.repaintColumn(columnToRepaint.intValue());
                }
            }

            this.lastX = this.currentX;
        }
    }

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

    private void updateColumnWidth(ILayer natLayer, MouseEvent e) {
        int dragWidth = e.x - this.startX;
        int newColumnWidth = this.originalColumnWidth + dragWidth;
        if (newColumnWidth < getColumnWidthMinimum()) {
            newColumnWidth = getColumnWidthMinimum();
        }
        // trigger the ColumnResizeCommand with downScaling enabled
        // as the user performed a drag operation the newColumnWidth is the
        // value on the screen, which needs to be down scaled in the DataLayer
        natLayer.doCommand(
                new ColumnResizeCommand(
                        natLayer,
                        this.columnPositionToResize,
                        newColumnWidth,
                        true));
    }

    // XXX: This method must ask the layer what it's minimum width is!
    /**
     * @since 1.3
     */
    protected int getColumnWidthMinimum() {
        return DEFAULT_COLUMN_WIDTH_MINIMUM;
    }

    private class ColumnResizeOverlayPainter implements IOverlayPainter {

        static final int COLUMN_RESIZE_OVERLAY_WIDTH = 2;

        @Override
        public void paintOverlay(GC gc, ILayer layer) {
            Color originalBackgroundColor = gc.getBackground();
            gc.setBackground(GUIHelper.COLOR_DARK_GRAY);
            gc.fillRectangle(
                    ColumnResizeDragMode.this.currentX - (COLUMN_RESIZE_OVERLAY_WIDTH / 2),
                    0,
                    COLUMN_RESIZE_OVERLAY_WIDTH,
                    layer.getHeight());
            gc.setBackground(originalBackgroundColor);
        }
    }
}
