import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;

public class Solution4 extends JFrame {

    private final CardPanel cardPanel;

    public Solution4() {

        setTitle("cards");
        setSize(900, 650);
        setMinimumSize(new Dimension(600, 450));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        // =====================================================
        // MENU
        // =====================================================

        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");

        fileMenu.add(newItem);
        fileMenu.add(new5Item);
        fileMenu.add(new10Item);
        fileMenu.addSeparator();
        fileMenu.add(exitItem);

        menuBar.add(fileMenu);

        setJMenuBar(menuBar);

        // =====================================================
        // GAME AREA
        // =====================================================

        cardPanel = new CardPanel();

        add(cardPanel, BorderLayout.CENTER);

        // New = random amount from 5 through 10
        newItem.addActionListener(e -> {

            int amount =
                    5 + cardPanel.random.nextInt(6);

            cardPanel.createCards(amount);
        });

        // Exactly 5 cards
        new5Item.addActionListener(e ->
                cardPanel.createCards(5)
        );

        // Exactly 10 cards
        new10Item.addActionListener(e ->
                cardPanel.createCards(10)
        );

        // Exit application
        exitItem.addActionListener(e ->
                System.exit(0)
        );

        setVisible(true);
    }

    // =========================================================
    // CARD CLASS
    // =========================================================

    static class PlayingCard {

        int number;

        int x;
        int y;

        final int width = 100;
        final int height = 145;

        boolean hovered = false;

        PlayingCard(int number) {
            this.number = number;
        }

        // -----------------------------------------------------
        // CHECK IF MOUSE IS OVER CARD
        // -----------------------------------------------------

        boolean contains(int mouseX, int mouseY) {

            int drawY = hovered
                    ? y - 18
                    : y;

            return mouseX >= x
                    && mouseX <= x + width
                    && mouseY >= drawY
                    && mouseY <= drawY + height;
        }

        // -----------------------------------------------------
        // DRAW CARD
        // -----------------------------------------------------

        void draw(Graphics2D g2) {

            int drawY = hovered
                    ? y - 18
                    : y;

            /*
             * Card body
             *
             * A darker light-gray color.
             */
            g2.setColor(
                    new Color(
                            145,
                            150,
                            155
                    )
            );

            g2.fillRoundRect(
                    x,
                    drawY,
                    width,
                    height,
                    22,
                    22
            );

            // Thin black border
            g2.setColor(Color.BLACK);

            g2.setStroke(
                    new BasicStroke(1.5f)
            );

            g2.drawRoundRect(
                    x,
                    drawY,
                    width,
                    height,
                    22,
                    22
            );

            // Card number
            g2.setFont(
                    new Font(
                            "SansSerif",
                            Font.BOLD,
                            20
                    )
            );

            g2.drawString(
                    String.valueOf(number),
                    x + 12,
                    drawY + 27
            );
        }
    }

    // =========================================================
    // CARD PANEL
    // =========================================================

    static class CardPanel extends JPanel
            implements MouseListener,
            MouseMotionListener {

        /*
         * These are cards still in the player's hand.
         */
        private final List<PlayingCard> handCards =
                new ArrayList<>();

        /*
         * These are cards that have been clicked.
         */
        private final List<PlayingCard> playedCards =
                new ArrayList<>();

        final Random random =
                new Random();

        private PlayingCard hoveredCard = null;

        CardPanel() {

            setBackground(
                    new Color(
                            235,
                            235,
                            235
                    )
            );

            addMouseListener(this);
            addMouseMotionListener(this);
        }

        // =====================================================
        // CREATE NEW GAME
        // =====================================================

        void createCards(int amount) {

            /*
             * Completely clear the previous game.
             */
            handCards.clear();
            playedCards.clear();

            hoveredCard = null;

            /*
             * Create EXACTLY the requested
             * number of cards.
             */
            for (int i = 0; i < amount; i++) {

                int number =
                        random.nextInt(20) + 1;

                PlayingCard card =
                        new PlayingCard(number);

                handCards.add(card);
            }

            revalidate();
            repaint();
        }

        // =====================================================
        // POSITION HAND CARDS
        // =====================================================

        private void positionHandCards() {

            if (handCards.isEmpty()) {
                return;
            }

            int cardWidth = 100;

            /*
             * Distance between the start of
             * one card and the next card.
             *
             * Since card width is 100,
             * this creates overlap.
             */
            int spacing = 45;

            int totalWidth =
                    cardWidth
                            + spacing
                            * (handCards.size() - 1);

            int startX =
                    (getWidth() - totalWidth) / 2;

            /*
             * Position cards in lower half.
             */
            int y =
                    (int) (
                            getHeight() * 0.68
                    );

            for (int i = 0;
                 i < handCards.size();
                 i++) {

                PlayingCard card =
                        handCards.get(i);

                card.x =
                        startX
                                + i * spacing;

                card.y = y;
            }
        }

        // =====================================================
        // POSITION PLAYED CARDS
        // =====================================================

        private void positionPlayedCards() {

            if (playedCards.isEmpty()) {
                return;
            }
        
            /*
             * All played cards go to the exact same
             * position, creating a pile.
             */
            int x =
                    (getWidth() - 100) / 2;
        
            int y =
                    getHeight() / 4 - 72;
        
            for (PlayingCard card : playedCards) {
        
                card.x = x;
                card.y = y;
        
                card.hovered = false;
            }
        }

        // =====================================================
        // DRAW EVERYTHING
        // =====================================================

        @Override
        protected void paintComponent(Graphics g) {

            super.paintComponent(g);

            Graphics2D g2 =
                    (Graphics2D) g.create();

            /*
             * Smooth rounded corners
             * and text.
             */
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON
            );

            positionHandCards();
            positionPlayedCards();

            // Draw played cards first
            for (PlayingCard card : playedCards) {
                card.draw(g2);
            }

            // Draw hand cards afterward
            for (PlayingCard card : handCards) {
                card.draw(g2);
            }

            g2.dispose();
        }

        // =====================================================
        // FIND HAND CARD UNDER MOUSE
        // =====================================================

        private PlayingCard findHandCard(
                int mouseX,
                int mouseY) {

            /*
             * Go backwards because cards
             * later in the list are drawn
             * on top of earlier cards.
             */
            for (int i =
                 handCards.size() - 1;
                 i >= 0;
                 i--) {

                PlayingCard card =
                        handCards.get(i);

                if (card.contains(
                        mouseX,
                        mouseY
                )) {

                    return card;
                }
            }

            return null;
        }

        // =====================================================
        // HOVER
        // =====================================================

        @Override
        public void mouseMoved(MouseEvent e) {

            PlayingCard newHovered =
                    findHandCard(
                            e.getX(),
                            e.getY()
                    );

            if (newHovered != hoveredCard) {

                if (hoveredCard != null) {
                    hoveredCard.hovered = false;
                }

                hoveredCard = newHovered;

                if (hoveredCard != null) {
                    hoveredCard.hovered = true;
                }

                repaint();
            }

            /*
             * Hand cursor when hovering
             * over a card.
             */
            if (hoveredCard != null) {

                setCursor(
                        Cursor.getPredefinedCursor(
                                Cursor.HAND_CURSOR
                        )
                );

            } else {

                setCursor(
                        Cursor.getDefaultCursor()
                );
            }
        }

        // =====================================================
        // CLICK CARD
        // =====================================================

        @Override
        public void mouseClicked(MouseEvent e) {

            PlayingCard clicked =
                    findHandCard(
                            e.getX(),
                            e.getY()
                    );

            if (clicked != null) {

                /*
                 * Remove card from hand.
                 */
                handCards.remove(clicked);

                /*
                 * Add it to the cards
                 * in the upper half.
                 *
                 * Previous cards stay there.
                 */
                clicked.hovered = false;

                playedCards.add(clicked);

                hoveredCard = null;

                setCursor(
                        Cursor.getDefaultCursor()
                );

                repaint();
            }
        }

        // =====================================================
        // UNUSED MOUSE METHODS
        // =====================================================

        @Override
        public void mouseDragged(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {

            if (hoveredCard != null) {

                hoveredCard.hovered = false;

                hoveredCard = null;

                repaint();
            }

            setCursor(
                    Cursor.getDefaultCursor()
            );
        }
    }

    // =========================================================
    // MAIN
    // =========================================================

    public static void main(String[] args) {

        SwingUtilities.invokeLater(
                Solution4::new
        );
    }
}
