/*******************************************************************************
 * 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
 *     Jonas Hugo <Jonas.Hugo@jeppesen.com>,
 *       Markus Wahl <Markus.Wahl@jeppesen.com> - Use getters and setters for
 *         the markers of SelectionLayer instead of the fields.
 *     Dirk Fauth <dirk.fauth@googlemail.com> - Bug 453851
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.selection;

import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.isControlOnly;

import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.selection.command.SelectCellCommand;
import org.eclipse.swt.graphics.Rectangle;

public class SelectCellCommandHandler implements ILayerCommandHandler<SelectCellCommand> {

    /**
     * @since 1.6
     */
    protected final SelectionLayer selectionLayer;

    public SelectCellCommandHandler(SelectionLayer selectionLayer) {
        this.selectionLayer = selectionLayer;
    }

    @Override
    public boolean doCommand(ILayer targetLayer, SelectCellCommand command) {
        if (command.convertToTargetLayer(this.selectionLayer)) {
            toggleCell(
                    command.getColumnPosition(),
                    command.getRowPosition(),
                    command.isShiftMask(),
                    command.isControlMask(),
                    command.isForcingEntireCellIntoViewport());
            this.selectionLayer.fireCellSelectionEvent(
                    command.getColumnPosition(),
                    command.getRowPosition(),
                    command.isForcingEntireCellIntoViewport(),
                    command.isShiftMask(),
                    command.isControlMask());
            return true;
        }
        return false;
    }

    /**
     * Toggles the selection state of the given row and column.
     */
    protected void toggleCell(
            int columnPosition, int rowPosition,
            boolean withShiftMask, boolean withControlMask,
            boolean forcingEntireCellIntoViewport) {
        boolean selectCell = true;
        if (isControlOnly(withShiftMask, withControlMask)) {
            if (this.selectionLayer.isCellPositionSelected(columnPosition, rowPosition)) {
                ILayerCell cell = this.selectionLayer.getCellByPosition(columnPosition, rowPosition);
                Rectangle cellRect = new Rectangle(
                        cell.getOriginColumnPosition(),
                        cell.getOriginRowPosition(),
                        cell.getColumnSpan(),
                        cell.getRowSpan());
                this.selectionLayer.clearSelection(cellRect);
                selectCell = false;
            }
        }
        if (selectCell) {
            selectCell(columnPosition, rowPosition, withShiftMask, withControlMask);
        }
    }

    /**
     * Selects a cell, optionally clearing current selection
     */
    public void selectCell(int columnPosition, int rowPosition,
            boolean withShiftMask, boolean withControlMask) {

        if (!withShiftMask && !withControlMask) {
            this.selectionLayer.clear(false);
        }

        ILayerCell cell = this.selectionLayer.getCellByPosition(columnPosition,
                rowPosition);

        if (cell != null) {
            this.selectionLayer.setLastSelectedCell(cell.getOriginColumnPosition(),
                    cell.getOriginRowPosition());

            // Shift pressed + row selected
            if (this.selectionLayer.getSelectionModel().isMultipleSelectionAllowed()
                    && withShiftMask
                    && this.selectionLayer.getLastSelectedRegion() != null
                    && this.selectionLayer.hasRowSelection()
                    && (this.selectionLayer.getSelectionAnchor().rowPosition != SelectionLayer.NO_SELECTION)
                    && (this.selectionLayer.getSelectionAnchor().columnPosition != SelectionLayer.NO_SELECTION)) {
                // if cell.rowPosition > getSelectionAnchor().rowPositon, then
                // use cell.rowPosition + span - 1 (maxRowPosition)
                // else use cell.originRowPosition (minRowPosition)
                // and compare with selectionAnchor.rowPosition
                Rectangle lastSelected = new Rectangle(
                        this.selectionLayer.getLastSelectedRegion().x,
                        this.selectionLayer.getLastSelectedRegion().y,
                        this.selectionLayer.getLastSelectedRegion().width,
                        this.selectionLayer.getLastSelectedRegion().height);

                if (cell.getRowPosition() > this.selectionLayer.getSelectionAnchor().rowPosition) {
                    int maxRowPosition = cell.getOriginRowPosition() + cell.getRowSpan() - 1;
                    lastSelected.height = Math.abs(
                            this.selectionLayer.getSelectionAnchor().rowPosition - maxRowPosition) + 1;
                } else {
                    int minRowPosition = cell.getOriginRowPosition();
                    lastSelected.height = Math.abs(
                            this.selectionLayer.getSelectionAnchor().rowPosition - minRowPosition) + 1;
                }
                lastSelected.y = Math.min(
                        this.selectionLayer.getSelectionAnchor().rowPosition,
                        cell.getOriginRowPosition());

                if (cell.getColumnPosition() > this.selectionLayer.getSelectionAnchor().columnPosition) {
                    int maxColumnPosition = cell.getOriginColumnPosition() + cell.getColumnSpan() - 1;
                    lastSelected.width = Math.abs(
                            this.selectionLayer.getSelectionAnchor().columnPosition - maxColumnPosition) + 1;
                } else {
                    int minColumnPosition = cell.getOriginColumnPosition();
                    lastSelected.width = Math.abs(
                            this.selectionLayer.getSelectionAnchor().columnPosition - minColumnPosition) + 1;
                }
                lastSelected.x = Math.min(
                        this.selectionLayer.getSelectionAnchor().columnPosition,
                        cell.getOriginColumnPosition());

                this.selectionLayer.setLastSelectedRegion(lastSelected);
                this.selectionLayer.addSelection(this.selectionLayer.getLastSelectedRegion());
            } else {
                this.selectionLayer.setLastSelectedRegion(null);
                Rectangle selection = new Rectangle(
                        cell.getOriginColumnPosition(),
                        cell.getOriginRowPosition(),
                        cell.getColumnSpan(),
                        cell.getRowSpan());

                this.selectionLayer.addSelection(selection);
            }
        }
    }

    @Override
    public Class<SelectCellCommand> getCommandClass() {
        return SelectCellCommand.class;
    }

}
