package cz.vutbr.feec.imageprocessing.op.segmentation.watershed;

import ij.ImagePlus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeInt;
import cz.vutbr.feec.imageprocessing.IOObject.ImagePlusClrIOObject;
import cz.vutbr.feec.imageprocessing.IOObject.ImagePlusSegmentedIOObject;



public class WatershedOperator extends Operator {
	private static final String PARAM_CONVERT_MODEL = "convert_model";
	private static final List<RgbToGSModel> CONVERT_MODEL = Arrays.asList(RgbToGSModel.RGB_TO_GS, RgbToGSModel.AVG);
	private static final String PARAM_CONNECTIVITY_MODEL = "connectivity_model";
	private static final List<ConnectivityModels> CONNECTIVITY_MODEL = Arrays.asList(ConnectivityModels.C4, ConnectivityModels.C8);
	private static final String PARAM_SMALL_REG = "small_region_threshold";
	private static final String PARAM_MERGING_THR = "merging_threshold";
	
	public WatershedOperator(OperatorDescription description) {
		super(description);
		getTransformer().addGenerationRule(exsOutImg,
				ImagePlusSegmentedIOObject.class);
	}

	private InputPort imgIn = getInputPorts().createPort("color image", ImagePlusClrIOObject.class);
	private OutputPort exsOutImg = getOutputPorts().createPort("segmented image");

	@Override
	public void doWork() throws OperatorException {
		ImagePlusClrIOObject in = imgIn.getData();
		ImagePlusSegmentedIOObject out = new ImagePlusSegmentedIOObject(in);
		WatershedAlgorithm wa = new WatershedAlgorithm();
		wa.debug(false);

		// convertModel 0 weight RGB, model 1 does not weight RGB during computing gray
		int convModelindex = getParameterAsInt(PARAM_CONVERT_MODEL);
		RgbToGSModel convertModel = CONVERT_MODEL.get(convModelindex);
		
		// connectivityModel 0 means 8-point neighborhood; 1 means 4-point neighborhood
		int conectindex = getParameterAsInt(PARAM_CONNECTIVITY_MODEL);
		ConnectivityModels connectivityModel = CONNECTIVITY_MODEL.get(conectindex);
		
		// threshold for connecting small regions to neighboring regions in promile
		int smallRegionThr = getParameterAsInt(PARAM_SMALL_REG);
		
		// threshold for merging; compared with (R1-R2)*(G1-G2)*(B1-B2), where indexes 1 and 2 are two merging regions
		int mergingThr = getParameterAsInt(PARAM_MERGING_THR);
		
		// true if wsheds should be assigned to neighboring regions labeling in Region class
		boolean wsheds = true;
		ImagePlus res = wa.doWork(in.getImage(), convertModel, connectivityModel, smallRegionThr, mergingThr, wsheds);
		out.setImage(res);
		wa.setOutput(out);
		
		exsOutImg.deliver(out);
		//exsOutImg.deliver(wa.setOutput(out));
	}

	@Override
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> p = super.getParameterTypes();
		ParameterType pas = new ParameterTypeCategory(PARAM_CONVERT_MODEL, "Type of conversion from RGB to grayscale", IterableToStringArray(CONVERT_MODEL), 0);
		p.add(new ParameterTypeCategory(PARAM_CONNECTIVITY_MODEL, "4-connected or 8-connected neighbourhood", IterableToStringArray(CONNECTIVITY_MODEL), 0));
		p.add(pas);
		p.add(new ParameterTypeInt(PARAM_SMALL_REG, "Regions bellow this value of size in per mille of original size will be merged with neighboring regions", 0, 20, 1));
		p.add(new ParameterTypeInt(PARAM_MERGING_THR, "Two neighbouring regions will be merged if their color similarity is below this threshold", 0, Integer.MAX_VALUE, 6000));
		return p;
	}
	
	private static String[] IterableToStringArray(Iterable arr) {
		List<String> l = new ArrayList<String>();
		for(Object o : arr) l.add(o.toString());
		return l.toArray(new String[l.size()]);
	}
}
































