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

import com.intellij.ide.IdeTooltipManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.BalloonBuilder;
import com.intellij.openapi.ui.popup.JBPopupAdapter;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.BalloonImpl;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class BalloonPopupBuilderImpl implements BalloonBuilder {
  @Nullable private final Map<Disposable, List<Balloon>> myStorage;
  @Nullable private Disposable myAnchor;

  private final JComponent myContent;

  private Color   myBorder             = IdeTooltipManager.getInstance().getBorderColor(true);
  @Nullable private Insets myBorderInsets = null;
  private Color   myFill               = MessageType.INFO.getPopupBackground();
  private boolean myHideOnMouseOutside = true;
  private boolean myHideOnKeyOutside   = true;
  private long    myFadeoutTime        = -1;
  private boolean myShowCallout        = true;
  private boolean myCloseButtonEnabled = false;
  private boolean myHideOnFrameResize  = true;
  private boolean myHideOnLinkClick    = false;

  private ActionListener myClickHandler;
  private boolean        myCloseOnClick;
  private int myAnimationCycle = 500;

  private int myCalloutShift;
  private int myPositionChangeXShift;
  private int myPositionChangeYShift;
  private boolean myHideOnAction = true;
  private boolean myHideOnCloseClick = true;
  private boolean myDialogMode;
  private String  myTitle;
  private Insets  myContentInsets = JBUI.insets(2);
  private boolean myShadow        = UIUtil.isUnderDarcula();
  private boolean mySmallVariant  = false;

  private Balloon.Layer myLayer;
  private boolean myBlockClicks = false;
  private boolean myRequestFocus = false;

  private Dimension myPointerSize;
  private int       myCornerToPointerDistance = -1;

  public BalloonPopupBuilderImpl(@Nullable Map<Disposable, List<Balloon>> storage, @NotNull final JComponent content) {
    myStorage = storage;
    myContent = content;
  }

  @NotNull
  @Override
  public BalloonBuilder setHideOnAction(boolean hideOnAction) {
    myHideOnAction = hideOnAction;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setDialogMode(boolean dialogMode) {
    myDialogMode = dialogMode;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setBorderColor(@NotNull final Color color) {
    myBorder = color;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setBorderInsets(@Nullable Insets insets) {
    myBorderInsets = insets;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setFillColor(@NotNull final Color color) {
    myFill = color;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setHideOnClickOutside(final boolean hide) {
    myHideOnMouseOutside  = hide;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setHideOnKeyOutside(final boolean hide) {
    myHideOnKeyOutside = hide;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setShowCallout(final boolean show) {
    myShowCallout = show;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setFadeoutTime(long fadeoutTime) {
    myFadeoutTime = fadeoutTime;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setBlockClicksThroughBalloon(boolean block) {
    myBlockClicks = block;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setRequestFocus(boolean requestFocus) {
    myRequestFocus = requestFocus;
    return this;
  }

  @Override
  public BalloonBuilder setHideOnCloseClick(boolean hideOnCloseClick) {
    myHideOnCloseClick = hideOnCloseClick;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setAnimationCycle(int time) {
    myAnimationCycle = time;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setHideOnFrameResize(boolean hide) {
    myHideOnFrameResize = hide;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setHideOnLinkClick(boolean hide) {
    myHideOnLinkClick = hide;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setPositionChangeXShift(int positionChangeXShift) {
    myPositionChangeXShift = positionChangeXShift;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setPositionChangeYShift(int positionChangeYShift) {
    myPositionChangeYShift = positionChangeYShift;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setCloseButtonEnabled(boolean enabled) {
    myCloseButtonEnabled = enabled;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setClickHandler(ActionListener listener, boolean closeOnClick) {
    myClickHandler = listener;
    myCloseOnClick = closeOnClick;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setCalloutShift(int length) {
    myCalloutShift = length;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setTitle(@Nullable String title) {
    myTitle = title;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setContentInsets(Insets insets) {
    myContentInsets = insets;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setShadow(boolean shadow) {
    myShadow = shadow;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setSmallVariant(boolean smallVariant) {
    mySmallVariant = smallVariant;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setLayer(Balloon.Layer layer) {
    myLayer = layer;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setDisposable(@NotNull Disposable anchor) {
    myAnchor = anchor;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setPointerSize(Dimension size) {
    myPointerSize = size;
    return this;
  }

  @NotNull
  @Override
  public BalloonBuilder setCornerToPointerDistance(int distance) {
    myCornerToPointerDistance = distance;
    return this;
  }

  @NotNull
  @Override
  public Balloon createBalloon() {
    final BalloonImpl result = new BalloonImpl(
      myContent, myBorder, myBorderInsets, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myHideOnCloseClick,
      myShowCallout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myHideOnLinkClick, myClickHandler, myCloseOnClick,
      myAnimationCycle, myCalloutShift, myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle, myContentInsets, myShadow,
      mySmallVariant, myBlockClicks, myLayer, myRequestFocus, myPointerSize, myCornerToPointerDistance);

    if (myStorage != null && myAnchor != null) {
      List<Balloon> balloons = myStorage.get(myAnchor);
      if (balloons == null) {
        myStorage.put(myAnchor, balloons = new ArrayList<>());
        Disposer.register(myAnchor, new Disposable() {
          @Override
          public void dispose() {
            List<Balloon> toDispose = myStorage.remove(myAnchor);
            if (toDispose != null) {
              for (Balloon balloon : toDispose) {
                if (!balloon.isDisposed()) {
                  Disposer.dispose(balloon);
                }
              }
            }
          }
        });
      }
      balloons.add(result);
      result.addListener(new JBPopupAdapter() {
        @Override
        public void onClosed(LightweightWindowEvent event) {
          if (!result.isDisposed()) {
            Disposer.dispose(result);
          }
        }
      });
    }

    return result;
  }
}
