package org.gnu.grub.fonttool;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;

public class CharMatrix extends JComponent {
    private Font charFont;
    private int zoom = 1;
    private int startingChar;
    private Map<Rectangle, Integer> glyphLocations = new HashMap<Rectangle, Integer>();
    private GlyphSelectionListener glyphSelectionListener;
    private Point mouseLocation;

    public interface GlyphSelectionListener {
        void glyphSelected(int codePoint);
    }

    public CharMatrix() {
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                int codePoint = getCodePointForGlyphAt(e.getPoint());
                if (codePoint != -1) {
                    if (glyphSelectionListener != null)
                        glyphSelectionListener.glyphSelected(codePoint);
                }
            }

            public void mouseExited(MouseEvent e) {
                mouseLocation = null;
                repaint();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent e) {
                mouseLocation = e.getPoint();
                repaint();
            }
        });
    }

    public void setGlyphSelectionListener(GlyphSelectionListener listener) {
        this.glyphSelectionListener = listener;
    }

    public Font getCharFont() {
        return charFont;
    }

    public void setCharFont(Font font) {
        this.charFont = font;
        repaint();
    }

    public int getZoom() {
        return zoom;
    }

    public void setZoom(int zoom) {
        this.zoom = zoom;
        repaint();
    }

    public int getStartingChar() {
        return startingChar;
    }

    public void setStartingChar(int startingChar) {
        this.startingChar = startingChar;
        repaint();
    }

    public int getCodePointForGlyphAt(Point p) {
        for (Rectangle rectangle : glyphLocations.keySet()) {
            if (rectangle.contains(p))
                return glyphLocations.get(rectangle);
        }
        return -1;
    }

    protected void paintComponent(Graphics g) {
        glyphLocations.clear();

        if (charFont == null)
            return;

        int w = getWidth();
        int h = getHeight();
        int maxCharWidth = charFont.getMaxCharWidth();
        int maxCharHeight = charFont.getMaxCharHeight();
        int space = 2;
        int charsPerRow = w / (maxCharWidth + space);
        int rows = h / (maxCharHeight + space);

        Rectangle highlightBounds = null;
        int codePoint = startingChar;
        int y = 3 + charFont.getAscent();   // Point y to the baseline.
        for (int row = 0; row < rows; row++) {
            int x = 3;
            for (int col = 0; col < charsPerRow; col++) {
                Glyph glyph = charFont.getGlyph(codePoint);

                if (glyph != null) {
                    Rectangle charBounds =
                            CharView.drawGlyph(g, charFont, glyph, x, y, zoom, getForeground());
                    // Expand the bounding box for a bigger clickable area.
                    final int boundsPad = 1;
                    charBounds.x -= boundsPad;
                    charBounds.y -= boundsPad;
                    charBounds.width += 2 * boundsPad;
                    charBounds.height += 2 * boundsPad;
                    glyphLocations.put(charBounds, codePoint);
                    if (mouseLocation != null && charBounds.contains(mouseLocation)) {
                        highlightBounds = charBounds;
                    }
                }
                x += maxCharWidth + space;
                codePoint++;
            }

            y += maxCharHeight + space;
        }

        if (highlightBounds != null) {
            g.setColor(Color.blue);
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(new BasicStroke(3f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 2f));
            g2.draw(new Rectangle(highlightBounds.x - 2, highlightBounds.y - 2,
                                  highlightBounds.width + 3, highlightBounds.height + 3));
            g2.setStroke(new BasicStroke(1f));
        }

    }
}
