import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class BubbleSort2 extends Application {
    private final Color[] cs = {Color.RED, Color.BLUE};
    private int[] init = {10, 52, 23, 34, 8, 12, 4, 46, 7, 45, 44, 3};
    private int[] args = new int[init.length];
    private final int scale = 10;
    private volatile Thread thread;
    private int i, j;
    private volatile boolean threadSuspended = true;

    private Canvas canvas;

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("バブルソート");
        Button step = new Button("step");
        step.setOnAction(e -> stepThread());

        canvas = new Canvas(320,250);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        paint(gc);

        VBox root = new VBox();
        root.setAlignment(Pos.CENTER);
        root.getChildren().addAll(step, canvas);
        stage.setScene(new Scene(root));
        stage.show();

        startThread();
    }

    private synchronized void stepThread() {
        threadSuspended = false;
        notify();
    }

    private void bubbleSort() {
        while (true) {
            for (int k = 0; k < init.length; k++) { args[k] = init[k]; }
            // バブルソートアルゴリズム
            for (i = 0; i < args.length - 1; i++) {
                for (j = args.length - 1; j > i; j--) {
                    if (args[j - 1] > args[j]) { // スワップする。
                        int tmp = args[j - 1];
                        args[j - 1] = args[j];
                        args[j] = tmp;
                    }
                    Platform.runLater(() -> paint(canvas.getGraphicsContext2D()));
                    /* repaint の後で止まる */
                    try {
                        synchronized(this) {
                            while (threadSuspended) {
                                wait();
                            }
                            threadSuspended = true;
                        }
                    } catch (InterruptedException e) {}
                }
            }
        }
    }

    private void startThread() {
        if (thread == null) {
            Task<Void> task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    bubbleSort(); // 外側クラスのメソッドにしないと this があわない
                    return null;
                }
            };

            thread = new Thread(task);
            thread.setDaemon(true);
            thread.start();
        }
    }

    private void paint(GraphicsContext gc) {
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
        gc.setFill(Color.YELLOW);
        gc.fillOval(5, 30 + j * scale, scale, scale);
        gc.setFill(Color.CYAN);
        gc.fillOval(5, 30 + i * scale, scale, scale);
        int n = args.length;
        for (int i = 0; i < n; i++) {
            gc.setFill(cs[args[i] % 2]);
            gc.fillRect(20, 30 + i * scale, args[i] * scale / 2, scale);
        }
    }

    public static void main(String... args) {
        launch(args);
    }
}
