package cz.vutbr.feec.utko.opencv.io;
/**
 * Implementation of the multiple image read and the result write to folder a loop operator
 * 
 * Author: Martin Hasmanda
 */

import ij.IJ;
import ij.process.ImageConverter;

import java.io.File;
import java.util.List;

import static com.googlecode.javacv.cpp.opencv_highgui.*;

import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.rapidminer.operator.OperatorChain;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.UserError;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.operator.ports.metadata.SimplePrecondition;
import com.rapidminer.operator.ports.metadata.SubprocessTransformRule;

import java.util.Vector;

import com.rapidminer.operator.ports.CollectingPortPairExtender;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.PortPairExtender;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeDirectory;
import com.rapidminer.parameter.conditions.BooleanParameterCondition;

import cz.vutbr.feec.imageprocessing.IOObject.ImagePlusBaseIOObject;
import cz.vutbr.feec.imageprocessing.IOObject.ImagePlusClrIOObject;
import cz.vutbr.feec.imageprocessing.IOObject.ImagePlusGSIOObject;

public class MultiRWImageOperator extends OperatorChain{

	// input pin in the begin of chain
	private final OutputPort innerOut = getSubprocess(0).getInnerSources().createPort("color image plus"); // input
	private final InputPort innerIn = getSubprocess(0).getInnerSinks().createPort("color image plus"); //output
	
	// for a operator
	private final OutputPort externOut = getOutputPorts().createPort("color image plus");
	
	// infinite pins (input in loop)
	private final PortPairExtender inputPortPairExtender = 
		new PortPairExtender("input", getInputPorts(), getSubprocess(0).getInnerSources());
	
	// infinite pins (output in loop)
	private final CollectingPortPairExtender outputPortPairExtender = 
		new CollectingPortPairExtender("output", getSubprocess(0).getInnerSinks(), getOutputPorts());

	
	protected static final String PARAMETER_IMAGE_FOLDERNAME = "image foldername";
	protected static final String PARAMETER_FITNESS_FOLDERNAME = "fitness foldername";
	protected static final String PARAMETER_RESULTS_FOLDERNAME = "results foldername";
	protected static final String PARAMETER_DEFINE_MASK = "Set mask";

	protected static final String[] extensionList = new String[] { ".bmp", ".gif", ".jpg", ".png" };
	
	public MultiRWImageOperator(OperatorDescription description) {
		super(description, "Executed Process");
		
		inputPortPairExtender.start();
		outputPortPairExtender.start();

	    getTransformer().addRule(inputPortPairExtender.makePassThroughRule());
	    getTransformer().addRule(new SubprocessTransformRule(getSubprocess(0)));
	    getTransformer().addRule(outputPortPairExtender.makePassThroughRule());
	    
		innerIn.addPrecondition(new SimplePrecondition(innerIn, new MetaData(ImagePlusBaseIOObject.class)));
		getTransformer().addPassThroughRule(innerIn, externOut);
		getTransformer().addGenerationRule(innerOut, ImagePlusGSIOObject.class);
		getTransformer().addRule(new SubprocessTransformRule(getSubprocess(0)));
		getTransformer().addGenerationRule(externOut, ImagePlusClrIOObject.class);
	}
	
	public void wirteImage(ImagePlusBaseIOObject imagePlusIO, String filename) {
		
		//create IplImage form BufferedImage
		ImagePlusBaseIOObject imgPlus = imagePlusIO;
		//create IplImage form BufferedImage
		IplImage ipl;
		if(imgPlus.isColor()) {
			ipl = IplImage.createFrom(imgPlus.getImage().getBufferedImage());
		} else {
			// grayscale
			// Convert to rgb
			ImageConverter ic = new ImageConverter(imgPlus.getImage());
			ic.convertToRGB();
			ipl = IplImage.createFrom(imgPlus.getImage().getBufferedImage());
		}
		
		cvSaveImage(filename, ipl);
	}
	
	protected File inputFitnessFolder;
	protected File inputImageFolder;
	protected File outputImageFolder;

	String [] GetFolderImages(File folder) throws OperatorException
	{
		System.out.println(folder.getPath());
		if (!folder.exists())
			throw new UserError(this, 301, "Image folder does not exist");
		
		String [] files = folder.list();
		Vector<String> filteredFiles = new Vector<String>();
		
		boolean extFound;
		
		for (String str : files) {
			extFound = false;
			for (int i = 0; i < extensionList.length; i++) {
				if(str.endsWith(extensionList[i]))
				{
					System.out.println(str);
					extFound = true;
					break;
				}
			}
			if(extFound)
				filteredFiles.add(str);
		}
		
		String [] str = new String[filteredFiles.size()];
		System.arraycopy(filteredFiles.toArray(), 0, str, 0, filteredFiles.size());
		
		return (str);
	}
	
	@Override
	public void doWork() throws OperatorException {

		inputImageFolder = getParameterAsFile(PARAMETER_IMAGE_FOLDERNAME);
		outputImageFolder = getParameterAsFile(PARAMETER_RESULTS_FOLDERNAME);
		inputFitnessFolder = getParameterAsFile(PARAMETER_FITNESS_FOLDERNAME);
		
		boolean bMask = getParameterAsBoolean(PARAMETER_DEFINE_MASK);
		
		String filenames[] = GetFolderImages(inputImageFolder);
		String masknames[] = null;
		
		if(bMask)
			masknames = GetFolderImages(inputFitnessFolder);
	
		inputPortPairExtender.passDataThrough();
		outputPortPairExtender.reset();
		
		int idx = 0;
		for(String filename : filenames)
		{
			String fileIn  = new String(inputImageFolder.getPath() + File.separator + filename);
			String fileOut = new String(outputImageFolder.getPath() + File.separator + "out_" + filename);
			
			IJ.open(fileIn);
			ImagePlusGSIOObject imagePlusIO = new ImagePlusGSIOObject(fileIn, IJ.getImage());
			imagePlusIO.setFileName(fileIn);
						
			if (bMask) {
				String maskIn = new String(inputFitnessFolder.getPath() + File.separator + masknames[idx++]);
			
				IJ.open(maskIn);
				imagePlusIO.setFitnessMask(IJ.getImage());
				
				System.out.println(fileIn);
				System.out.println(maskIn);
			}
			
			innerOut.deliver(imagePlusIO);
			
			getSubprocess(0).execute();
			outputPortPairExtender.collect();

			wirteImage((ImagePlusBaseIOObject) innerIn.getData(), fileOut);
		}
		
		outputPortPairExtender.passDataThrough();

		// deliver to output only last
		externOut.deliver((ImagePlusBaseIOObject) innerIn.getData());
	}
	
	
	@Override
	public List<ParameterType> getParameterTypes() {
		
		List<ParameterType> types = super.getParameterTypes();

		// the input images
		ParameterType type = new ParameterTypeDirectory(PARAMETER_IMAGE_FOLDERNAME, "Folder with input images.", false);
		type.setExpert(false); 
		types.add(type);
		
		ParameterType maskDefinition = new ParameterTypeBoolean(PARAMETER_DEFINE_MASK, "Define mask of the input image.", true, false);
		types.add(maskDefinition);
		
		// the fitness masks
		type = new ParameterTypeDirectory(PARAMETER_FITNESS_FOLDERNAME,"Folder with fitness images.", true);
		type.registerDependencyCondition(new BooleanParameterCondition(this,PARAMETER_DEFINE_MASK, true, true));
		type.setExpert(false);
		types.add(type);

		// the output images
		type = new ParameterTypeDirectory(PARAMETER_RESULTS_FOLDERNAME, "Folder with result images.", false);
		type.setExpert(false); 
		types.add(type);
		
		return types;
	}
}
