/*
 * Copyright 2003-2012 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.ide.ui.dialogs.properties.roots.editors;

import com.intellij.icons.AllIcons.Modules;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.Gray;
import com.intellij.ui.HoverHyperlinkLabel;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBPanel;
import com.intellij.ui.roots.FilePathClipper;
import com.intellij.ui.roots.IconActionComponent;
import com.intellij.ui.roots.ResizingWrapper;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.EventDispatcher;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.UIUtil;
import jetbrains.mps.extapi.persistence.FileBasedModelRoot;
import jetbrains.mps.extapi.persistence.SourceRootKind;
import jetbrains.mps.extapi.persistence.SourceRoot;
import jetbrains.mps.ide.vfs.VirtualFileUtils;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.vfs.path.Path;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.ui.persistence.ModelRootEntry;
import org.jetbrains.mps.openapi.ui.persistence.ModelRootEntryExt;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Stroke;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;

import static com.intellij.uiDesigner.core.GridConstraints.ANCHOR_EAST;
import static com.intellij.uiDesigner.core.GridConstraints.ANCHOR_NORTHWEST;
import static com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL;
import static com.intellij.uiDesigner.core.GridConstraints.FILL_NONE;
import static com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW;
import static com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK;
import static com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED;
import static jetbrains.mps.ide.ui.dialogs.properties.roots.editors.FileBasedModelRootEditor.getKindText;

public final class FileBasedModelRootEntry implements ModelRootEntry<FileBasedModelRoot>, ModelRootEntryExt {
  private final EventDispatcher<ModelRootEntryListener> myEventDispatcher = EventDispatcher.create(ModelRootEntryListener.class);
  private final FileBasedModelRoot myFileBasedModelRoot;
  private final Map<JComponent, Color> myComponentToForegroundMap = new HashMap<>();

  private FileBasedModelRootEditor myFileBasedModelRootEditor;

  public FileBasedModelRootEntry(@NotNull FileBasedModelRoot modelRoot) {
    myFileBasedModelRoot = modelRoot;
  }

  @NotNull
  @Override
  public FileBasedModelRoot getModelRoot() {
    return myFileBasedModelRoot;
  }

  @NotNull
  @Override
  public String getDetailsText() {
    return "";
  }

  @Nullable
  @Override
  public JComponent getDetailsComponent() {
    JBPanel panel = new JBPanel(new GridBagLayout());
    for (SourceRootKind kind : myFileBasedModelRoot.getSupportedFileKinds1()) {
      Collection<SourceRoot> sourceRoots = myFileBasedModelRoot.getSourceRoots(kind);

      if (!sourceRoots.isEmpty()) {
        final JComponent kindComponent = createKindGroupComponent(getKindText(kind), sourceRoots, getKindColor(kind));
        panel.add(kindComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new JBInsets(0, 0, 10, 0), 0, 0));
      }
    }
    panel.setBackground(UIUtil.isUnderDarcula() ? UIUtil.getPanelBackground() : ModelRootEntryContainer.SELECTED_CONTENT_COLOR);
    return panel;
  }

  @Override
  public void setForegroundColor(Color foregroundColor) {
    for (final JComponent component : myComponentToForegroundMap.keySet()) {
      component.setForeground(foregroundColor);
    }
  }

  @Override
  public void resetForegroundColor() {
    for (final JComponent component : myComponentToForegroundMap.keySet()) {
      component.setForeground(myComponentToForegroundMap.get(component));
    }
  }

  protected Color getKindColor(@NotNull SourceRootKind kind) {
    return kind.isExcluded() ? ModelRootEntryContainer.EXCLUDED_COLOR : ModelRootEntryContainer.SOURCES_COLOR;
  }

  protected Icon getKindIcon(SourceRootKind kind) {
    return kind.isExcluded() ? Modules.ExcludeRoot : Modules.SourceRoot;
  }

  protected JComponent createKindGroupComponent(String title, Collection<SourceRoot> sourceRoots, Color foregroundColor)   {
    if (sourceRoots.isEmpty()) {
      return new JBPanel<>();
    }
    final JBPanel<JBPanel> panel = new JBPanel<>(new GridLayoutManager(sourceRoots.size(), 3, new JBInsets(1, 17, 0, 2), 0, 1));
    panel.setOpaque(false);

    int idx = 0;
    for (SourceRoot sourceRoot : sourceRoots) {
      int verticalPolicy = (idx == (sourceRoots.size() - 1)) ? SIZEPOLICY_CAN_GROW : SIZEPOLICY_FIXED;
      panel.add(createKindFileComponent(sourceRoot, foregroundColor), new GridConstraints(idx, 0, 1, 1, ANCHOR_NORTHWEST, FILL_HORIZONTAL,
                                                                                          SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, verticalPolicy, null, null, null));
      int column = 1;
      int colspan = 2;

      panel.add(createKindFileDeleteComponent(sourceRoot), new GridConstraints(idx, column, 1, colspan, ANCHOR_EAST, FILL_NONE,
                                                                                     SIZEPOLICY_FIXED, verticalPolicy, null, null, null));
      idx++;
    }

    final JLabel titleLabel = new JLabel(title);
    final Font labelFont = UIUtil.getLabelFont();
    titleLabel.setFont(labelFont.deriveFont(Font.BOLD));
    titleLabel.setOpaque(false);
    titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
    registerTextComponent(titleLabel, foregroundColor);

    final JBPanel groupPanel = new JBPanel(new BorderLayout());
    groupPanel.setOpaque(false);
    groupPanel.add(titleLabel, BorderLayout.NORTH);
    groupPanel.add(panel, BorderLayout.CENTER);

    return groupPanel;
  }

  private JComponent createKindFileComponent(@NotNull SourceRoot sourceRoot, @NotNull Color foreground) {
    String pathPresentation = sourceRoot.getPath();
    if (pathPresentation.equals("")) {
      pathPresentation = ".";
    }

    JLabel label2Return = new JLabel(pathPresentation);

    VirtualFile virtualFile = VirtualFileUtils.getProjectVirtualFile(sourceRoot.getAbsolutePath());
    if (virtualFile != null && virtualFile.exists()) {
      HoverHyperlinkLabel hyperlinkLabel = new HoverHyperlinkLabel(pathPresentation, foreground);
      hyperlinkLabel.setMinimumSize(new Dimension(0, 0));
      hyperlinkLabel.addHyperlinkListener(e -> myFileBasedModelRootEditor.selectFile(sourceRoot.getAbsolutePath()));
      registerTextComponent(hyperlinkLabel, foreground);
      label2Return = hyperlinkLabel;
    }
    else {
      label2Return.setOpaque(false);
      label2Return.setForeground(Color.RED);
    }

    return new UnderlinedPathLabel(label2Return);
  }

  private JComponent createKindFileDeleteComponent(final SourceRoot sourceRoot) {
    final String tooltipText = "Remove";
    return new IconActionComponent(Modules.DeleteContentFolder, Modules.DeleteContentFolderRollover, tooltipText, () -> {
      myFileBasedModelRoot.removeSourceRoot(sourceRoot);
      updateUI();
    });
  }

  private void registerTextComponent(final JComponent component, final Color foreground) {
    component.setForeground(foreground);
    myComponentToForegroundMap.put(component, foreground);
  }

  @Override
  public boolean isValid() {
    try {
      IFile contentDirectory = myFileBasedModelRoot.getContentDirectory();
      return contentDirectory != null && contentDirectory.exists();
    } catch (Exception e) {
      return false;
    }
  }

  @NotNull
  @Override
  public FileBasedModelRootEditor getEditor() {
    if (myFileBasedModelRootEditor == null) {
      myFileBasedModelRootEditor = new FileBasedModelRootEditor();
      myFileBasedModelRootEditor.setFileBasedModelRootEntry(this);
    }

    return myFileBasedModelRootEditor;
  }

  @Override
  public void addModelRootEntryListener(@NotNull ModelRootEntryListener listener) {
    myEventDispatcher.addListener(listener);
  }

  @Override
  public void removeModelRootEntryListener(@NotNull ModelRootEntryListener listener) {
    myEventDispatcher.removeListener(listener);
  }

  public void updateUI() {
    myEventDispatcher.getMulticaster().fireDataChanged();
  }

  @Override
  public void dispose() {
  }

  private static class UnderlinedPathLabel extends ResizingWrapper {
    private static final float[] DASH = {0, 2, 0, 2};
    private static final Color DASH_LINE_COLOR = new JBColor(Gray._201, Gray._100);

    public UnderlinedPathLabel(JLabel wrappedComponent) {
      super(wrappedComponent);
      FilePathClipper.install(wrappedComponent, this);
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      final int startX = myWrappedComponent.getWidth();
      final int endX = getWidth();
      if (endX > startX) {
        final FontMetrics fontMetrics = myWrappedComponent.getFontMetrics(myWrappedComponent.getFont());
        final int y = fontMetrics.getMaxAscent();
        final Color savedColor = g.getColor();
        g.setColor(DASH_LINE_COLOR);
        drawDottedLine((Graphics2D)g, startX, y, endX, y);
        g.setColor(savedColor);
      }
    }

    private void drawDottedLine(Graphics2D g, int x1, int y1, int x2, int y2) {
      /*
      // TODO!!!
      final Color color = g.getColor();
      g.setColor(getBackground());
      g.setColor(color);
      for (int i = x1 / 2 * 2; i < x2; i += 2) {
        g.drawRect(i, y1, 0, 0);
      }
      */
      final Stroke saved = g.getStroke();
      if (!SystemInfo.isMac && !UIUtil.isUnderDarcula()) {
        g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, DASH, y1 % 2));
      }

      if (UIUtil.isUnderDarcula()) {
        UIUtil.drawDottedLine(g, x1, y1, x2, y2, null, g.getColor());
      } else {
        UIUtil.drawLine(g, x1, y1, x2, y2);
      }

      g.setStroke(saved);
    }
  }
}
