/*
 * Copyright 2000-2009 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 org.intellij.lang.annotations.JdkConstants;

import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.awt.*;

/**
 * @author Konstantin Bulenkov
 */
public class TitledBorderWithMnemonic extends TitledBorder {
  private final String myOriginalTitle;

  public TitledBorderWithMnemonic(String title) {
    this(null, title, LEADING, DEFAULT_POSITION, null, null);
  }

  public TitledBorderWithMnemonic(Border border) {
    this(border, "", LEADING, DEFAULT_POSITION, null, null);
  }

  public TitledBorderWithMnemonic(Border border, String title) {
    this(border, title, LEADING, DEFAULT_POSITION, null, null);
  }

  public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition) {
    this(border, title, titleJustification, titlePosition, null, null);
  }

  public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition, Font titleFont) {
    this(border, title, titleJustification, titlePosition, titleFont, null);
  }

  public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition, Font titleFont,
                                  Color titleColor) {
    super(border, title.replaceAll("&", ""), titleJustification, titlePosition, titleFont, titleColor);
    myOriginalTitle = title;
  }

  @Override
  public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
    Point textLoc = new Point();
    Border border = getBorder();

    if (getTitle() == null || getTitle().length() == 0) {
        if (border != null) {
            border.paintBorder(c, g, x, y, width, height);
        }
        return;
    }

    Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
                                         width - (EDGE_SPACING * 2),
                                         height - (EDGE_SPACING * 2));
    Font font = g.getFont();
    Color color = g.getColor();

    g.setFont(getFont(c));

    FontMetrics fm = g.getFontMetrics();
    int         fontHeight = fm.getHeight();
    int         descent = fm.getDescent();
    int         ascent = fm.getAscent();
    int         diff;
    int         stringWidth = fm.stringWidth(getTitle());
    Insets      insets;

    if (border != null) {
        insets = border.getBorderInsets(c);
    } else {
        insets = new Insets(0, 0, 0, 0);
    }

    int titlePos = getTitlePosition();
    switch (titlePos) {
      case ABOVE_TOP -> {
        diff = ascent + descent + (Math.max(EDGE_SPACING,
                                            TEXT_SPACING * 2) - EDGE_SPACING);
        grooveRect.y += diff;
        grooveRect.height -= diff;
        textLoc.y = grooveRect.y - (descent + TEXT_SPACING);
      }
      case TOP, DEFAULT_POSITION -> {
        diff = Math.max(0, ((ascent / 2) + TEXT_SPACING) - EDGE_SPACING);
        grooveRect.y += diff;
        grooveRect.height -= diff;
        textLoc.y = (grooveRect.y - descent) +
                    (insets.top + ascent + descent) / 2;
      }
      case BELOW_TOP -> textLoc.y = grooveRect.y + insets.top + ascent + TEXT_SPACING;
      case ABOVE_BOTTOM -> textLoc.y = (grooveRect.y + grooveRect.height) -
                                       (insets.bottom + descent + TEXT_SPACING);
      case BOTTOM -> {
        grooveRect.height -= fontHeight / 2;
        textLoc.y = ((grooveRect.y + grooveRect.height) - descent) +
                    ((ascent + descent) - insets.bottom) / 2;
      }
      case BELOW_BOTTOM -> {
        grooveRect.height -= fontHeight;
        textLoc.y = grooveRect.y + grooveRect.height + ascent +
                    TEXT_SPACING;
      }
    }

    int justification = getTitleJustification();
    if(c.getComponentOrientation().isLeftToRight()) {
        if(justification==LEADING ||
           justification==DEFAULT_JUSTIFICATION) {
            justification = LEFT;
        }
        else if(justification==TRAILING) {
            justification = RIGHT;
        }
    }
    else {
        if(justification==LEADING ||
           justification==DEFAULT_JUSTIFICATION) {
            justification = RIGHT;
        }
        else if(justification==TRAILING) {
            justification = LEFT;
        }
    }

    switch (justification) {
      case LEFT -> textLoc.x = grooveRect.x + TEXT_INSET_H + insets.left;
      case RIGHT -> textLoc.x = (grooveRect.x + grooveRect.width) -
                                (stringWidth + TEXT_INSET_H + insets.right);
      case CENTER -> textLoc.x = grooveRect.x +
                                 ((grooveRect.width - stringWidth) / 2);
    }

    // If title is positioned in middle of border AND its fontsize
    // is greater than the border's thickness, we'll need to paint
    // the border in sections to leave space for the component's background
    // to show through the title.
    //
    if (border != null) {
        if (((titlePos == TOP || titlePos == DEFAULT_POSITION) &&
              (grooveRect.y > textLoc.y - ascent)) ||
             (titlePos == BOTTOM &&
              (grooveRect.y + grooveRect.height < textLoc.y + descent))) {

            Rectangle clipRect = new Rectangle();

            // save original clip
            Rectangle saveClip = g.getClipBounds();

            // paint strip left of text
            clipRect.setBounds(saveClip);
            if (computeIntersection2(clipRect, x, y, textLoc.x-1-x, height)) {
                g.setClip(clipRect);
                border.paintBorder(c, g, grooveRect.x, grooveRect.y,
                              grooveRect.width, grooveRect.height);
            }

            // paint strip right of text
            clipRect.setBounds(saveClip);
            if (computeIntersection2(clipRect, textLoc.x+stringWidth+1, y,
                           x+width-(textLoc.x+stringWidth+1), height)) {
                g.setClip(clipRect);
                border.paintBorder(c, g, grooveRect.x, grooveRect.y,
                              grooveRect.width, grooveRect.height);
            }

            if (titlePos == TOP || titlePos == DEFAULT_POSITION) {
                // paint strip below text
                clipRect.setBounds(saveClip);
                if (computeIntersection2(clipRect, textLoc.x-1, textLoc.y+descent,
                                    stringWidth+2, y+height-textLoc.y-descent)) {
                    g.setClip(clipRect);
                    border.paintBorder(c, g, grooveRect.x, grooveRect.y,
                              grooveRect.width, grooveRect.height);
                }

            } else { // titlePos == BOTTOM
              // paint strip above text
                clipRect.setBounds(saveClip);
                if (computeIntersection2(clipRect, textLoc.x-1, y,
                      stringWidth+2, textLoc.y - ascent - y)) {
                    g.setClip(clipRect);
                    border.paintBorder(c, g, grooveRect.x, grooveRect.y,
                              grooveRect.width, grooveRect.height);
                }
            }

            // restore clip
            g.setClip(saveClip);

        } else {
            border.paintBorder(c, g, grooveRect.x, grooveRect.y,
                              grooveRect.width, grooveRect.height);
        }
    }

    g.setColor(getTitleColor());
    g.drawString(getTitle(), textLoc.x, textLoc.y);

    final int index = myOriginalTitle.indexOf('&');
    if (index != -1 && index != myOriginalTitle.length() - 1 && index == myOriginalTitle.lastIndexOf('&') && g instanceof Graphics2D) {
      int x0 = fm.stringWidth(getTitle().substring(0, index));
      int x1 = fm.stringWidth(getTitle().substring(0, index+1));
      ((Graphics2D)g).setPaint(getTitleColor());
      g.drawLine(textLoc.x + x0 - 1, textLoc.y + 1, textLoc.x + x1 - 1, textLoc.y + 1);
      ((Graphics2D)g).setPaint(color);
    }

    g.setFont(font);
    g.setColor(color);
  }

  private static boolean computeIntersection2(Rectangle dest,
                                             int rx, int ry, int rw, int rh) {
      int x1 = Math.max(rx, dest.x);
      int x2 = Math.min(rx + rw, dest.x + dest.width);
      int y1 = Math.max(ry, dest.y);
      int y2 = Math.min(ry + rh, dest.y + dest.height);
      dest.x = x1;
      dest.y = y1;
      dest.width = x2 - x1;
      dest.height = y2 - y1;

      if (dest.width <= 0 || dest.height <= 0) {
          return false;
      }
      return true;
  }
}
