/*******************************************************************************
 * Copyright (c) 2019, 2020 Dirk Fauth.
 *
 * 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:
 *     Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.copy.command;

import org.eclipse.nebula.widgets.nattable.copy.InternalCellClipboard;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.layer.event.VisualRefreshEvent;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;

/**
 * Handler class for copying selected data within the {@link SelectionLayer} to
 * the clipboard. Will treat cells with row spanning as a single cell and will
 * not create gaps for rows with no cell to copy.
 *
 * @since 1.6
 */
public class RowSpanningCopyDataCommandHandler extends CopyDataCommandHandler {

    protected InternalCellClipboard clipboard;

    /**
     * Creates an instance that only checks the {@link SelectionLayer} for data
     * to add to the clipboard.
     *
     * @param selectionLayer
     *            The {@link SelectionLayer} within the NatTable. Can not be
     *            <code>null</code>.
     * @param clipboard
     *            The {@link InternalCellClipboard} that should be used for
     *            copy/paste operations within a NatTable instance.
     */
    public RowSpanningCopyDataCommandHandler(SelectionLayer selectionLayer, InternalCellClipboard clipboard) {
        super(selectionLayer);
        this.clipboard = clipboard;
    }

    /**
     * Creates an instance that checks the {@link SelectionLayer} and the column
     * header layer if given for data to add to the clipboard.
     *
     * @param selectionLayer
     *            The {@link SelectionLayer} within the NatTable. Can not be
     *            <code>null</code>.
     * @param columnHeaderLayer
     *            The column header layer within the NatTable grid. Can be
     *            <code>null</code>.
     * @param clipboard
     *            The {@link InternalCellClipboard} that should be used for
     *            copy/paste operations within a NatTable instance.
     */
    public RowSpanningCopyDataCommandHandler(
            SelectionLayer selectionLayer, ILayer columnHeaderLayer, InternalCellClipboard clipboard) {
        super(selectionLayer, columnHeaderLayer, null);
        this.clipboard = clipboard;
    }

    @Override
    protected void internalDoCommand(CopyDataToClipboardCommand command, ILayerCell[][] assembledCopiedDataStructure) {
        // copy to clipboard
        super.internalDoCommand(command, assembledCopiedDataStructure);

        preInternalCopy();

        // remember cells to copy to support paste
        if (this.clipboard != null) {
            this.clipboard.setCopiedCells(assembledCopiedDataStructure);
        }

        postInternalCopy();
    }

    /**
     * Collects and assembles the selected data per row position that should be
     * copied to the clipboard. For cells with row spanning only the origin cell
     * will be tracked.
     *
     * @param currentRowPosition
     *            The row position of which the selected cells should be
     *            collected.
     * @return An array containing the selected cells that should be copied to
     *         the clipboard.
     */
    @Override
    protected ILayerCell[] assembleBody(int currentRowPosition) {
        final int[] selectedColumns = getSelectedColumnPositions();
        final ILayerCell[] bodyCells = new ILayerCell[selectedColumns.length];

        for (int columnPosition = 0; columnPosition < selectedColumns.length; columnPosition++) {
            final int selectedColumnPosition = selectedColumns[columnPosition];
            if (this.selectionLayer.isCellPositionSelected(selectedColumnPosition, currentRowPosition)) {
                ILayerCell cell = null;
                if (getCopyLayer() == null) {
                    cell = this.selectionLayer.getCellByPosition(selectedColumnPosition, currentRowPosition);
                } else {
                    int copyColPos = LayerUtil.convertColumnPosition(this.selectionLayer, selectedColumnPosition, getCopyLayer());
                    int copyRowPos = LayerUtil.convertRowPosition(this.selectionLayer, currentRowPosition, getCopyLayer());

                    cell = getCopyLayer().getCellByPosition(copyColPos, copyRowPos);
                }

                if (cell != null && cell.getOriginRowPosition() == cell.getRowPosition() && isCopyAllowed(cell)) {
                    bodyCells[columnPosition] = cell;
                }
            }
        }

        if (!isEmpty(bodyCells)) {
            return bodyCells;
        } else {
            return null;
        }
    }

    /**
     * Perform actions prior copying values to the internal clipboard. E.g.
     * disabling formula evaluation.
     */
    protected void preInternalCopy() {
        // no specific action needed to be performed by default
    }

    /**
     * Perform actions after copying values to the internal clipboard. E.g.
     * enabling formula evaluation.
     */
    protected void postInternalCopy() {
        this.selectionLayer.fireLayerEvent(new VisualRefreshEvent(this.selectionLayer));
    }
}
