/*
 *  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.IplImage;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.conditions.EqualTypeCondition;
import com.rapidminer.parameter.conditions.NonEqualTypeCondition;

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 Crop extends BaseProcessingOperator {
	public Crop(OperatorDescription description) {
		super(description);
	}

	private static final String TYPE_OF_UNIT = "Units";
	private static final String[] TYPES_OF_UNIT = new String[] { "percent % ", "pixels" };

	private static final String PERCENT_LEFT = "Left %";
	private static final String PERCENT_RIGHT = "Right %";
	private static final String PERCENT_TOP = "Top %";
	private static final String PERCENT_BOTTOM = "Bottom %";
	private static final String PX_LEFT = "Left";
	private static final String PX_RIGHT = "Right";
	private static final String PX_TOP = "Top";
	private static final String PX_BOTTOM = "Bottom";

	private static final int UNIT_PERCENT = 0;
	private static final int UNIT_PIXELS = 1;

	@Override
	public IplImage doWork(IplImage img) throws OperatorException {

		int oldHeight = cvGetSize(img).height();
		int oldWidth = cvGetSize(img).width();
		int newHeight = 0;
		int newWidth = 0;
		
		if (getParameterAsInt(TYPE_OF_UNIT) == UNIT_PIXELS) {
			int left = getParameterAsInt(PX_LEFT);
			int right = getParameterAsInt(PX_RIGHT);
			int top = getParameterAsInt(PX_TOP);
			int bottom = getParameterAsInt(PX_BOTTOM);
			
			if(left+right >= oldWidth || top+bottom >= oldHeight){
				throw new OperatorException("Crop bounds must be smaller than original image size.");
			}
			
			newHeight = oldHeight - (top + bottom);
			newWidth = oldWidth - (left+ right);
			
			cvSetImageROI(img, cvRect(left, top, newWidth, newHeight));
		} else {
			double leftP = getParameterAsDouble(PERCENT_LEFT);
			double rightP = getParameterAsDouble(PERCENT_RIGHT);
			double topP = getParameterAsDouble(PERCENT_TOP);
			double bottomP = getParameterAsDouble(PERCENT_BOTTOM);
			
			if(leftP+rightP >= 100 || topP+bottomP >= 100){
				throw new OperatorException("Crop bounds both sides together in % must be smaller than 100.");
			}
			int startX = (int)(oldWidth * leftP);
			int startY = (int)(oldHeight * topP);
			newHeight = (int)(oldHeight * (topP + bottomP));
			newWidth = (int)(oldWidth *(leftP+rightP));
			
			cvSetImageROI(img, cvRect(startX, startY, newWidth, newHeight));
		}
		IplImage outImage = cvCreateImage(cvSize(newWidth, newHeight),
				img.depth(), 3);

		cvResize(img, outImage, CV_INTER_LINEAR);
		cvResetImageROI(img);

		return outImage;
	}

	@Override
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> p = super.getParameterTypes();
		p.add(new ParameterTypeCategory(TYPE_OF_UNIT,
				"Units to determine new dimension.", TYPES_OF_UNIT, 1));

		ParameterType perLeft = new ParameterTypeDouble(PERCENT_LEFT,
				"Percent to cut of from left side.", 0, 99.0, 20.0);
		ParameterType perRight = new ParameterTypeDouble(PERCENT_RIGHT,
				"Percent to cut of from right side.", 0, 99.0, 20.0);
		ParameterType perTop = new ParameterTypeDouble(PERCENT_TOP,
				"Percent to cut of from top side.", 0, 99.0, 20.0);
		ParameterType perBottom = new ParameterTypeDouble(PERCENT_BOTTOM,
				"Percent to cut of from bottom side.", 0, 99.0, 20.0);
		perLeft.setDefaultValue(20.0);
		perRight.setDefaultValue(20.0);
		perTop.setDefaultValue(20.0);
		perBottom.setDefaultValue(20.0);
		
		perLeft.registerDependencyCondition(new NonEqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		perRight.registerDependencyCondition(new NonEqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		perTop.registerDependencyCondition(new NonEqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		perBottom.registerDependencyCondition(new NonEqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		
		ParameterType pxLeft = new ParameterTypeInt(PX_LEFT,
				"Pixels to cut of from left side.", 0, 9999, 20);
		ParameterType pxRight = new ParameterTypeInt(PX_RIGHT,
				"Pixels to cut of from right side.", 0, 9999, 20);
		ParameterType pxTop = new ParameterTypeInt(PX_TOP,
				"Pixels to cut of from top side.", 0, 9999, 20);
		ParameterType pxBottom = new ParameterTypeInt(PX_BOTTOM,
				"Pixels to cut of from buttom side.", 0, 9999, 20);

		pxLeft.registerDependencyCondition(new EqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		pxRight.registerDependencyCondition(new EqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		pxTop.registerDependencyCondition(new EqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));
		pxBottom.registerDependencyCondition(new EqualTypeCondition(this,
				TYPE_OF_UNIT, TYPES_OF_UNIT, true, UNIT_PIXELS));

		
		p.add(perLeft);
		p.add(perRight);
		p.add(perTop);
		p.add(perBottom);
		p.add(pxLeft);
		p.add(pxRight);
		p.add(pxTop);
		p.add(pxBottom);
		return p;
	}
}
