package dotsandboxes;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.util.ArrayList;

public class DotsAndBoxesUI {

    static final int lineLength = 32;
    static final int margin = 10;
    static final int gap = 0;
    static final int dotDiameter = 6;

    // The coordinate of the top or left of a the painting area for this row/col
    private static int corner(int col) {
        return margin + col * (gap + lineLength + gap + dotDiameter);
    }

    /** Colours for the different players. Only goes up to 5. */
    static final Color[] playerColours = { Color.WHITE, Color.RED, Color.BLUE, Color.GREEN, Color.PINK, Color.ORANGE };

    final DotsAndBoxesGrid grid;
    final JPanel anchorPane;
    final Canvas canvas;
    final JLabel label;

    private void updateLabel() {
        label.setForeground(playerColours[grid.getPlayer()]);
        label.setText(String.format("Player %d's turn", grid.getPlayer()));
    }

    public DotsAndBoxesUI(final DotsAndBoxesGrid grid) {
        this.grid = grid;
        anchorPane = new JPanel(new BorderLayout());

        label = new JLabel("");
        updateLabel();

        canvas = new DABCanvas();

        anchorPane.add(canvas, BorderLayout.CENTER);
        anchorPane.add(label, BorderLayout.NORTH);

        grid.addConsumer((g) -> {
            updateLabel();
            canvas.repaint();
        });
    }

    /** A component that paints and handles clicks on the game */
    class DABCanvas extends Canvas {

        final ArrayList<Horizontal> horizontals = new ArrayList<>();
        final ArrayList<Vertical> verticals = new ArrayList<>();
        final ArrayList<Box> boxes = new ArrayList<>();

        /** Represents a horizontal line. */
        record Horizontal(int col, int row) {
            Rectangle rect() {
                int x = corner(col) + dotDiameter + gap;
                int y = corner(row);
                return new Rectangle(x, y, lineLength, dotDiameter);
            }

            /** Whether or not this line contains this point */
            boolean contains(int x, int y) {
                return rect().contains(x, y);
            }

            /** Paints this element, based on the passed in grid */
            public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
                g2d.setColor(grid.getHorizontal(col, row) ? Color.DARK_GRAY : Color.LIGHT_GRAY);
                g2d.fill(this.rect());
            }
        }

        /** Represents a horizontal line. */
        record Vertical(int col, int row) {
            Rectangle rect() {
                int x = corner(col);
                int y = corner(row) + dotDiameter + gap;
                return new Rectangle(x, y, dotDiameter, lineLength);
            }

            /** Whether or not this line contains this point */
            boolean contains(int x, int y) {
                return rect().contains(x, y);
            }

            /** Paints this element, based on the passed in grid */
            public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
                g2d.setColor(grid.getVertical(col, row) ? Color.DARK_GRAY : Color.LIGHT_GRAY);
                g2d.fill(this.rect());
            }
        }

        /** represents a box */
        record Box(int col, int row) {
            Rectangle rect() {
                int x = corner(col) + dotDiameter + gap;
                int y = corner(row) + dotDiameter + gap;
                return new Rectangle(x, y, lineLength, lineLength);
            }

            /** Whether or not this line contains this point */
            boolean contains(int x, int y) {
                return rect().contains(x, y);
            }

            /** Paints this element, based on the passed in grid */
            public void draw(DotsAndBoxesGrid grid, Graphics2D g2d) {
                g2d.setColor(playerColours[grid.getBoxOwner(col, row)]);
                g2d.fill(this.rect());
            }
        }


        public DABCanvas() {
            // Size the canvas to just contain the elements
            int width = corner(grid.width) + margin;
            int height = corner(grid.height) + margin;
            this.setPreferredSize(new Dimension(width, height));

            // Create records for the boxes
            for (int row = 0; row < grid.height - 1; row++) {
                for (int col = 0; col < grid.width - 1; col++) {
                    boxes.add(new Box(col, row));
                }
            }

            // Create records for horizontals
            for (int row = 0; row < grid.height; row++) {
                for (int col = 0; col < grid.width - 1; col++) {
                    horizontals.add(new Horizontal(col, row));
                }
            }

            // Create records for verticals
            for (int row = 0; row < grid.height - 1; row++) {
                for (int col = 0; col < grid.width; col++) {
                    verticals.add(new Vertical(col, row));
                }
            }

            addMouseListener(new MouseInputAdapter() {
                @Override public void mousePressed(MouseEvent e) {
                    for (Horizontal h : horizontals) {
                        if (h.contains(e.getX(), e.getY())) {
                            grid.drawHorizontal(h.col(), h.row(), grid.getPlayer());
                        }
                    }

                    for (Vertical v : verticals) {
                        if (v.contains(e.getX(), e.getY())) {
                            grid.drawVertical(v.col(), v.row(), grid.getPlayer());
                        }
                    }
                }        
            });

        }

        @Override public void paint(Graphics g) {
            Graphics2D g2d = (Graphics2D)g;
            g.clearRect(0, 0, this.getWidth(), this.getHeight());
            g2d.setColor(Color.WHITE);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());

            // Paint the boxes
            for (Box b : boxes) {
                b.draw(grid, g2d);
            }

            // Paint the horizontals
            for (Horizontal h : horizontals) {
                h.draw(grid, g2d);
            }

            // Paint the boxes
            for (Vertical v : verticals) {
                v.draw(grid, g2d);
            }

            // Draw the dots
            for (int row = 0; row < grid.height; row++) {
                for (int col = 0; col < grid.width; col++) {
                    g2d.setColor(Color.BLACK);
                    g2d.fillOval(corner(col), corner(row), dotDiameter, dotDiameter);
                }
            }
        }

    }




}