/*
 * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.apple.laf;

import java.awt.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.UIResource;

import apple.laf.JRSUIState;
import apple.laf.JRSUIConstants.*;

import com.apple.laf.AquaUtils.RecyclableSingleton;

@SuppressWarnings("serial") // Superclass is not serializable across versions
public class AquaTableHeaderBorder extends AbstractBorder {
    protected static final int SORT_NONE = 0;
    protected static final int SORT_ASCENDING = 1;
    protected static final int SORT_DECENDING = -1;

    protected final Insets editorBorderInsets = new Insets(1, 3, 1, 3);
    protected final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIState.getInstance());

    protected static AquaTableHeaderBorder getListHeaderBorder() {
        // we don't want to share this, because the .setSelected() state
        // would persist to all other JTable instances
        return new AquaTableHeaderBorder();
    }

    protected AquaTableHeaderBorder() {
        painter.state.set(AlignmentHorizontal.LEFT);
        painter.state.set(AlignmentVertical.TOP);
    }

    /**
     * Paints the border for the specified component with the specified
     * position and size.
     * @param c the component for which this border is being painted
     * @param g the paint graphics
     * @param x the x position of the painted border
     * @param y the y position of the painted border
     * @param width the width of the painted border
     * @param height the height of the painted border
     */
    protected boolean doPaint = true;
    public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
        if (!doPaint) return;
        final JComponent jc = (JComponent)c;

        // if the developer wants to set their own color, we should
        // interpret this as "get out of the way", and don't draw aqua.
        final Color componentBackground = jc.getBackground();
        if (!(componentBackground instanceof UIResource)) {
            doPaint = false;
            jc.paint(g);
            getAlternateBorder().paintBorder(jc, g, x, y, width, height);
            doPaint = true;
            return;
        }

        final State state = getState(jc);
        painter.state.set(state);
        painter.state.set(jc.hasFocus() ? Focused.YES : Focused.NO);
        painter.state.set(height > 16 ? Widget.BUTTON_BEVEL : Widget.BUTTON_LIST_HEADER);
        painter.state.set(selected ? BooleanValue.YES : BooleanValue.NO);

        switch (sortOrder) {
            case SORT_ASCENDING:
                painter.state.set(Direction.UP);
                break;
            case SORT_DECENDING:
                painter.state.set(Direction.DOWN);
                break;
            default:
                painter.state.set(Direction.NONE);
                break;
        }

        final int newX = x;
        final int newY = y;
        final int newWidth = width;
        final int newHeight = height;

        painter.paint(g, c, newX - 1, newY - 1, newWidth + 1, newHeight + 1);

        // Draw the header
        g.clipRect(newX, y, newWidth, height);
        g.translate(fHorizontalShift, -1);
        doPaint = false;
        jc.paint(g);
        doPaint = true;
    }

    protected State getState(final JComponent jc) {
        if (!jc.isEnabled()) return State.DISABLED;

        final JRootPane rootPane = jc.getRootPane();
        if (rootPane == null) return State.ACTIVE;

        if (!AquaFocusHandler.isActive(rootPane)) return State.INACTIVE;

        return State.ACTIVE;
    }

    private static final RecyclableSingleton<Border> alternateBorder = new RecyclableSingleton<Border>() {
        @Override
        protected Border getInstance() {
            return BorderFactory.createRaisedBevelBorder();
        }
    };
    protected static Border getAlternateBorder() {
        return alternateBorder.get();
    }

    /**
     * Returns the insets of the border.
     * @param c the component for which this border insets value applies
     */
    public Insets getBorderInsets(final Component c) {
        // bad to create new one each time. For debugging only.
        return editorBorderInsets;
    }

    public Insets getBorderInsets(final Component c, final Insets insets) {
        insets.left = editorBorderInsets.left;
        insets.top = editorBorderInsets.top;
        insets.right = editorBorderInsets.right;
        insets.bottom = editorBorderInsets.bottom;
        return insets;
    }

    /**
     * Returns whether or not the border is opaque.  If the border
     * is opaque, it is responsible for filling in it's own
     * background when painting.
     */
    public boolean isBorderOpaque() {
        return false;
    }

    /**
     * Sets whether or not this instance of Border draws selected or not.  Used by AquaFileChooserUI
     */
    private boolean selected = false;
    protected void setSelected(final boolean inSelected) {
        selected = inSelected;
    }

    /**
     * Sets an amount to shift the position of the labels.  Used by AquaFileChooserUI
     */
    private int fHorizontalShift = 0;
    protected void setHorizontalShift(final int inShift) {
        fHorizontalShift = inShift;
    }

    private int sortOrder = SORT_NONE;
    protected void setSortOrder(final int inSortOrder) {
        if (inSortOrder < SORT_DECENDING || inSortOrder > SORT_ASCENDING) {
            throw new IllegalArgumentException("Invalid sort order constant: " + inSortOrder);
        }

        sortOrder = inSortOrder;
    }
}
