package cz.vutbr.feec.utko.opencv.operators.lineOperations;
/**
 * Implementation of the gradient line boundary operator
 * 
 * The functions implements method for a line operations 
 * and finding the maximum gradient on the perpendicular vector
 * 
 * Author: Martin Hasmanda
 */

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

import java.util.ArrayList;
import java.util.Collection;
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.operator.ports.metadata.GenerateNewMDRule;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.operator.ports.metadata.SimplePrecondition;

import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.ParameterTypeString;

import cz.vutbr.feec.imageprocessing.IOObject.PointsIOObject;
import cz.vutbr.feec.imageprocessing.IOObject.innerObject.InterrestArea;
import cz.vutbr.feec.utko.opencv.io.OpenCVIOObject;
import cz.vutbr.feec.utko.opencv.operators.imageProcessing.BaseProcessingOperator;
import cz.vutbr.feec.utko.opencv.operators.lineOperations.lineOperationsClasses.GradientLineBoundary;

public class GradientLineBoundaryOperator extends BaseProcessingOperator {

//	protected InputPort inOpenCV = getInputPorts().createPort("color image 1");
//	protected OutputPort outOpenCV = getOutputPorts().createPort("color image out");
//	
	protected OutputPort outOpenCVMask = getOutputPorts().createPort("color image mask");
	
	protected InputPort inLine = getInputPorts().createPort("Line In");
	protected OutputPort outPointsLeft = getOutputPorts().createPort("points Out L");
	protected OutputPort outPointsRight = getOutputPorts().createPort("points Out R");
	
	private static final String DIST_TO_LINE = "Distance to line";
	private static final String LINE_SAMPLES = "Count of samples";
	private static final String GRAD_MAX = "Max boundary of gradient";
	private static final String LINE_ORDER = "Approximation order";

		
	public GradientLineBoundaryOperator(OperatorDescription description) {
		super(description);
//		inOpenCV.addPrecondition(new SimplePrecondition(inOpenCV, new MetaData(OpenCVIOObject.class)));
//		getTransformer().addRule(new GenerateNewMDRule(outOpenCV, OpenCVIOObject.class));
		getTransformer().addRule(new GenerateNewMDRule(outOpenCVMask, OpenCVIOObject.class));
		inLine.addPrecondition(new SimplePrecondition(inLine, new MetaData(PointsIOObject.class)));
		getTransformer().addRule(new GenerateNewMDRule(outPointsLeft, PointsIOObject.class));
		getTransformer().addRule(new GenerateNewMDRule(outPointsRight, PointsIOObject.class));
	}

//	public void doWork() throws OperatorException {
//		
	public IplImage doWork(IplImage cvImg) throws OperatorException {
		
		IplImage img = cvCloneImage(cvImg);
		
		PointsIOObject srcPts = inLine.getData();
		PointsIOObject ptsL = new PointsIOObject(img.width(), img.height(), 0, 0);
		PointsIOObject ptsR = new PointsIOObject(img.width(), img.height(), 0, 0);

		IplImage result = null;
		
		if(img.nChannels() == 3)
		{
			result = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
			cvCvtColor( img, result, CV_RGB2GRAY );
		}			
		else if(img.nChannels() == 4)
		{			
			result = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
			cvCvtColor( img, result, CV_BGRA2GRAY );
		}
		else
			result = cvCloneImage(img);
		
		// get input line
		Collection<InterrestArea> coll = srcPts.getPoints();
		ArrayList<InterrestArea> objects = new ArrayList<InterrestArea>(coll);
		CvPoint ptIn = cvPoint(objects.get(0).x,objects.get(0).y); 
		CvPoint ptOut = cvPoint(objects.get(1).x,objects.get(1).y);
		
		GradientLineBoundary grad = new GradientLineBoundary();

		GradientLineBoundary.Line src = grad.CreateLine(ptIn, ptOut);

		int distToLine  = getParameterAsInt(DIST_TO_LINE);
		int lineSample = getParameterAsInt(LINE_SAMPLES);
		int gradMaxVal  = getParameterAsInt(GRAD_MAX);
		int actOrder = getParameterAsInt(LINE_ORDER);
		
	    ////////////////////////////////////////////////////////////////////////
	    // finding lines points
	    GradientLineBoundary.LineBoundaryParams params = 
	    	grad.CreateLineSample(cvGetSize(result), src, distToLine, lineSample);
	    
	    // finding gradients
	    IplImage sample = cvCloneImage(result);

	    cvNormalize(sample, sample, 0, 255, CV_MINMAX, null);
	    
	    cvSmooth(sample, sample, CV_MEDIAN, 9, 9, 0, 0);
	    
	    grad.FindGradients(sample, params, gradMaxVal);

	    // approximate line
	    sample = cvCloneImage(result);
	    
	    grad.ApproximateLine(sample, params, actOrder);
	    
	    IplImage sampleRgb = cvCreateImage(cvSize(img.width(), img.height()), 8, 3);
	    cvCvtColor(sample, sampleRgb, CV_GRAY2BGR);
	    grad.CreateDebugImage(sampleRgb, params, false, true, false, true, false);
	    //grad.CreateDebugImage(sampleRgb, params, false, false, false, true, true);
	    
	    // compute the mask
	    IplImage mask = grad.CreateMaskLine(sampleRgb, params);
	    
	    ////////////////////////////////////////////////////////////////////////
	    // save points L
	    for (CvPoint p : params.res2) {
			ptsL.addPoint(new InterrestArea(p.x(), p.y()));
		}
	    
	    // save points L
	    for (CvPoint p : params.res3) {
			ptsR.addPoint(new InterrestArea(p.x(), p.y()));
		}

	    img = cvCloneImage(sampleRgb);
	   /* 
	    IplImage imgdown = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	    cvZero(imgdown);
	    
	    // save points L
	    for (CvPoint p : params.res3) {
	    	cvDrawCircle(imgdown, new CvPoint(p.x(), p.y()), 1, cvScalarAll(255), 1, 8,1);
		}
*/
	    

	    outPointsLeft.deliver(ptsL);
		outPointsRight.deliver(ptsR);
		
//		cvIO.setImage(img);
//		outOpenCV.deliver(cvIO);

		// result mask
		outOpenCVMask.deliver(new OpenCVIOObject(mask));
		
		return img;
	}
	
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> types = super.getParameterTypes();
	
		types.add(new ParameterTypeInt(DIST_TO_LINE, "Maximal distance to line", 0, 200, 100));
		types.add(new ParameterTypeInt(LINE_SAMPLES, "Count of samples for sampling a line", 0, 1000, 200));
		types.add(new ParameterTypeInt(GRAD_MAX, "Maximal gradient in direction from a line", 0, 255, 10));
		types.add(new ParameterTypeInt(LINE_ORDER, "Line order for approximate a line", 1, 5, 2));
		
		
		return types;
	}
}
