import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * What to do?
 * Hover once over the tooltip and see the size of dirty nodes list.
 * The number of dirty nodes will stay small as long as u hover over it, but as soon as u leave the label and the tooltip closes it will increase statically.
 * Unless u hover again it will keep increasing by 2 per second.
 * The are some hacks presented how to stop this....
 * Tho a real change in java fx logic to prevent this would be preferred.
 */
public class HalloFx extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        String fix = getParameters().getNamed().get("fix");
        int fixNumber = fix== null || fix.equals("") ? 0:Integer.parseInt(fix);

        Scene scene = new Scene(new Pane());
        Label tooltip_here = new Label("tooltip here");
        Tooltip tooltip = new Tooltip();
        Pane tooltipPane = new Pane();
        tooltip.setGraphic(tooltipPane);

        new Thread(()->{
            while (true){
                Platform.runLater(()->{
                    tooltipPane.getChildren().clear();
                    Label yet_another_label = new Label("yet another label");
                    if(fixNumber != 1 || tooltip.isShowing()){
                        tooltipPane.getChildren().add(yet_another_label);
                        //Well one way to fix it is just to stop updating the UI or to stop changing it when tooltip is not showing
                    }

                    Scene tooltipScene = tooltipPane.getScene();
                    if(fixNumber==2){
                        try {
                            Field scenePulseListener = Scene.class.getDeclaredField("scenePulseListener");
                            scenePulseListener.setAccessible(true);
                            Object pulseListener = scenePulseListener.get(tooltipScene);
                            Method synchronizeSceneNodes = pulseListener.getClass().getDeclaredMethod("synchronizeSceneNodes");
                            synchronizeSceneNodes.setAccessible(true);
                            synchronizeSceneNodes.invoke(pulseListener);
                            //Keep up the pulse of the tooltip when it is closed, because the tooltip stops listening to the pulse when closed. This is a second way to fix it.
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    if(tooltipScene==null) return;

                    try {
                        Field dirtyNodesSize = Scene.class.getDeclaredField("dirtyNodesSize");
                        dirtyNodesSize.setAccessible(true);
                        Object size = dirtyNodesSize.get(tooltipScene);
                        if(size!=null)System.out.println(size);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }


                });
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        tooltip_here.setTooltip(tooltip);
        scene.setRoot(new Pane(tooltip_here));

        stage.setTitle("JavaFX Tooltip leak");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
