/*******************************************************************************
 * 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.reorder.action;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.painter.IOverlayPainter;
import org.eclipse.nebula.widgets.nattable.reorder.command.ColumnReorderEndCommand;
import org.eclipse.nebula.widgets.nattable.reorder.command.ColumnReorderStartCommand;
import org.eclipse.nebula.widgets.nattable.selection.command.ClearAllSelectionsCommand;
import org.eclipse.nebula.widgets.nattable.ui.action.IDragMode;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeDetectUtil;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
import org.eclipse.nebula.widgets.nattable.ui.util.MouseEventHelper;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.viewport.action.AutoScrollDragMode;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

/**
 * Default {@link IDragMode} invoked for 'left click + drag' on the column
 * header. It does the following when invoked:
 * <ol>
 * <li>Fires a column reorder command, to move columns</li>
 * <li>Overlays a black line indicating the new column position</li>
 * </ol>
 */
public class ColumnReorderDragMode extends AutoScrollDragMode {

    protected NatTable natTable;
    protected MouseEvent initialEvent;
    protected MouseEvent currentEvent;
    protected int dragFromGridColumnPosition;

    protected IOverlayPainter targetOverlayPainter = new ColumnReorderOverlayPainter();

    public ColumnReorderDragMode() {
        super(true, false);
    }

    @Override
    public void mouseDown(NatTable natTable, MouseEvent event) {
        this.natTable = natTable;
        this.initialEvent = event;
        this.currentEvent = this.initialEvent;
        this.dragFromGridColumnPosition = getDragFromGridColumnPosition();

        natTable.addOverlayPainter(this.targetOverlayPainter);

        natTable.doCommand(new ClearAllSelectionsCommand());

        fireMoveStartCommand(natTable, this.dragFromGridColumnPosition);
    }

    @Override
    public void mouseMove(NatTable natTable, MouseEvent event) {
        super.mouseMove(natTable, event);

        this.currentEvent = event;

        natTable.redraw();
    }

    @Override
    public void mouseUp(NatTable natTable, MouseEvent event) {
        // Cancel any active viewport drag
        super.mouseUp(natTable, event);

        natTable.removeOverlayPainter(this.targetOverlayPainter);

        // only trigger column reordering in case there is a real drag operation
        if (!MouseEventHelper.treatAsClick(this.initialEvent, this.currentEvent)) {
            int dragToGridColumnPosition = getDragToGridColumnPosition(
                    getMoveDirection(event.x),
                    natTable.getColumnPositionByX(event.x));

            if (!isValidTargetColumnPosition(natTable, this.dragFromGridColumnPosition, dragToGridColumnPosition)) {
                dragToGridColumnPosition = -1;
            }

            fireMoveEndCommand(natTable, dragToGridColumnPosition);
        }

        natTable.redraw();
    }

    protected int getDragFromGridColumnPosition() {
        return this.natTable.getColumnPositionByX(this.initialEvent.x);
    }

    protected int getDragToGridColumnPosition(CellEdgeEnum moveDirection, int gridColumnPosition) {
        int dragToGridColumnPosition = -1;

        if (moveDirection != null) {
            if (moveDirection == CellEdgeEnum.LEFT) {
                dragToGridColumnPosition = gridColumnPosition;
            } else if (moveDirection == CellEdgeEnum.RIGHT) {
                dragToGridColumnPosition = gridColumnPosition + 1;
            }
        }

        return dragToGridColumnPosition;
    }

    protected CellEdgeEnum getMoveDirection(int x) {
        ILayerCell cell = getColumnCell(x);
        if (cell != null) {
            Rectangle selectedColumnHeaderRect = cell.getBounds();
            return CellEdgeDetectUtil.getHorizontalCellEdge(selectedColumnHeaderRect, new Point(x, this.initialEvent.y));
        }

        return null;
    }

    /**
     *
     * @param x
     *            The x coordinate to determine the column of the NatTable.
     * @return The cell at the given x coordinate and the y coordinate of the
     *         initial event.
     * @since 1.6
     */
    protected ILayerCell getColumnCell(int x) {
        int gridColumnPosition = this.natTable.getColumnPositionByX(x);
        int gridRowPosition = this.natTable.getRowPositionByY(this.initialEvent.y);
        return this.natTable.getCellByPosition(gridColumnPosition, gridRowPosition);
    }

    protected boolean isValidTargetColumnPosition(ILayer natLayer, int dragFromGridColumnPosition, int dragToGridColumnPosition) {
        return dragFromGridColumnPosition >= 0 && dragToGridColumnPosition >= 0;
    }

    protected void fireMoveStartCommand(NatTable natTable, int dragFromGridColumnPosition) {
        natTable.doCommand(new ColumnReorderStartCommand(natTable, dragFromGridColumnPosition));
    }

    protected void fireMoveEndCommand(NatTable natTable, int dragToGridColumnPosition) {
        natTable.doCommand(new ColumnReorderEndCommand(natTable, dragToGridColumnPosition));
    }

    /**
     * {@link IOverlayPainter} that paints the black line on the target
     * destination if valid.
     *
     * @since 1.6
     */
    protected class ColumnReorderOverlayPainter implements IOverlayPainter {

        @Override
        public void paintOverlay(GC gc, ILayer layer) {
            int dragFromGridColumnPosition = getDragFromGridColumnPosition();

            if (ColumnReorderDragMode.this.currentEvent.x > ColumnReorderDragMode.this.natTable.getWidth()) {
                return;
            }

            CellEdgeEnum moveDirection = getMoveDirection(ColumnReorderDragMode.this.currentEvent.x);
            int dragToGridColumnPosition = getDragToGridColumnPosition(
                    moveDirection,
                    ColumnReorderDragMode.this.natTable.getColumnPositionByX(ColumnReorderDragMode.this.currentEvent.x));

            if (isValidTargetColumnPosition(
                    ColumnReorderDragMode.this.natTable,
                    dragFromGridColumnPosition, dragToGridColumnPosition)) {
                int dragToColumnHandleX = -1;

                if (moveDirection != null) {
                    Rectangle selectedColumnHeaderRect = getColumnCell(ColumnReorderDragMode.this.currentEvent.x).getBounds();

                    if (moveDirection == CellEdgeEnum.LEFT) {
                        dragToColumnHandleX = selectedColumnHeaderRect.x;
                    } else if (moveDirection == CellEdgeEnum.RIGHT) {
                        dragToColumnHandleX = selectedColumnHeaderRect.x + selectedColumnHeaderRect.width;
                    }
                }

                if (dragToColumnHandleX > 0) {
                    Color orgBgColor = gc.getBackground();
                    gc.setBackground(GUIHelper.COLOR_DARK_GRAY);
                    gc.fillRectangle(dragToColumnHandleX - 1, 0, 2, layer.getHeight());
                    gc.setBackground(orgBgColor);
                }
            }
        }
    }
}
