/*
 * 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;

import jetbrains.mps.errors.MessageStatus;
import jetbrains.mps.errors.QuickFixProvider;
import jetbrains.mps.nodeEditor.inspector.InspectorEditorComponent;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.message.EditorMessageOwner;
import jetbrains.mps.openapi.editor.message.SimpleEditorMessage;
import jetbrains.mps.smodel.ModelAccessHelper;
import org.jetbrains.mps.openapi.model.SNode;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Cyril.Konopko, 01.04.2008
 */
public class DefaultEditorMessage implements EditorMessage {
  private static final int DEFAULT_MESSAGE_PRIORITY = 0;

  private Color myColor;
  private String myMessage;
  private EditorMessageOwner myOwner;
  private SNode myNode;
  private List<QuickFixProvider> myIntentionProviders;
  private MessageStatus myStatus = MessageStatus.OK;

  private Map<Object, Object> myUserObjects;

  public DefaultEditorMessage(SNode node, Color color, String message, EditorMessageOwner owner) {
    myNode = node;
    myColor = color;
    myMessage = message;
    myOwner = owner;
  }

  public DefaultEditorMessage(SNode node, MessageStatus status, Color color, String message, EditorMessageOwner owner) {
    this(node, color, message, owner);
    myStatus = status;
  }

  @Override
  public boolean sameAs(SimpleEditorMessage message) {
    return message.getNode() == getNode() && getOwner() == message.getOwner() &&
        getStatus() == message.getStatus() && getMessage().equals(message.getMessage());
  }

  @Override
  public void putUserObject(Object key, Object value) {
    if (myUserObjects == null) {
      myUserObjects = new HashMap<>(1);
    }
    myUserObjects.put(key, value);
  }

  @Override
  public Object getUserObject(Object key) {
    if (myUserObjects == null) {
      return null;
    }
    return myUserObjects.get(key);
  }

  @Override
  public String getMessage() {
    return myMessage;
  }

  @Override
  public Color getColor() {
    return myColor;
  }

  @Override
  public EditorMessageOwner getOwner() {
    return myOwner;
  }

  @Override
  public boolean isValid(EditorComponent editorComponent) {
    return getCellInBothWays(editorComponent) != null;
  }

  @Override
  public int getStart(jetbrains.mps.openapi.editor.EditorComponent editorComponent) {
    EditorCell editorCell = getCellInBothWays((EditorComponent) editorComponent);
    if (editorCell == null) {
      return -1;
    }
    return editorCell.getY();
  }

  @Override
  public int getHeight(jetbrains.mps.openapi.editor.EditorComponent editorComponent) {
    EditorCell editorCell = getCellInBothWays((EditorComponent) editorComponent);
    if (editorCell == null) {
      return -1;
    }
    return editorCell.getHeight();
  }

  @Override
  public void doNavigate(EditorComponent editorComponent) {
    editorComponent.changeSelection(getCellInBothWays(editorComponent));
  }

  protected EditorCell getCellInBothWays(final EditorComponent editor) {
    return new ModelAccessHelper(editor.getRepository()).runReadAction(() -> {
      EditorCell editorCell = getCell(editor);
      if (editorCell != null) {
        return editorCell;
      }
      return getCellForParentNodeInMainEditor(editor);
    });
  }

  @Override
  public MessageStatus getStatus() {
    return myStatus;
  }

  @Override
  public EditorCell getCell(EditorComponent editor) {
    if (editor == null) {
      return null;
    }
    return editor.getBigValidCellForNode(getNode());
  }

  @Override
  public EditorCell getCellForParentNodeInMainEditor(final EditorComponent editor) {
    return new ModelAccessHelper(editor.getRepository()).runReadAction(() -> {
      if (getNode() == null) {
        return null;
      }
      if (editor instanceof InspectorEditorComponent) {
        return null;
      }
      SNode parent = getNode().getParent();
      while (parent != null) {
        EditorCell result = editor.getBigValidCellForNode(parent);
        if (result != null) {
          return result;
        }
        parent = parent.getParent();
      }
      return null;
    });
  }

  @Override
  public boolean acceptCell(EditorCell cell, EditorComponent editor) {
    if (cell == null) {
      return false;
    }
    return cell.isBig() && editor.isValid(cell) && cell.getSNode() == getNode();
  }

  @Override
  public SNode getNode() {
    return myNode;
  }

  @Override
  public boolean showInEditor() {
    return true;
  }

  @Override
  public void paint(Graphics g, EditorComponent editorComponent, EditorCell cell) {
    paintWithColor(g, cell, getColor());
  }

  protected void paintWithColor(Graphics g, EditorCell cell, Color color) {
    int x = cell.getX() + cell.getLeftInset();
    int y = cell.getY();
    int width = cell.getWidth() - cell.getLeftInset() - cell.getRightInset() - 1;
    int height = cell.getHeight() - cell.getTopInset() - cell.getBottomInset() - 1;
    g.setColor(color);
    g.drawRect(x, y, width, height);
    color = new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 5);
    g.setColor(color);
    g.fillRect(x, y, width, height);
  }

  @Override
  public boolean isBackground() {
    return false;
  }

  public void setIntentionProvider(QuickFixProvider intentionProvider) {
    addIntentionProvider(intentionProvider);
  }

  public void addIntentionProvider(QuickFixProvider intentionProvider) {
    if (myIntentionProviders == null) {
      myIntentionProviders = new ArrayList<>(1);
    }
    myIntentionProviders.add(intentionProvider);
  }

  @Override
  public QuickFixProvider getIntentionProvider() {
    if (myIntentionProviders == null) {
      return null;
    }
    if (myIntentionProviders.isEmpty()) {
      return null;
    }
    return myIntentionProviders.get(0);
  }

  @Override
  public List<QuickFixProvider> getIntentionProviders() {
    ArrayList<QuickFixProvider> result = new ArrayList<>(1);
    if (myIntentionProviders != null) {
      result.addAll(myIntentionProviders);
    }
    return result;
  }

  @Override
  public int getPriority() {
    return DEFAULT_MESSAGE_PRIORITY;
  }

  @Override
  public boolean showInGutter() {
    return true;
  }
}
