/*
 *  Brno University of Technology
 *
 *  Copyright (C) 2009-2010 by Brno University of Technology and the contributors
 *
 *  Complete list of developers available at our web site:
 *
 *       http://spl.utko.feec.vutbr.cz
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package cz.vutbr.feec.utko.opencv.operators.imageProcessing;

import java.util.List;

import com.googlecode.javacv.cpp.opencv_core.CvMemStorage;
import com.googlecode.javacv.cpp.opencv_core.CvSeq;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.UserError;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeCategory;

import cz.vutbr.feec.utko.opencv.io.OpenCVGrayImage;

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 Contours extends BaseProcessingOperator {

	private static final String[] MODES = { "EXTERNAL", "LIST", "CCOMP" };
	private static final String PARAM_MODE = "mode";
	private static final String[] APPROXIM = { "CODE", "APPROX_NONE",
			"CV_CHAIN_APPROX_SIMPLE", "CV_CHAIN_APPROX_TC89_L1", "CV_LINK_RUNS" };
	private static final String PARAM_METHODS = "method";

	public Contours(OperatorDescription description) {
		super(description, OpenCVGrayImage.class, OpenCVGrayImage.class);
	}

	@Override
	public IplImage doWork(IplImage cvImg) throws OperatorException {
		int headerSize = com.sun.jna.Native
				.getNativeSize(CvContour.class);
		int mode = getMode(getParameterAsInt(PARAM_MODE)); // CV_RETR_LIST;
		int method = getMethod(getParameterAsInt(PARAM_METHODS), mode); // CV_CHAIN_APPROX_SIMPLE;

		int block_size = 0;
		CvMemStorage storage = cvCreateMemStorage(block_size);

		CvSeq seq = new CvSeq();

		// Memory allocated for OpenCV function operations
		cvFindContours(cvImg, storage, seq, headerSize,
				mode, method);

		return cvImg;

	}

	private int getMethod(int parameterAsInt, int mode) throws UserError {
		switch (parameterAsInt) {
		case 0:
			return CV_CHAIN_CODE;
		case 1:
			return CV_CHAIN_APPROX_NONE;
		case 2:
			return CV_CHAIN_APPROX_SIMPLE;
		case 3:
			return CV_CHAIN_APPROX_TC89_L1;
		case 4:
			return CV_CHAIN_APPROX_TC89_KCOS;
		case 5:
			if (mode != CV_RETR_LIST) {
				throw new UserError(this, 116,
						"The only 'CV_RETR_LIST' mode is allowed for 'CV_LINK_RUNS' method");
			}
			return CV_LINK_RUNS;
		}
		return -1;
	}

	private int getMode(int parameterAsInt) {
		switch (parameterAsInt) {
		case 0:
			// retrive only the extreme outer contours
			return CV_RETR_EXTERNAL;
		case 1:
			// retrieve all the contours and puts them in the list
			return CV_RETR_LIST;
		case 2:
			// retrieve all the contours and organizes them into two-level
			// hierarchy: top level are external boundaries of the components,
			// second level are bounda boundaries of the holes
			return CV_RETR_CCOMP;
		case 3:
			// retrieve all the contours and reconstructs the full hierarchy of
			// nested contours method
			return CV_RETR_TREE;
		}
		return -1;
	}

	@Override
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> p = super.getParameterTypes();
		p.add(new ParameterTypeCategory(PARAM_MODE, "Retrieval mode", MODES, 1));
		p.add(new ParameterTypeCategory(PARAM_METHODS, "Approximation method",
				APPROXIM, 1));
		return p;
	}
}
