import java.awt.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

/**
 * Test to show that the caret when left-click doesn't align with
 * the mouse pointer position on Windows with Java 13 and screen resolution > 100%.
 */
public class TestCaretJava13 {

    private TestCaretJava13() {
        JFrame f = new JFrame("Test Cursor/Caret with Java 9");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JTextPane textPane = new JTextPane();

        JComboBox<Font> fontCombo = new JComboBox<>();
        fontCombo.setMaximumRowCount(20);
        fontCombo.addItemListener(e -> {
            Font font = (Font) e.getItem();
            textPane.setFont(font);
        });
        fontCombo.addItem(textPane.getFont());
        fontCombo.addItem(new Font("Monospaced", Font.PLAIN, 12));
        List<Font> fonts = getFonts();
        for (Font font : fonts) {
            fontCombo.addItem(font);
        }

        fillTextPane(textPane);

        JPanel toolbar = new JPanel();
        toolbar.add(fontCombo);

        f.add(toolbar, BorderLayout.NORTH);
        f.add(new JScrollPane(textPane), BorderLayout.CENTER);

        f.pack();
        f.setVisible(true);
    }

    private void fillTextPane(JTextPane textPane) {
        StringBuilder buf = new StringBuilder();
        addSystemProperties(buf);

        for (int i = 0; i < 30; i++) {
            StringBuilder row = new StringBuilder();
            for (int j = 0; j < 50; j++) {
                row.append(j);
                if (j % 5 == 0) {
                    row.append(" ");
                }
            }
            buf.append(row).append(System.lineSeparator());
        }
        textPane.setText(buf.toString());
        textPane.setCaretPosition(0);
    }

    private void addSystemProperties(StringBuilder buf) {
        buf.append("os.name: ").append(System.getProperty("os.name")).append(System.lineSeparator());
        buf.append("os.version: ").append(System.getProperty("os.version")).append(System.lineSeparator());
        buf.append("os.arch: ").append(System.getProperty("os.arch")).append(System.lineSeparator());
        buf.append("java.version: ").append(System.getProperty("java.version")).append(System.lineSeparator());
        buf.append("java.vm.name: ").append(System.getProperty("java.vm.name")).append(System.lineSeparator());
        buf.append("java.vm.vendor: ").append(System.getProperty("java.vm.vendor")).append(System.lineSeparator());
        buf.append("java.home: ").append(System.getProperty("java.home")).append(System.lineSeparator());
        buf.append("Monitors: ").append(System.lineSeparator()).append(getScreenInfo()).append(System.lineSeparator());
        buf.append(System.lineSeparator());
    }

    private static String getScreenInfo() {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] devices = env.getScreenDevices();

        StringBuilder returnString = new StringBuilder();
        int screenNo = 1;
        for (GraphicsDevice device : devices) {
            DisplayMode displayMode = device.getDisplayMode();
            if (returnString.length() > 0) {
                returnString.append(System.lineSeparator());
            }
            returnString.append("Screen ").append(screenNo++);
            returnString.append(": size: ").append(displayMode.getWidth()).append(" x ").append(displayMode.getHeight());
            returnString.append(", refresh rate: ").append(displayMode.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN ? displayMode.getRefreshRate() : "unknown");
            returnString.append(", bit depth: ").append(displayMode.getBitDepth());
            if (isHiDPI(device)) {
                returnString.append(", HiDPI display: true");
            }
        }
        return returnString.toString();
    }

    private static boolean isHiDPI(GraphicsDevice device) {
        try {
            Field field = device.getClass().getDeclaredField("scale");

            if (field != null) {
                field.setAccessible(true);
                Object scale = field.get(device);

                if (scale instanceof Integer && (Integer) scale == 2) {
                    return true;
                }
            }
        } catch (Throwable ignore) {
        }

        return device.getDefaultConfiguration().getDefaultTransform().getScaleX() == 2;
    }

    private List<Font> getFonts() {
        List<Font> fonts = new ArrayList<>();

        String[] fontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();

        for (String fontFamilyName : fontFamilyNames) {
            Font font = new Font(fontFamilyName, Font.PLAIN, 12);
            fonts.add(font);
        }
        return fonts;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestCaretJava13::new);
    }
}