/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.netbeans.swing.laf.flatlaf.ui;

import com.formdev.flatlaf.util.UIScale;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIManager;
import org.netbeans.swing.laf.flatlaf.HiDPIUtils;
import org.netbeans.swing.tabcontrol.TabDisplayer;
import org.netbeans.swing.tabcontrol.plaf.AbstractTabCellRenderer;
import org.netbeans.swing.tabcontrol.plaf.TabControlButton;
import org.netbeans.swing.tabcontrol.plaf.TabPainter;

/**
 * FlatLaf implementation of tab renderer
 */
public class FlatEditorTabCellRenderer extends AbstractTabCellRenderer {

    private static final int CLOSE_ICON_RIGHT_PAD = 2;

    private static final Color background = UIManager.getColor("EditorTab.background"); // NOI18N
    private static final Color activeBackground = Utils.getUIColor("EditorTab.activeBackground", background); // NOI18N
    private static final Color selectedBackground = Utils.getUIColor("EditorTab.selectedBackground", activeBackground); // NOI18N
    private static final Color hoverBackground = UIManager.getColor("EditorTab.hoverBackground"); // NOI18N
    private static final Color attentionBackground = UIManager.getColor("EditorTab.attentionBackground"); // NOI18N

    private static final Color foreground = Utils.getUIColor( "EditorTab.foreground", "TabbedPane.foreground" ); // NOI18N
    private static final Color activeForeground = Utils.getUIColor( "EditorTab.activeForeground", foreground ); // NOI18N
    private static final Color selectedForeground = Utils.getUIColor( "EditorTab.selectedForeground", activeForeground ); // NOI18N
    private static final Color hoverForeground = Utils.getUIColor( "EditorTab.hoverForeground", foreground ); // NOI18N
    private static final Color attentionForeground = Utils.getUIColor( "EditorTab.attentionForeground", foreground ); // NOI18N

    private static final Color underlineColor = UIManager.getColor("EditorTab.underlineColor"); // NOI18N
    private static final Color inactiveUnderlineColor = UIManager.getColor("EditorTab.inactiveUnderlineColor"); // NOI18N
    private static final Color tabSeparatorColor = UIManager.getColor("EditorTab.tabSeparatorColor"); // NOI18N
    private static final Color contentBorderColor = UIManager.getColor("TabbedContainer.editor.contentBorderColor"); // NOI18N

    private static final Insets tabInsets = UIScale.scale(UIManager.getInsets("EditorTab.tabInsets")); // NOI18N
    private static final int underlineHeight = UIScale.scale(UIManager.getInt("EditorTab.underlineHeight")); // NOI18N
    private static final boolean underlineAtTop = UIManager.getBoolean("EditorTab.underlineAtTop"); // NOI18N
    private static boolean showTabSeparators = UIManager.getBoolean("EditorTab.showTabSeparators"); // NOI18N

    private static final FlatTabPainter painter = new FlatTabPainter();

    boolean nextTabSelected;

    public FlatEditorTabCellRenderer() {
        super(painter, new Dimension(tabInsets.left + tabInsets.right, tabInsets.top + tabInsets.bottom));
    }

    @Override
    public Dimension getPadding() {
        Dimension d = super.getPadding();
        if (isShowCloseButton() && !Boolean.getBoolean("nb.tabs.suppressCloseButton")) { // NOI18N
            d.width += findCloseIcon().getIconWidth() + UIScale.scale(CLOSE_ICON_RIGHT_PAD);
        }
        return d;
    }

    @Override
    protected int getCaptionYAdjustment() {
        // Workaround for a issue in AbstractTabCellRenderer.paintIconAndText(Graphics),
        // which uses font height (which includes font descent) to calculate Y-coordinate
        // when available height is equal to font height (availH <= txtH),
        // but HtmlRenderer.renderString() expects Y-coordinate at baseline.
        // So the text is painted vertically out of center.
        //
        // This seems to be no issue with other LAFs because they seem to use
        // TabPainter insets differently and the available height is larger than
        // the font height (availH > txtH), in which case 3 pixels are removed from
        // the Y-coordinate to avoid that the text is painted vertically out of center.

        FontMetrics fm = getFontMetrics(getFont());
        int txtH = fm.getHeight();
        Insets ins = getInsets();
        int availH = getHeight() - (ins.top + ins.bottom);
        return (availH <= txtH) ? -fm.getDescent() : -1;
    }

    @Override
    protected int stateChanged(int oldState, int newState) {
        int result = super.stateChanged(oldState, newState);

        // set text color
        setForeground(colorForState(foreground, activeForeground, selectedForeground,
                hoverForeground, attentionForeground));

        return result;
    }

    private Color colorForState(Color normal, Color active, Color selected, Color hover, Color attention) {
        return isAttention() ? attention
                : isArmed() ? hover
                : isSelected() ? selected
                : isActive() ? active
                : normal;
    }

    private Icon findCloseIcon() {
        return FlatTabControlIcon.get(TabControlButton.ID_CLOSE_BUTTON,
                inCloseButton()
                        ? (isPressed() ? TabControlButton.STATE_PRESSED : TabControlButton.STATE_ROLLOVER)
                        : TabControlButton.STATE_DEFAULT);
    }

    private static class FlatTabPainter implements TabPainter {

        @Override
        public Insets getBorderInsets(Component c) {
            return tabInsets;
        }

        @Override
        public void getCloseButtonRectangle(JComponent jc, Rectangle rect, Rectangle bounds) {
            FlatEditorTabCellRenderer ren = (FlatEditorTabCellRenderer) jc;

            if (!ren.isShowCloseButton()) {
                rect.x = -100;
                rect.y = -100;
                rect.width = 0;
                rect.height = 0;
                return;
            }
            Icon icon = ren.findCloseIcon();
            int iconWidth = icon.getIconWidth();
            int iconHeight = icon.getIconHeight();
            rect.x = bounds.x + bounds.width - iconWidth - UIScale.scale(CLOSE_ICON_RIGHT_PAD);
            rect.y = bounds.y + (Math.max(0, bounds.height / 2 - iconHeight / 2)) - 1;
            rect.width = iconWidth;
            rect.height = iconHeight;
        }

        @Override
        public Polygon getInteriorPolygon(Component c) {
            int x = 0;
            int y = 0;
            int width = c.getWidth();
            int height = c.getHeight();

            //just a plain rectangle
            Polygon p = new Polygon();
            p.addPoint(x, y);
            p.addPoint(x + width, y);
            p.addPoint(x + width, y + height);
            p.addPoint(x, y + height);
            return p;
        }

        @Override
        public boolean isBorderOpaque() {
            return true;
        }

        @Override
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            // not using borders
        }

        @Override
        public void paintInterior(Graphics g, Component c) {
            // Paint the whole tab background at scale 1x (on HiDPI screens).
            // Necessary so that it aligns nicely at bottom left and right edges
            // with the content border, which is also painted at scale 1x.
            HiDPIUtils.paintAtScale1x(g, 0, 0, c.getWidth(), c.getHeight(),
                (gd, width, height, scale) -> {
                    paintInteriorAtScale1x(gd, c, width, height, scale);
                });

            // paint close button
            FlatEditorTabCellRenderer ren = (FlatEditorTabCellRenderer) c;
            if (!ren.isClipLeft() && !ren.isClipRight())
                paintCloseButton(g, ren);
        }

        private void paintInteriorAtScale1x(Graphics2D g, Component c, int width, int height, double scale) {
            FlatEditorTabCellRenderer ren = (FlatEditorTabCellRenderer) c;
            boolean selected = ren.isSelected();

            // get background color
            Color bg = ren.colorForState(
                    background, activeBackground, selectedBackground,
                    hoverBackground, attentionBackground);

            boolean showSeparator = showTabSeparators && !selected && !ren.nextTabSelected;

            // do not round tab separator width to get nice small lines at 125%, 150% and 175%
            int tabSeparatorWidth = showSeparator ? (int) (1 * scale) : 0;

            // paint background
            g.setColor(bg);
            g.fillRect(0, 0, width - (bg != background ? tabSeparatorWidth : 0), height);

            if (selected && underlineHeight > 0) {
                // paint underline if tab is selected
                int underlineHeight = (int) Math.round(FlatEditorTabCellRenderer.underlineHeight * scale);
                g.setColor(ren.isActive() ? underlineColor : inactiveUnderlineColor);
                if (underlineAtTop)
                    g.fillRect(0, 0, width - tabSeparatorWidth, underlineHeight);
                else
                    g.fillRect(0, height - underlineHeight, width - tabSeparatorWidth, underlineHeight);
            } else {
                // paint bottom border
                int contentBorderWidth = HiDPIUtils.deviceBorderWidth(scale, 1);
                g.setColor(contentBorderColor);
                g.fillRect(0, height - contentBorderWidth, width, contentBorderWidth);
            }

            if (showSeparator) {
                int offset = (int) (4 * scale);
                g.setColor(tabSeparatorColor);
                g.fillRect(width - tabSeparatorWidth, offset, tabSeparatorWidth, height - (offset * 2) - 1);
            }
        }

        private void paintCloseButton(Graphics g, FlatEditorTabCellRenderer ren) {
            //Get the close button bounds, more or less
            Rectangle r = new Rectangle();
            getCloseButtonRectangle(ren, r, new Rectangle(0, 0,
                                                          ren.getWidth(),
                                                          ren.getHeight()));

            if (!g.hitClip(r.x, r.y, r.width, r.height)) {
                return;
            }

            //paint close button
            Icon icon = ren.findCloseIcon();
            icon.paintIcon(ren, g, r.x, r.y);
        }

        @Override
        public boolean supportsCloseButton(JComponent renderer) {
            return renderer instanceof TabDisplayer ?
                ((TabDisplayer) renderer).isShowCloseButton() : true;
        }
    }
}
