// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collide.client.treeview;

import collide.client.treeview.TreeNodeMutator.Css;
import elemental.dom.Element;

/**
 * Utility for mutating the String name for the model object backing a
 * {@link TreeNodeElement}, in place in a {@link Tree}.
 *
 * In other words, this is a utility class that facilitates renaming of
 * {@link TreeNodeElement}s.
 */
public class TreeNodeLabelRenamer<D> {

  /**
   * Callback that gets invoked right after the model has been changed.
   */
  public interface LabelRenamerCallback<D> {
    void onCommit(String oldLabel, TreeNodeElement<D> node);

    boolean passValidation(TreeNodeElement<D> node, String newLabel);
  }

  private final NodeDataAdapter<D> dataAdapter;

  /**
   * The {@link NodeRenderer} that we use to re-render the label portions of the
   * {@link TreeNodeElement}.
   */
  private final NodeRenderer<D> renderer;

  private LabelRenamerCallback<D> callback;

  private final TreeNodeMutator<D> treeNodeMutator;

  private final TreeNodeMutator.MutationAction<D> mutationAction =
      new TreeNodeMutator.MutationAction<D>() {

        @Override
        public Element getElementForMutation(TreeNodeElement<D> node) {
          return renderer.getNodeKeyTextContainer(node.getNodeLabel());
        }

        @Override
        public void onBeforeMutation(TreeNodeElement<D> node) {
          // Nothing to do.
        }

        @Override
        public void onMutationCommit(TreeNodeElement<D> node, String oldLabel, String newLabel) {
          if (callback != null) {
            mutateNodeKey(node, newLabel);
            callback.onCommit(oldLabel, node);
            callback = null;
          }
        }

        @Override
        public boolean passValidation(TreeNodeElement<D> node, String newLabel) {
          return callback == null || callback.passValidation(node, newLabel);
        }
      };

  public TreeNodeLabelRenamer(
      NodeRenderer<D> renderer, NodeDataAdapter<D> dataAdapter, Css treeMutatorCss) {
    this.renderer = renderer;
    this.dataAdapter = dataAdapter;
    this.treeNodeMutator = new TreeNodeMutator<D>(treeMutatorCss);
  }

  public boolean isMutating() {
    return treeNodeMutator.isMutating();
  }

  /**
   * Replaces the nodes text label with an input box to allow the user to
   * rename the node.
   */
  public void enterMutation(TreeNodeElement<D> node, LabelRenamerCallback<D> callback) {

    // If we are already mutating, return.
    if (isMutating()) {
      return;
    }

    this.callback = callback;
    treeNodeMutator.enterMutation(node, mutationAction);
  }

  public void cancel() {
    treeNodeMutator.cancel();
  }

  public NodeDataAdapter<D> getDataAdapter() {
    return dataAdapter;
  }

  public NodeRenderer<D> getRenderer() {
    return renderer;
  }

  public TreeNodeMutator<D> getTreeNodeMutator() {
    return treeNodeMutator;
  }

  /**
   * Commits the current text if it passes validation, or cancels the mutation.
   */
  public void forceCommit() {
    treeNodeMutator.forceCommit();
  }

  /**
   * Updates the name value for the model object for a node, and then replaces
   * the label's contents with the new value.
   */
  public void mutateNodeKey(TreeNodeElement<D> node, String newName) {
    getDataAdapter().setNodeName(node.getData(), newName);
    Element elem = getRenderer().getNodeKeyTextContainer(node.getNodeLabel());
    elem.setTextContent(newName);
  }
}
