/*
 * Copyright (c) 2015-2021 Alan Snyder.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the license agreement. For details see
 * accompanying license terms.
 */

package org.violetlib.aqua;

import java.awt.*;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.UIResource;

import org.jetbrains.annotations.Nullable;

/**
 * A contextual style popup that (if necessary) scrolls without using a scroll bar and scrolls by growing taller when
 * possible.
 */
public class AquaContextualPopup {

    private JComponent wrapper;
    private Popup p;
    private @Nullable MenuVibrantEffects effects;

    /**
     * Create a contextual style popup.
     * @param content The content to be displayed in the popup.
     * @param owner The owner of the popup. Needed for proper mouse grabbing if this popup is heavyweight.
     * @param installVibrantSelection If true, install support for highlighting the selection using a vibrant style.
     * @param selectedRegion An optional region of the content that should be initially visible. The view may be
     *                       scrolled to make the region visible.
     * @param selectedRegionLocation An optional preferred screen location for the selected region. The view may be
     *                               scrolled to place the region at this location.
     * @param x The desired X screen location of the popup.
     * @param y The desired Y screen location of the popup. If off the top of the screen, the popup will be relocated
     *          with scrolling enabled.
     * @param width If positive, the requested popup width.
     * @param height If positive, determines the requested bottom edge of the popup;
     */
    public AquaContextualPopup(JComponent content,
                               Component owner,
                               boolean installVibrantSelection,
                               Rectangle selectedRegion,
                               Point selectedRegionLocation,
                               int x, int y, int width, int height) {

        Border border = getContextualMenuBorder();
        Insets s = border.getBorderInsets(null);
        Dimension ps = content.getPreferredSize();

        if (width <= 0) {
            width = ps.width + s.left + s.right;
        }

        if (height <= 0) {
            height = ps.height + s.top + s.bottom;
        }

        Rectangle screen = AquaUtils.getScreenBounds(new Point(x, y), owner);

        if (y < screen.y) {
            int delta = screen.y - y;
            y = screen.y;
            height -= delta;
        }

        int screenWidth = screen.width;
        int screenHeight = screen.height;

        if (width > screenWidth) {
            width = screenWidth;
        }

        {
            int availableWidth = screen.x + screen.width - x;
            if (width > availableWidth) {
                x = screen.x + screen.width - width;
            }
        }

        if (height > screenHeight) {
            height = screenHeight;
        }

        {
            int availableHeight = screen.y + screen.height - y;
            if (height > availableHeight) {
                height = availableHeight;
            }
        }

        if (width < ps.width + s.left + s.right || height < ps.height + s.top + s.bottom) {
            if (selectedRegionLocation != null) {
                selectedRegionLocation = new Point(selectedRegionLocation.x - x, selectedRegionLocation.y - y);
            }
            wrapper = new MyScrollingWrapper(content, selectedRegion, selectedRegionLocation, width, height, border);
        } else {
            wrapper = new AquaBasicPopupMenuWrapper(content, border);
        }

        Border existingBorder = content.getBorder();
        if (existingBorder == null || existingBorder instanceof UIResource) {
            content.setBorder(null);
        }

        wrapper.putClientProperty(AquaVibrantSupport.POPUP_BACKGROUND_STYLE_KEY, "vibrantMenu");
        wrapper.putClientProperty(AquaVibrantSupport.POPUP_CORNER_RADIUS_KEY, 6);

        if (installVibrantSelection && content instanceof JPopupMenu) {
            JPopupMenu menu = (JPopupMenu) content;
            effects = new MenuVibrantEffects(wrapper, menu, AquaVibrantSupport.MENU_STYLE);
        }

        PopupFactory f = PopupFactory.getSharedInstance();
        p = f.getPopup(owner, wrapper, x, y);
    }

    public Popup getPopup() {
        return p;
    }

    public final void dispatchEvent(AWTEvent e) {
        wrapper.dispatchEvent(e);
    }

    public static Border getContextualMenuBorder() {
        return new EmptyBorder(5, 0, 5, 0);
    }

    protected class MyScrollingWrapper extends AquaScrollingPopupMenuWrapper {
        public MyScrollingWrapper(JComponent originalView, Rectangle selectedRegion, Point selectedRegionLocation,
                                  int width, int height, Border border) {
            super(originalView, selectedRegion, selectedRegionLocation, width, height, border);
            if (originalView instanceof JPopupMenu) {
                Object magicValue = AquaPopupMenuUI.getHidePopupKey();
                putClientProperty("doNotCancelPopup", magicValue);
                int count = getComponentCount();
                for (int i = 0; i < count; i++) {
                    JComponent cc = (JComponent) getComponent(i);
                    cc.putClientProperty("doNotCancelPopup", magicValue);
                }
            }
        }

        @Override
        protected void scroll(MouseEvent e, int delta) {
            // Before scrolling, try to enlarge the popup vertically
            Point loc = AquaUtils.getScreenLocation(wrapper);
            Rectangle screen = AquaUtils.getScreenBounds(loc, wrapper);
            Window w = SwingUtilities.getWindowAncestor(wrapper);
            if (w != null) {
                if (delta > 0) {
                    // scrolling up
                    int availableExtension = w.getY() - screen.y;
                    if (availableExtension > 0) {
                        int extension = Math.min(delta, availableExtension);
                        w.setBounds(w.getX(), w.getY() - extension, w.getWidth(), w.getHeight() + extension);
                        // The layout must be changed immediately to prevent excess scrolling on the next event
                        w.invalidate();
                        w.validate();
                        height += extension;
                        delta -= extension;
                        configure(true);
                    }
                } else {
                    // scrolling down
                    int availableExtension = screen.y + screen.height - (w.getY() + w.getHeight());
                    if (availableExtension > 0) {
                        int extension = Math.min(-delta, availableExtension);
                        w.setBounds(w.getX(), w.getY(), w.getWidth(), w.getHeight() + extension);
                        // The layout must be changed immediately to prevent excess scrolling on the next event
                        w.invalidate();
                        w.validate();
                        delta += extension;
                        height += extension;
                        Point vp = viewport.getViewPosition();
                        viewport.setViewPosition(new Point(vp.x, vp.y - extension));
                        configure(true);
                    }
                }

                if (delta != 0) {
                    super.scroll(e, delta);
                }

                if (e != null && originalContent instanceof AquaExtendedPopup) {
                    AquaExtendedPopup cbp = (AquaExtendedPopup) originalContent;
                    cbp.updateSelection(e);
                }
            }
        }
    }
}
