package cz.vutbr.feec.utko.opencv.operators.lineOperations;
/**
 * Implementation of the line approximate operator
 * 
 * The functions implements method for a approximate polynomial curve to input points 
 * 
 * Author: Martin Hasmanda
 */
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Vector;

import com.googlecode.javacv.cpp.opencv_core.CvPoint;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
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 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.lineOperations.lineOperationsClasses.GradientLineBoundary;


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


public class LineApproximateOperator extends Operator {

	protected InputPort inOpenCV = getInputPorts().createPort("color image 1");
	protected OutputPort outOpenCV = getOutputPorts().createPort("color image out");
	
	protected InputPort inPoints = getInputPorts().createPort("Pts In");
	protected OutputPort outPoints = getOutputPorts().createPort("Pts Out");
	
	private final String ORDER = "order of approxiamation";
	private final String SAMPLES = "Count of curve samples ";
	
	public LineApproximateOperator(OperatorDescription description) {
		super(description);
		inOpenCV.addPrecondition(new SimplePrecondition(inOpenCV, new MetaData(OpenCVIOObject.class)));
		getTransformer().addRule(new GenerateNewMDRule(outOpenCV, OpenCVIOObject.class));		
		inPoints.addPrecondition(new SimplePrecondition(inPoints, new MetaData(PointsIOObject.class)));
		getTransformer().addRule(new GenerateNewMDRule(outPoints, PointsIOObject.class));
	}

	public void doWork(/*IplImage img*/) throws OperatorException {
		
		OpenCVIOObject cvIO = inOpenCV.getData();
		IplImage img = cvIO.getImage();
		
		// get input points
		PointsIOObject src = inPoints.getData();
		PointsIOObject res = new PointsIOObject(img.width(), img.height(), 0, 0);
		
		Collection<InterrestArea> pts = src.getPoints();
		ArrayList<InterrestArea> objects = new ArrayList<InterrestArea>(pts);
		
		int order = getParameterAsInt(ORDER);
		int samples = getParameterAsInt(SAMPLES);
		
		// approximate
		GradientLineBoundary grad = new GradientLineBoundary();
		Vector<CvPoint> points = new Vector<CvPoint>();
		
		for (int i = 0; i < objects.size(); i++) {
			points.add(cvPoint(objects.get(i).x, objects.get(i).y));
		}
		
		Vector<Double> poly = grad.GetPolynomialPoints(points, order);
		Vector<CvPoint> resPts = grad.GetApproximatePoints(img, poly, samples, order);
		
		// draw result points
		for (int i = 0; i < resPts.size(); i++) {
			CvPoint pt = resPts.get(i);
			if(pt.x()>img.width()-1 || pt.x()<0 || pt.y()>img.height()-1 || pt.y()<0)
				continue;
			cvCircle(img, pt, 3, CV_RGB(255, 255, 0), 1, 1, 0);
			res.addPoint(resPts.get(i).x(), resPts.get(i).y());
		}
		
		// approximate points
		outPoints.deliver(res);
		
		
		cvIO.setImage(img);
		outOpenCV.deliver(cvIO);
	}

	/**
	 * List of parameters for the algorithm
	 */
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> types = super.getParameterTypes();
		
		types.add(new ParameterTypeInt(ORDER, "Order of approximation ", 2, 6, 3));
		types.add(new ParameterTypeInt(SAMPLES, "Samples of a curve", 10, 1000, 20));
		
		return types;
	}
}
