import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * please read...
 * 
 * THIS IS CODEX'S INTRO:
 * Displays a hand of numbered cards that can be raised and moved upstairs.
 * <p>
 * Codex (OpenAI) generated and tested this program from the requirements
 * and Gemini's video evidence supplied by [student name]. [Student] supplied
 * the reconstruction instructions and author information.
 * The upper cards retain click order and overlap like the bottom hand;
 * these choices were not confirmed by the video evidence.
 *
 * 
 * HUMAN AGAIN:
 * I first used ChatGPT to plan the reconstruction process and determine what
 * information needed to be extracted from the demonstration. ChatGPT was also
 * used to create detailed prompts for the other models and to review their
 * outputs for inconsistencies or unsupported assumptions.
 * 
 * 
 * I then provided the original video to Gemini. Gemini analyzed the full
 * demonstration and produced a transcript, a description of the GUI and its
 * behavior, observed input/output behavior, approximate component geometry,
 * interaction details, and an initial attempt at recreating Solution3.java.
 * 
 * 
 * Gemini was then given a second, more targeted analysis prompt to verify
 * uncertain details such as card dimensions, overlapping behavior, hover
 * priority, window resizing, centering, and the behavior of multiple cards
 * after they were clicked.
 * 
 * 
 * ChatGPT reviewed both Gemini outputs, identified problems in the initial
 * implementation, and combined the observations into a single evidence file.
 * In particular, Gemini's first implementation incorrectly assumed that only
 * one selected card existed in the upper area, while the second video analysis
 * confirmed that multiple selected cards accumulate there.
 * 
 * 
 * The complete Gemini evidence, assignment requirements, and your coding
 * and formatting rules were then provided as ".md" files to Codex using GPT-6 Astra. 
 * Astra was instructed to treat Gemini's generated Java code only as an untrusted first
 * attempt and instead reconstruct the program from the observed behavior,
 * verified visual details, and instructor requirements.
 * 
 * 
 * The resulting Solution3.java was compiled, run, and visually compared
 * against the original demonstration. The final result appears to reproduce
 * the demonstrated GUI and behavior very closely.
 * 
 * This Result aside from this comment block was completed in one prompt, and the code was not modified after generation.
 * 
 * Extra Notes About this: 
 * I do quite a bit of coding outside of class, so Codex has seen a lot of how I 
 * normally structure code, work through logic, and write comments (Memory Saving). Because of that, 
 * some of the generated code may look more like my usual style than typical AI output, 
 * especially since I asked it to stick close to how I normally write things.
 * 

 * 
 * @author [redacted]
 */
public class Solution3 extends JPanel {
    private static final int WINDOW_WIDTH = 650;
    private static final int WINDOW_HEIGHT = 450;
    private static final int MIN_HAND_SIZE = 5;
    private static final int MAX_HAND_SIZE = 10;
    private static final int MAX_CARD_VALUE = 20;
    private static final int CARD_WIDTH = 45;
    private static final int CARD_HEIGHT = 75;
    private static final int CARD_STEP = 32;
    private static final int CORNER_DIAMETER = 10;
    private static final Color CARD_COLOR = new Color(220, 220, 220);
    private static final int HOVER_RISE = 15;
    private static final int BOTTOM_MARGIN = 50;
    private static final int FONT_SIZE = 13;

    private final ArrayList<Integer> handCards = new ArrayList<>();
    private final ArrayList<Integer> upperCards = new ArrayList<>();
    private final Random random = new Random();
    private int hoveredIndex = -1;

    /**
     * Creates and displays the initially empty Cards window.
     *
     * @throws java.awt.HeadlessException if no display is available
     */
    public Solution3() {
        // Create the window and its four menu commands.
        JFrame window = new JFrame("Cards");
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");
        JMenuItem newItem = new JMenuItem("New");
        JMenuItem new5Item = new JMenuItem("New 5");
        JMenuItem new10Item = new JMenuItem("New 10");
        JMenuItem exitItem = new JMenuItem("Exit");

        // Connect each menu command to its operation.
        newItem.addActionListener(event -> generateHand(MIN_HAND_SIZE
                + random.nextInt(MAX_HAND_SIZE - MIN_HAND_SIZE + 1)));
        new5Item.addActionListener(event -> generateHand(MIN_HAND_SIZE));
        new10Item.addActionListener(event -> generateHand(MAX_HAND_SIZE));
        exitItem.addActionListener(event -> System.exit(0));

        // Add the commands in their demonstrated order.
        fileMenu.add(newItem);
        fileMenu.add(new5Item);
        fileMenu.add(new10Item);
        fileMenu.addSeparator();
        fileMenu.add(exitItem);
        menuBar.add(fileMenu);

        // Track movement, departure, and clicks on the bottom hand.
        MouseAdapter mouseHandler = new MouseAdapter() {
            /**
             * Raises the topmost card under the pointer.
             *
             * @param event the mouse movement
             */
            @Override
            public void mouseMoved(MouseEvent event) {
                hoveredIndex = cardIndex(event.getPoint());
                repaint();
            }

            /**
             * Lowers the raised card when the pointer leaves the panel.
             *
             * @param event the mouse departure
             */
            @Override
            public void mouseExited(MouseEvent event) {
                hoveredIndex = -1;
                repaint();
            }

            /**
             * Moves the clicked card into the upper collection.
             *
             * @param event the mouse click
             */
            @Override
            public void mouseClicked(MouseEvent event) {
                // Find the topmost card at the click position.
                int clickedIndex = cardIndex(event.getPoint());

                // Transfer that card and close the gap in the hand.
                if (clickedIndex >= 0) {
                    upperCards.add(handCards.remove(clickedIndex));
                    hoveredIndex = -1;
                    repaint();
                }
            }
        };

        addMouseListener(mouseHandler);
        addMouseMotionListener(mouseHandler);

        // Show the resizable window with a plain card font.
        setFont(new Font(Font.SANS_SERIF, Font.PLAIN, FONT_SIZE));
        window.setJMenuBar(menuBar);
        window.setContentPane(this);
        window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setLocationRelativeTo(null);
        window.setVisible(true);
    }

    /**
     * Replaces both collections with a new, sorted bottom hand.
     *
     * @param cardCount the number of cards to generate, from 5 through 10
     */
    private void generateHand(int cardCount) {
        // Clear the previous hand, upper collection, and hover state.
        handCards.clear();
        upperCards.clear();
        hoveredIndex = -1;

        // Choose distinct values from 1 through 20.
        while (handCards.size() < cardCount) {
            int cardValue = random.nextInt(MAX_CARD_VALUE) + 1;

            if (!handCards.contains(cardValue)) {
                handCards.add(cardValue);
            }
        }

        // Put the new hand in numeric order and display it.
        Collections.sort(handCards);
        repaint();
    }

    /**
     * Paints both collections with right-hand cards above left-hand cards.
     *
     * @param graphics the drawing context for the panel
     */
    @Override
    protected void paintComponent(Graphics graphics) {
        // Clear the panel before drawing the current collections.
        super.paintComponent(graphics);

        // Center the upper cards vertically within the panel's upper half.
        int upperLeft = groupStart(upperCards.size());
        int upperTop = Math.max(0, (getHeight() / 2 - CARD_HEIGHT) / 2);

        for (int i = 0; i < upperCards.size(); ++i) {
            drawCard(graphics, upperCards.get(i),
                    upperLeft + i * CARD_STEP, upperTop);
        }

        // Draw the bottom hand, raising only the hovered card.
        int handLeft = groupStart(handCards.size());

        for (int i = 0; i < handCards.size(); ++i) {
            int cardTop = bottomY();

            if (i == hoveredIndex) {
                cardTop -= HOVER_RISE;
            }
            drawCard(graphics, handCards.get(i),
                    handLeft + i * CARD_STEP, cardTop);
        }
    }

    /**
     * Finds the left edge that centers the entire visible row.
     *
     * @param cardCount the number of cards in the row
     * @return the row's left edge, or the panel center for an empty row
     */
    private int groupStart(int cardCount) {
        int visibleWidth = 0;

        if (cardCount > 0) {
            visibleWidth = CARD_WIDTH + (cardCount - 1) * CARD_STEP;
        }
        return (getWidth() - visibleWidth) / 2;
    }

    /**
     * Finds the baseline top edge of the bottom hand after resizing.
     *
     * @return the top edge of a bottom card that is not raised
     */
    private int bottomY() {
        return Math.max(0, getHeight() - BOTTOM_MARGIN - CARD_HEIGHT);
    }

    /**
     * Draws a gray, rounded card with a border and centered black number.
     *
     * @param graphics  the drawing context
     * @param cardValue the number on the card
     * @param cardLeft  the left edge of the card
     * @param cardTop   the top edge of the card
     */
    private void drawCard(Graphics graphics, int cardValue,
            int cardLeft, int cardTop) {
        // Measure the number and find its centered drawing position.
        String number = Integer.toString(cardValue);
        FontMetrics metrics = graphics.getFontMetrics();
        int numberLeft = cardLeft
                + (CARD_WIDTH - metrics.stringWidth(number)) / 2;
        int numberBaseline = cardTop
                + (CARD_HEIGHT - metrics.getHeight()) / 2
                + metrics.getAscent();

        // Fill and outline the card with subtly rounded corners.
        graphics.setColor(CARD_COLOR);
        graphics.fillRoundRect(cardLeft, cardTop, CARD_WIDTH, CARD_HEIGHT,
                CORNER_DIAMETER, CORNER_DIAMETER);
        graphics.setColor(Color.BLACK);
        graphics.drawRoundRect(cardLeft, cardTop,
                CARD_WIDTH - 1, CARD_HEIGHT - 1,
                CORNER_DIAMETER, CORNER_DIAMETER);

        // Center the number using the font's measured dimensions.
        graphics.drawString(number, numberLeft, numberBaseline);
    }

    /**
     * Finds the topmost bottom card containing the given point.
     *
     * @param point the mouse position relative to the panel
     * @return the card's index, or -1 when the point is outside every card
     */
    private int cardIndex(Point point) {
        int handLeft = groupStart(handCards.size());

        // Check right to left, using the same raised bounds as the drawing.
        for (int i = handCards.size() - 1; i >= 0; --i) {
            int cardTop = bottomY();

            if (i == hoveredIndex) {
                cardTop -= HOVER_RISE;
            }

            RoundRectangle2D bounds = new RoundRectangle2D.Double(
                    handLeft + i * CARD_STEP, cardTop,
                    CARD_WIDTH, CARD_HEIGHT,
                    CORNER_DIAMETER, CORNER_DIAMETER);

            if (bounds.contains(point)) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Solution3());
    }
}
