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

import jetbrains.mps.editor.runtime.style.StyleAttributes;
import jetbrains.mps.ide.icons.IconLoadHelper;
import jetbrains.mps.nodeEditor.EditorSettings;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.project.AbstractModule;
import jetbrains.mps.util.MacrosFactory;
import jetbrains.mps.vfs.FileSystem;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SModule;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.HashMap;
import java.util.Map;

public class EditorCell_Image extends EditorCell_Basic {
  private static Map<String, Icon> ourIconCache = new HashMap<String, Icon>();

  private ImageAlignment myAlignment = ImageAlignment.justify;
  private Icon myIcon;

  private int myDescent = -1;

  protected EditorCell_Image(EditorContext editorContext, SNode node) {
    super(editorContext, node);
    getStyle().set(StyleAttributes.PUNCTUATION_LEFT, true);
    getStyle().set(StyleAttributes.PUNCTUATION_RIGHT, true);
  }

  public static EditorCell_Image createImageCell(EditorContext editorContext, SNode node, String imageFileName) {
    return createImageCell(editorContext, node, getModule(node), imageFileName);
  }

  @Nullable
  private static SModule getModule(SNode node) {
    SModel model = node.getModel();
    if (model == null) {
      return null;
    }
    return model.getModule();
  }

  public static EditorCell_Image createImageCell(EditorContext editorContext, SNode node, @Nullable SModule imageModule, String imagePath) {
    EditorCell_Image result = new EditorCell_Image(editorContext, node);
    result.setIcon(loadIcon(editorContext, imageModule, imagePath));
    return result;
  }

  public static EditorCell_Image createImageCell(EditorContext editorContext, SNode node, Image image) {
    EditorCell_Image result = new EditorCell_Image(editorContext, node);
    result.setImage(image);
    return result;
  }

  @Override
  protected void paintContent(Graphics g, ParentSettings parentSettings) {
    if (myIcon == null) {
      return;
    }
    switch (myAlignment) {
      case justify: {
        if ((myWidth != -1 && myWidth != myIcon.getIconWidth())
            || (myHeight != -1 && myHeight != myIcon.getIconHeight())) {
          paintIconScaled(g);
        } else {
          myIcon.paintIcon(getEditor(), g, myX, myY);
        }
        break;
      }
      case center: {
        int x = myX + (myWidth - myIcon.getIconWidth()) / 2;
        int y = myY + (myHeight - myIcon.getIconHeight()) / 2;
        myIcon.paintIcon(getEditor(), g, x, y);
        break;
      }
      case title: {
        break;
      }
    }
  }

  private void paintIconScaled(Graphics g) {
    int iconWidth = myIcon.getIconWidth();
    int iconHeight = myIcon.getIconHeight();

    if (iconWidth <= 0 || iconHeight <= 0) {
      return;
    }

    double sx = (double) myWidth / (double) iconWidth;
    double sy = (double) myHeight / (double) iconHeight;
    Graphics2D gscaled = (Graphics2D) g.create();
    gscaled.translate(myX, myY);
    gscaled.scale(sx, sy);
    myIcon.paintIcon(getEditor(), gscaled, 0, 0);
  }

  @Override
  protected void relayoutImpl() {
    if (myIcon == null) {
      return;
    }
    if (myAlignment == ImageAlignment.justify) {
      int width = myIcon.getIconWidth();
      if (width != -1) {
        myWidth = width;
      }
      int height = myIcon.getIconHeight();
      if (height != -1) {
        myHeight = height;
      }
    }

    if (myDescent < 0) {
      myDescent = getFontMetrics().getDescent();
    }
  }

  private FontMetrics getFontMetrics() {
    return FontRegistry.getInstance().getFontMetrics(EditorSettings.getInstance().getDefaultEditorFont());
  }

  @Override
  public int getAscent() {
    return myHeight - getDescent();
  }

  @Override
  public int getDescent() {
    return myDescent >= 0 ? myDescent : 0;
  }

  public void setDescent(int descent) {
    myDescent = descent;
  }

  public void setAlignment(ImageAlignment alignment) {
    myAlignment = alignment;
  }

  protected void setImageFileName(String fileName) {
    if (fileName != null && FileSystem.getInstance().getFileByPath(fileName).exists()) {
      setImage(Toolkit.getDefaultToolkit().getImage(fileName));
    } else {
      setImage(null);
    }
  }

  private static Icon loadIcon(EditorContext context, SModule module, String iconPath) {
    String fullPath = MacrosFactory.forModule((AbstractModule) module).expandPath(iconPath);
    if (fullPath == null) {
      return null;
    }
    if (!ourIconCache.containsKey(fullPath)){
      ourIconCache.put(fullPath, IconLoadHelper.loadIcon(fullPath));
    }
    return ourIconCache.get(fullPath);
  }

  @Nullable
  private static AbstractModule toAbstractModule(SModule module) {
    if (!(module instanceof AbstractModule)) {
      return null;
    }
    return (AbstractModule) module;
  }

  protected void setImage(Image image) {
    setIcon(image == null ? null : new ImageIcon(image));
  }

  protected void setIcon(Icon icon) {
    myIcon = icon;
  }

  public Icon getIcon() {
    return myIcon;
  }

  public static enum ImageAlignment {
    justify, center, title;
  }
}
