/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * 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 jetbrains.mps.nodeEditor.messageTargets;

import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.nodeEditor.cells.CellFinderUtil;
import jetbrains.mps.nodeEditor.cells.EditorCell_Property;
import jetbrains.mps.nodeEditor.cells.ModelAccessor;
import jetbrains.mps.nodeEditor.cells.PropertyAccessor;
import jetbrains.mps.nodeEditor.inspector.InspectorEditorComponent;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.util.Condition;

/**
 * @author Evgeny Gerashchenko
 * @since 23 November 10
 */
public class CellFinder {
  @Nullable
  private static EditorCell getRawCell(@Nullable EditorComponent editorComponent, @Nullable final SNode node) {
    if (editorComponent == null || node == null) {
      return null;
    }
    return editorComponent.getBigValidCellForNode(node);
  }

  @Nullable
  public static EditorCell getCellForReference(@Nullable EditorComponent editorComponent, @Nullable final SNode node, final String role) {
    if (role == null) {
      return null;
    }
    EditorCell rawCell = getRawCell(editorComponent, node);
    if (rawCell == null) {
      return null;
    }
    EditorCell child = CellFinderUtil.findChildByCondition(rawCell, new Condition<EditorCell>() {
      @Override
      public boolean met(EditorCell cell) {
        return cell.isReferenceCell() && role.equals(cell.getRole()) && node == cell.getSNode();
      }
    }, true, true);
    if (child != null) {
      return child;
    } else {
      return rawCell;
    }
  }

  @Nullable
  public static EditorCell getCellForProperty(@Nullable EditorComponent editorComponent, @Nullable final SNode node, final String name) {
    if (name == null) {
      return null;
    }
    EditorCell rawCell = getRawCell(editorComponent, node);
    if (rawCell == null) {
      return null;
    }

    EditorCell child = CellFinderUtil.findChildByCondition(rawCell, cell -> isCellForProperty(cell, node, name), true, true);
    if (child == null) {
      return rawCell;
    }

    return child;
  }

  static boolean isCellForProperty(@NotNull EditorCell cell, @Nullable SNode node, @NotNull String name) {
    if (!(cell instanceof EditorCell_Property)) {
      return false;
    }

    EditorCell_Property propertyCell = (EditorCell_Property) cell;
    if (propertyCell.getRole() != null) {
      // Ignore property cells with a role since they do not display the property of their node but rather the property of the target node.
      return false;
    }

    ModelAccessor modelAccessor = propertyCell.getModelAccessor();
    return modelAccessor instanceof PropertyAccessor && node == propertyCell.getSNode()
           && name.equals(((PropertyAccessor) modelAccessor).getPropertyName());
  }

  @Nullable
  public static EditorCell getCellForChild(@Nullable EditorComponent editorComponent, @Nullable final SNode node, final String role) {
    EditorCell rawCell = getRawCell(editorComponent, node);
    if (rawCell == null) {
      return null;
    }
    EditorCell child = CellFinderUtil.findChildByCondition(rawCell, new Condition<EditorCell>() {
      @Override
      public boolean met(EditorCell cell) {
        return role.equals(cell.getRole()) &&
               (node == cell.getSNode() || node == cell.getSNode().getParent() && cell.isBig());
      }
    }, true, true);
    if (child != null) {
      return child;
    }
    if (editorComponent instanceof InspectorEditorComponent && rawCell.getParent() == null) {
      rawCell = null;
    }
    return rawCell;
  }
}
