/*
 * Copyright 2000-2016 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 com.intellij.ui;

import com.intellij.icons.AllIcons.Ide.Notification.Shadow;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.IconLoader;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

/**
 * @author Alexander Lobas
 */
public class NotificationBalloonShadowBorderProvider implements BalloonImpl.ShadowBorderProvider {
  private static final Insets INSETS = new JBInsets(4, 6, 8, 6);
  private final Color myFillColor;
  private final Color myBorderColor;

  public NotificationBalloonShadowBorderProvider(@NotNull Color fillColor, @NotNull Color borderColor) {
    myFillColor = fillColor;
    myBorderColor = borderColor;
  }

  @NotNull
  @Override
  public Insets getInsets() {
    return INSETS;
  }

  @Override
  public void paintShadow(@NotNull JComponent component, @NotNull Graphics g) {
    int width = component.getWidth();
    int height = component.getHeight();

    int topLeftWidth = Shadow.Top_left.getIconWidth();
    int topLeftHeight = Shadow.Top_left.getIconHeight();

    int topRightWidth = Shadow.Top_right.getIconWidth();
    int topRightHeight = Shadow.Top_right.getIconHeight();

    int bottomLeftWidth = Shadow.Bottom_left.getIconWidth();
    int bottomLeftHeight = Shadow.Bottom_left.getIconHeight();

    int bottomRightWidth = Shadow.Bottom_right.getIconWidth();
    int bottomRightHeight = Shadow.Bottom_right.getIconHeight();

    int topWidth = Shadow.Top.getIconWidth();

    int bottomWidth = Shadow.Bottom.getIconWidth();
    int bottomHeight = Shadow.Bottom.getIconHeight();

    int leftHeight = Shadow.Left.getIconHeight();

    int rightWidth = Shadow.Right.getIconWidth();
    int rightHeight = Shadow.Right.getIconHeight();

    drawLine(component, g, Shadow.Top, width, topLeftWidth, topRightWidth, topWidth, 0, true);
    drawLine(component, g, Shadow.Bottom, width, bottomLeftWidth, bottomRightWidth, bottomWidth, height - bottomHeight, true);

    drawLine(component, g, Shadow.Left, height, topLeftHeight, bottomLeftHeight, leftHeight, 0, false);
    drawLine(component, g, Shadow.Right, height, topRightHeight, bottomRightHeight, rightHeight, width - rightWidth, false);

    Shadow.Top_left.paintIcon(component, g, 0, 0);
    Shadow.Top_right.paintIcon(component, g, width - topRightWidth, 0);
    Shadow.Bottom_right.paintIcon(component, g, width - bottomRightWidth, height - bottomRightHeight);
    Shadow.Bottom_left.paintIcon(component, g, 0, height - bottomLeftHeight);
  }

  private static void drawLine(@NotNull JComponent component,
                               @NotNull Graphics g,
                               @NotNull Icon icon,
                               int fullLength,
                               int start,
                               int end,
                               int step,
                               int start2,
                               boolean horizontal) {
    int length = fullLength - start - end;
    int count = length / step;
    int calcLength = step * count;
    int lastValue = start + calcLength;

    if (horizontal) {
      for (int i = start; i < lastValue; i += step) {
        icon.paintIcon(component, g, i, start2);
      }
    }
    else {
      for (int i = start; i < lastValue; i += step) {
        icon.paintIcon(component, g, start2, i);
      }
    }

    if (calcLength < length) {
      ImageIcon imageIcon = (ImageIcon)IconLoader.getIconSnapshot(icon);
      if (horizontal) {
        UIUtil.drawImage(g, imageIcon.getImage(), lastValue, start2, length - calcLength, imageIcon.getIconHeight(), component);
      }
      else {
        UIUtil.drawImage(g, imageIcon.getImage(), start2, lastValue, imageIcon.getIconWidth(), length - calcLength, component);
      }
    }
  }

  @Override
  public void paintBorder(@NotNull Rectangle bounds, @NotNull Graphics2D g) {
    g.setColor(myFillColor);
    g.fill(new Rectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height));
    g.setColor(myBorderColor);
    g.draw(new RoundRectangle2D.Double(bounds.x + 0.5, bounds.y + 0.5, bounds.width - 1, bounds.height - 1, 3, 3));
  }

  @Override
  public void paintPointingShape(@NotNull Rectangle bounds,
                                 @NotNull Point pointTarget,
                                 @NotNull Balloon.Position position,
                                 @NotNull Graphics2D g) {
    int x, y, length;

    if (position == Balloon.Position.above) {
      length = INSETS.bottom;
      x = pointTarget.x;
      y = bounds.y + bounds.height + length;
    }
    else if (position == Balloon.Position.below) {
      length = INSETS.top;
      x = pointTarget.x;
      y = bounds.y - length;
    }
    else if (position == Balloon.Position.atRight) {
      length = INSETS.left;
      x = bounds.x - length;
      y = pointTarget.y;
    }
    else {
      length = INSETS.right;
      x = bounds.x + bounds.width + length;
      y = pointTarget.y;
    }

    Polygon p = new Polygon();
    p.addPoint(x, y);

    length += 2;
    if (position == Balloon.Position.above) {
      p.addPoint(x - length, y - length);
      p.addPoint(x + length, y - length);
    }
    else if (position == Balloon.Position.below) {
      p.addPoint(x - length, y + length);
      p.addPoint(x + length, y + length);
    }
    else if (position == Balloon.Position.atRight) {
      p.addPoint(x + length, y - length);
      p.addPoint(x + length, y + length);
    }
    else {
      p.addPoint(x - length, y - length);
      p.addPoint(x - length, y + length);
    }

    g.setColor(myFillColor);
    g.fillPolygon(p);

    g.setColor(myBorderColor);

    length -= 2;
    if (position == Balloon.Position.above) {
      g.drawLine(x, y, x - length, y - length);
      g.drawLine(x, y, x + length, y - length);
    }
    else if (position == Balloon.Position.below) {
      g.drawLine(x, y, x - length, y + length);
      g.drawLine(x, y, x + length, y + length);
    }
    else if (position == Balloon.Position.atRight) {
      g.drawLine(x, y, x + length, y - length);
      g.drawLine(x, y, x + length, y + length);
    }
    else {
      g.drawLine(x, y, x - length, y - length);
      g.drawLine(x, y, x - length, y + length);
    }
  }
}