package org.genericsystem.cv;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.function.Function;
import java.util.stream.Stream;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;

public class ImgClass2 {

	private final Img classModel;
	private SimpleObjectProperty<Img> observableMean = new SimpleObjectProperty<>();
	private SimpleObjectProperty<Img> observableVariance = new SimpleObjectProperty<>();
	private final String directory;
	private final SimpleObjectProperty<Function<Img, Img>> preprocessor = new SimpleObjectProperty<>();
	private Zones zones;
	private ObjectMapper mapper = new ObjectMapper();

	public static ImgClass2 fromDirectory(Img classModel, String bgrDirectory) {
		return new ImgClass2(classModel, bgrDirectory);
	}

	private Img applyPreprocessor(Img img) {
		if (preprocessor.getValue() != null) {
			img = preprocessor.getValue().apply(img);
		}
		return img;
	}

	public ImgClass2(Img classModel, String bgrDirectory) {
		this.classModel = classModel;
		this.directory = bgrDirectory;
		computeMeanVariance();
		preprocessor.addListener((o, ov, nv) -> {

			new Thread(new Runnable() {
				@Override
				public void run() {

					computeMeanVariance();

				}
			}).start();

			// computeMeanVariance();
		});
	}

	public Stream<Img> classImgsStream() {
		return Tools.classImgsStream(directory);
	}

	private synchronized void computeMeanVariance() {
		Img img0 = applyPreprocessor(classImgsStream().iterator().next());
		boolean gray = img0.channels() == 1;
		int type = gray ? CvType.CV_32S : CvType.CV_32SC3;

		Mat mean = new Mat(img0.size(), type, Scalar.all(0));
		Mat m2 = new Mat(img0.size(), type, Scalar.all(0));
		Mat mask = Mat.ones(img0.size(), CvType.CV_8U);
		int count = 1;
		Iterator<Img> it = classImgsStream().iterator();
		while (it.hasNext()) {
			Mat img = new Mat();
			applyPreprocessor(it.next()).getSrc().convertTo(img, type);
			Mat delta = new Mat(img.size(), type);
			Core.subtract(img, mean, delta, mask, type);
			Core.addWeighted(mean, 1, delta, 1d / count, 0, mean, type);
			Mat delta2 = new Mat(m2.size(), type);
			Core.subtract(img, mean, delta2, mask, type);
			Mat product = delta.mul(delta2);
			Core.add(m2, product, m2);
			count++;
		}
		Mat variance = new Mat(m2.size(), type);
		Core.multiply(m2, new Scalar(1d / count, 1d / count, 1d / count), variance);
		variance.convertTo(variance, CvType.CV_8U);
		mean.convertTo(mean, CvType.CV_8U);
		this.observableMean.setValue(new Img(mean));
		this.observableVariance.setValue(new Img(variance));
		System.gc();
		System.runFinalization();
	}

	public void setPreprocessor(Function<Img, Img> after) {
		preprocessor.setValue(after);
	}

	public Img getClassModel() {
		return classModel;
	}

	public ObservableValue<Img> getObservableMean() {
		return observableMean;
	}

	public ObservableValue<Img> getObservableVariance() {
		return observableVariance;
	}

	public String getDirectory() {
		return directory;
	}

	public Img getClosedMean(Size morphClose) {
		return observableMean.getValue().morphologyEx(Imgproc.MORPH_CLOSE,
				new StructuringElement(Imgproc.MORPH_RECT, morphClose));
	}

	public Img getClosedVariance(Size morphClose) {
		return observableVariance.getValue().morphologyEx(Imgproc.MORPH_CLOSE,
				new StructuringElement(Imgproc.MORPH_RECT, morphClose));
	}

	public void loadZones() throws JsonParseException, JsonMappingException, IOException {
		zones = mapper.readValue(new File(directory + "/zones/zones.json"), Zones.class);
	}

	public void saveZones() throws JsonGenerationException, JsonMappingException, IOException {
		File save = new File(directory + "/zones/zones.json");
		save.getParentFile().mkdirs();
		mapper.enable(SerializationFeature.INDENT_OUTPUT);
		mapper.writeValue(save, zones);
	}

	public void setZones(Zones zones) {
		this.zones = zones;
	}

}

@@ -1347,12 +1347,15 @@
 %7B%0A%09%09
-if (
+return 
 prep
@@ -1385,20 +1385,10 @@
 null
-) %7B%0A%09%09%09img =
+ ?
  pre
@@ -1422,22 +1422,10 @@
 img)
-;%0A%09%09%7D%0A%09%09return
+ :
  img
@@ -1621,17 +1621,16 @@
 v) -%3E %7B%0A
-%0A
 %09%09%09new T
@@ -1690,17 +1690,16 @@
 run() %7B%0A
-%0A
 %09%09%09%09%09com
@@ -1718,17 +1718,16 @@
 ance();%0A
-%0A
 %09%09%09%09%7D%0A%09%09

save and load zones in/from a json file