import java.nio.ByteBuffer;
import java.util.ArrayList;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage; 

/** 
 * WritableImage test code 
 */ 
public class WritableImageTest extends Application { 
	// If false, some Exception occurs and GUI refresh stops. 
	// To avoid Exception, this flag must be set to true. 
	boolean exceptionAvoidFlag = false; 
	@Override 
	public void start(Stage primaryStage) throws Exception { 
		VBox vBox = new VBox(); 
		ArrayList<UpdateTask> tasks = new ArrayList<>(); 
		for (int i = 0; i < 50; i++) { 
			UpdateTask task = new UpdateTask(i); 
			tasks.add(task); 
			vBox.getChildren().add(createBp(task)); 
		} 
		tasks.forEach(t -> new Thread(t).start()); 
		Scene scene = new Scene(vBox, 400, 600); 
		primaryStage.setScene(scene); 
		primaryStage.setOnCloseRequest(v -> tasks.forEach(t -> t.stop = true)); 
		primaryStage.show(); 
	} 

	/** 
	 * create BorderPane with Label and ImageView 
	 */ 
	BorderPane createBp(UpdateTask task) { 
		final BorderPane borderPane = new BorderPane(); 
		final Label label = new Label("update info..."); 
		final ImageView imageView = new ImageView(); 
		task.valueProperty().addListener((ob, o, n) -> { 
			if (n == null) return; 
			label.setText(n.labelStr); 
			imageView.setImage(n.image); 
		}); 
		borderPane.setTop(label); 
		borderPane.setCenter(imageView);; 
		return borderPane; 
	} 

	/** 
	 * main entry point 
	 */ 
	public static void main(String args[]) { 
		launch(args); 
	} 

	/** 
	 * update label info and image 
	 */ 
	class UpdateTask extends Task<UpdateInfo> { 
		boolean stop = false; 
		int id; 
		int width = 320, height = 240; 
		public UpdateTask(int _id) { 
			id = _id; 
		} 
		@Override 
		protected UpdateInfo call() throws Exception { 
			int ctr = 0; 
			byte[] data = new byte[width * height * 3]; 
			WritableImage wi = new WritableImage(width, height); 
			PixelFormat<ByteBuffer> pf = PixelFormat.getByteRgbInstance(); 
			while (!stop) { 
				String str = id+" update: "+ctr; 
				// TODO need to renew WritableImage instance in order to avoid NLE 
				if (exceptionAvoidFlag) wi = new WritableImage(width, height); 
				fillImage(wi, pf, ctr++, data); 
				updateValue(new UpdateInfo(str, wi)); 
				Thread.sleep(50); 
			} 
			return null; 
		} 

		/** 
		 * fill in arbitrary data and write into WritableImage 
		 */ 
		private void fillImage(WritableImage wi, PixelFormat<ByteBuffer> pf, int index, byte[] data) { 
			for (int i = 0; i < data.length; i+=3) { 
				data[i + 0] = (byte) (i + index * 3); 
				data[i + 1] = (byte) (i + index * 3); 
				data[i + 2] = (byte) (i + index * 3); 
			} 
			wi.getPixelWriter().setPixels(0, 0, width, height, pf, data, 0, width * 3); 
		} 
	} 

	/** 
	 * update info 
	 */ 
	class UpdateInfo { 
		String labelStr; 
		Image image; 
		public UpdateInfo(String s, Image i) { 
			labelStr = s; 
			image = i; 
		} 
	} 

} 
