/*******************************************************************************
 * 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.edit.editor;

import java.util.List;

import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter;
import org.eclipse.nebula.widgets.nattable.data.validate.IDataValidator;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.EditController;
import org.eclipse.nebula.widgets.nattable.edit.ICellEditHandler;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.ui.matcher.IMouseEventMatcher;
import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * Interface for implementing editors that can be used in a NatTable. Such an
 * editor is mainly a wrapper for a native SWT control, providing additional
 * functionality to support NatTable specific handling, e.g. conversion,
 * validation, model updates.
 *
 * Implementations are responsible for capturing new cell value during cell
 * edit.
 */
public interface ICellEditor {

    /**
     * This method will be called by the framework to activate this cell editor.
     * It initializes the the values needed for further processing of the editor
     * and will add listeners for general behavior of the editor control.
     *
     * @param parent
     *            The parent Composite, needed for the creation of the editor
     *            control.
     * @param originalCanonicalValue
     *            The value that should be put to the activated editor control.
     * @param editMode
     *            The {@link EditModeEnum} which is used to activate special
     *            behavior and styling. This is needed because activating an
     *            editor inline will have different behavior (e.g. moving the
     *            selection after commit) and styling than rendering the editor
     *            on a subdialog.
     * @param editHandler
     *            The {@link ICellEditHandler} that will be used on commit.
     * @param cell
     *            The cell whose corresponding editor should be activated.
     * @param configRegistry
     *            The {@link IConfigRegistry} containing the configuration of
     *            the current NatTable instance. This is necessary because the
     *            editors in the current architecture are not aware of the
     *            NatTable instance they are running in.
     * @return The SWT {@link Control} to be used for capturing the new cell
     *         value.
     */
    public Control activateCell(Composite parent,
            Object originalCanonicalValue, EditModeEnum editMode,
            ICellEditHandler editHandler, ILayerCell cell,
            IConfigRegistry configRegistry);

    /**
     * @return The column index of the cell to which this editor is attached.
     */
    int getColumnIndex();

    /**
     * @return The row index of the cell to which this editor is attached.
     */
    int getRowIndex();

    /**
     * @return The column position of the cell to which this editor is attached.
     */
    int getColumnPosition();

    /**
     * @return The row position of the cell to which this editor is attached.
     */
    int getRowPosition();

    /**
     * Returns the current value in this editor prior to conversion. For a text
     * editor that is used to edit integer values, this would mean it returns
     * the text value instead of the converted integer value. This method is
     * only intended to be used internally .
     *
     * @return The current value in this editor prior to conversion.
     */
    Object getEditorValue();

    /**
     * Sets the given value to editor control. This method is used to put the
     * display values to the wrapped editor.
     *
     * @param value
     *            The display value to set to the wrapped editor control.
     */
    void setEditorValue(Object value);

    /**
     * Converts the current value in this editor using the configured
     * {@link IDisplayConverter}. If there is no {@link IDisplayConverter}
     * registered for this editor, the value itself will be returned.
     *
     * @return The canonical value after converting the current value or the
     *         value itself if no {@link IDisplayConverter} is configured.
     * @throws RuntimeException
     *             for conversion failures. As the {@link IDisplayConverter}
     *             interface does not specify throwing checked Exceptions on
     *             converting data, only unchecked Exceptions can occur. This is
     *             needed to stop further commit processing if the conversion
     *             failed.
     * @see IDisplayConverter
     */
    Object getCanonicalValue();

    /**
     * Converts the current value in this editor using the configured
     * {@link IDisplayConverter}. If there is no {@link IDisplayConverter}
     * registered for this editor, the value itself will be returned. Will use
     * the specified {@link IEditErrorHandler} for handling conversion errors.
     *
     * @param conversionErrorHandler
     *            The error handler that will be activated in case of conversion
     *            errors.
     * @return The canonical value after converting the current value or the
     *         value itself if no {@link IDisplayConverter} is configured.
     * @throws RuntimeException
     *             for conversion failures. As the {@link IDisplayConverter}
     *             interface does not specify throwing checked Exceptions on
     *             converting data, only unchecked Exceptions can occur. This is
     *             needed to stop further commit processing if the conversion
     *             failed.
     * @see IDisplayConverter
     */
    Object getCanonicalValue(IEditErrorHandler conversionErrorHandler);

    /**
     * Sets the given canonical value to the wrapped editor control. Prior to
     * setting the value it needs to be converted to the display value, using
     * the configured {@link IDisplayConverter}.
     *
     * @param canonicalValue
     *            The canonical value to be set to the wrapped editor control.
     */
    void setCanonicalValue(Object canonicalValue);

    /**
     * Validates the given value using the configured {@link IDataValidator}.
     * This method should be called with the value converted before by using
     * {@link ICellEditor#getCanonicalValue()}.
     *
     * @param canonicalValue
     *            The canonical value to validate.
     * @return <code>true</code> if the current value in this editor is valid or
     *         no {@link IDataValidator} is registered, <code>false</code> if
     *         the value is not valid.
     */
    boolean validateCanonicalValue(Object canonicalValue);

    /**
     * Validates the current value in this editor using the configured
     * {@link IDataValidator}. Validates the given value using the configured
     * {@link IDataValidator}. This method should be called with the value
     * converted before by using {@link ICellEditor#getCanonicalValue()}. Will
     * use the specified {@link IEditErrorHandler} for handling validation
     * errors.
     *
     * @param canonicalValue
     *            The canonical value to validate.
     * @param validationErrorHandler
     *            The error handler that will be activated in case of validation
     *            errors.
     * @return <code>true</code> if the current value in this editor is valid or
     *         no {@link IDataValidator} is registered, <code>false</code> if
     *         the value is not valid.
     */
    public boolean validateCanonicalValue(Object canonicalValue,
            IEditErrorHandler validationErrorHandler);

    /**
     * Commits the current value of this editor. Will first try to convert and
     * validate the current value, and if that succeeds and the value can be
     * committed to the data model, the editor will be closed afterwards.
     *
     * @param direction
     *            The direction the selection within the NatTable should move
     *            after commit has finished.
     * @return <code>true</code> if the commit operation succeeded,
     *         <code>false</code> if the current value could not be committed. A
     *         value might not be committed for example if the conversion or the
     *         validation failed.
     */
    boolean commit(MoveDirectionEnum direction);

    /**
     * Commits the current value of this editor. Will first try to convert the
     * current value. Then it is checked if the validation should be executed
     * which can be specified via parameter. If that succeeds and the value can
     * be committed to the data model, the editor will be closed afterwards.
     *
     * @param direction
     *            The direction the selection within the NatTable should move
     *            after commit has finished.
     * @param closeAfterCommit
     *            flag to tell whether this editor needs to closed after the
     *            commit or if it should stay open.
     * @return <code>true</code> if the commit operation succeeded,
     *         <code>false</code> if the current value could not be committed. A
     *         value might not be committed for example if the conversion or the
     *         validation failed.
     */
    boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit);

    /**
     * Commits the current value of this editor.
     *
     * @param direction
     *            The direction the selection within the NatTable should move
     *            after commit has finished.
     * @param closeAfterCommit
     *            flag to tell whether this editor needs to closed after the
     *            commit or if it should stay open.
     * @param skipValidation
     *            Flag to specify whether the current value in this editor
     *            should be validated or not.
     * @return <code>true</code> if the commit operation succeeded,
     *         <code>false</code> if the current value could not be committed. A
     *         value might not be committed for example if the conversion or the
     *         validation failed.
     */
    boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit,
            boolean skipValidation);

    /**
     * Close/dispose the contained {@link Control}
     */
    void close();

    /**
     * @return <code>true</code> if this editor has been closed already,
     *         <code>false</code> if it is still open
     */
    boolean isClosed();

    /**
     * @return The editor control that is wrapped by this ICellEditor.
     */
    Control getEditorControl();

    /**
     * Creates the editor control that is wrapped by this ICellEditor. Will use
     * the style configurations in ConfigRegistry for styling the control.
     *
     * @param parent
     *            The Composite that will be the parent of the new editor
     *            control. Can not be <code>null</code>
     * @return The created editor control that is wrapped by this ICellEditor.
     */
    Control createEditorControl(Composite parent);

    /**
     * Determines whether the editor should be opened inline or using a dialog.
     * By default it will check this by configuration attribute
     * {@link EditConfigAttributes#OPEN_IN_DIALOG}. If there is no configuration
     * found for this, <code>true</code> will be returned for backwards
     * compatibility.
     * <p>
     * If this method returns <code>true</code>, the editor will be opened
     * inline (default).
     * </p>
     * <p>
     * There might be editors that are only able to be opened in a dialog. These
     * implementations need to override this method to always return
     * <code>false</code>, so the editor never gets opened inline.
     * </p>
     *
     * @param configRegistry
     *            The {@link IConfigRegistry} to retrieve the configuration for
     *            inline/dialog editing out of. Needed here because the instance
     *            {@link IConfigRegistry} might not be set on calling this
     *            method.
     * @param configLabels
     *            The labels out of the LabelStack of the cell whose editor
     *            should be activated. Needed here because this method needs to
     *            be called prior to activation to determine where to activate
     *            it.
     * @return <code>true</code> if the editor should opened inline,
     *         <code>false</code> if not.
     * @see EditConfigAttributes#OPEN_IN_DIALOG
     */
    boolean openInline(IConfigRegistry configRegistry, List<String> configLabels);

    /**
     * Determines whether this editor supports multi edit behavior or not. If
     * this method returns <code>true</code>, on selecting and pressing F2 on
     * several cells that are editable, having the same editor type and
     * converter registered, a multi edit dialog will open. By default this
     * method will return <code>true</code>. You can change this behavior by
     * setting the configuration attribute
     * {@link EditConfigAttributes#SUPPORT_MULTI_EDIT}.
     * <p>
     * You should consider returning <code>false</code> e.g. if the update
     * operation is complex or you use conditional validation, where a value is
     * validated against another value in the data model.
     *
     * @param configRegistry
     *            The {@link IConfigRegistry} to retrieve the configuration for
     *            multi edit support out of. Needed here because the instance
     *            {@link IConfigRegistry} might not be set on calling this
     *            method.
     * @param configLabels
     *            The labels out of the LabelStack of the cell whose editor
     *            should be activated. Needed here because this method needs to
     *            be called prior to activation to determine where to activate
     *            it.
     * @return <code>true</code> if this editor will open in a subdialog for
     *         multi editing, <code>false</code> if the multi editing of this
     *         kind of cell editor is not supported.
     * @see EditConfigAttributes#SUPPORT_MULTI_EDIT
     */
    boolean supportMultiEdit(IConfigRegistry configRegistry, List<String> configLabels);

    /**
     * This is a very special configuration to tell whether an ICellEditor
     * should open a multi edit dialog for multi editing or not. Usually for
     * multi editing there should be always a multi edit dialog be opened. There
     * are only special cases where this doesn't make sense. The only types of
     * ICellEditors that shouldn't open multi edit dialogs are editors that
     * change their values directly and there is no interactively editor control
     * opened, e.g. checkboxes.
     *
     * @return <code>true</code> if for multi editing a multi edit dialog should
     *         be opened, <code>false</code> if the multi editing should be
     *         performed directly without opening a multi edit dialog. Note:
     *         <code>true</code> is the default value and changing it to
     *         <code>false</code> for a custom editor might cause issues if not
     *         dealed correctly.
     */
    boolean openMultiEditDialog();

    /**
     * Determines behavior after committing the value of this editor in
     * combination with selection movement. If this method return
     * <code>true</code> and the selection is moved after committing, the editor
     * for the newly selected cell will be activated immediately. If this method
     * returns <code>false</code> or the selection is not moved after commit, no
     * action should be executed.
     * <p>
     * The behavior previous to this configuration was to not open the adjacent
     * editor. So if there is no configuration registered for this,
     * <code>false</code> will be returned by default.
     * </p>
     * <p>
     * Note: It only makes sense to call this method if the editor is already
     * activated. Calling this method on an editor that has not been activated
     * already will lead to exceptions.
     * </p>
     *
     * @return <code>true</code> if the adjacent editor should be opened if the
     *         selection moves after commit, <code>false</code> if not.
     * @see EditConfigAttributes#OPEN_ADJACENT_EDITOR
     */
    boolean openAdjacentEditor();

    /**
     * This method is intended to be used by {@link IMouseEventMatcher}
     * implementations that need to check for the editor and the click position
     * to determine whether an editor should be activated or not. By default
     * this method will return <code>true</code>. Special implementations that
     * need a different behavior need to return <code>false</code> instead. E.g.
     * checkbox editors should only be activated in case the icon that
     * represents the checkbox is clicked.
     *
     * @return <code>true</code> if this {@link ICellEditor} should be activated
     *         by clicking at any position in the corresponding cell,
     *         <code>false</code> if there need to be a special position
     *         clicked.
     */
    boolean activateAtAnyPosition();

    /**
     * This method is asked on tab traversal whether this {@link ICellEditor}
     * should be automatically activated or not. This is necessary to avoid
     * automatically changing the value of a checkbox or opening a dialog editor
     * on traversal.
     *
     * @param configRegistry
     *            The {@link IConfigRegistry} to retrieve the configuration out
     *            of. Needed here because the instance {@link IConfigRegistry}
     *            might not be set on calling this method.
     * @param configLabels
     *            The labels out of the LabelStack of the cell whose editor
     *            should be activated.
     * @return <code>true</code> if this {@link ICellEditor} should be activated
     *         in case of tab traversal, <code>false</code> if not.
     * @see EditConfigAttributes#ACTIVATE_EDITOR_ON_TRAVERSAL
     */
    boolean activateOnTraversal(IConfigRegistry configRegistry, List<String> configLabels);

    /**
     * This method is intended to add listeners to the wrapped editor control to
     * add context related behavior. For example, in {@link EditModeEnum#INLINE}
     * by default this should add a FocusListener that commits the current value
     * if the editor control loses focus.
     * <p>
     * This method was introduced mainly because of two issues:
     * <ol>
     * <li>On Mac OS calling setBounds() on a Control will cause losing focus.
     * So listeners need to be added after this method is called by the
     * EditController, otherwise on activating the editor it will be closed
     * immediately after the correct size is calculated.</li>
     * <li>The main concept for cell editor activation is, that the editor
     * control is disposed on closing the editor. This way everytime the cell
     * editor is activated, a new editor control will be created. If an editor
     * is implemented that needs to keep the editor control after closing the
     * editor, it needs to be ensured that the listeners are removed again.
     * Otherwise the listeners would be added again everytime the editor is
     * activated.</li>
     * </ol>
     * This method will be called automatically by
     * {@link EditController#editCell(ILayerCell, Composite, Object, IConfigRegistry)}.
     */
    void addEditorControlListeners();

    /**
     * This method is intended to remove listeners from the wrapped editor
     * control that was added by {@link ICellEditor#addEditorControlListeners()}
     * before to add context related behavior.
     * <p>
     * This method was introduced to add the possibility to create an
     * {@link ICellEditor} whose wrapped editor control should not be disposed
     * on closing the editor.
     * </p>
     * <p>
     * The main concept for cell editor activation is, that the editor control
     * is disposed on closing the editor. This way everytime the cell editor is
     * activated, a new editor control will be created. If an editor is
     * implemented that needs to keep the editor control after closing the
     * editor, it needs to be ensured that the listeners are removed again.
     * Otherwise the listeners would be added again everytime the editor is
     * activated.
     * </p>
     * This method needs to be called on {@link ICellEditor#close()}. There is
     * no automatical call by the framework if you are not using the abstract
     * implementation of {@link ICellEditor}.
     */
    void removeEditorControlListeners();

    /**
     * This method is used to calculate the bounds of the edit control when
     * opened inline. By default it should return the given cell bounds to match
     * the cell structure in NatTable. For several cases it might be useful to
     * return the preferred size to show all content rather than trimming the
     * control to the cell size.
     * <p>
     * Note: By changing the bounds you should ensure to only modify width and
     * height attributes and not x and y coordinate, otherwise the editor
     * control will show up somewhere else and not in place of the cell that is
     * edited.
     *
     * @param cellBounds
     *            The bounds of the cell for which the editor is opened.
     * @return The bounds of the editor control that should be applied. By
     *         default the cell bounds for several cases bigger.
     */
    Rectangle calculateControlBounds(Rectangle cellBounds);
}
