package cz.vutbr.feec.utko.objectTracking.example;

import org.opencv.features2d.Features2d;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.opencv.core.Core;
import org.opencv.core.CvType;
//import org.opencv.core.DMatch;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;

public class Template {
/*
	private int IdOfSizeMatcher=200;							//STARÁ VERZIA
	private int IdOfDistanceMatch=200;
	int IdOfContourMatcher=200;	
	private int IdOfColorMatcher=200;
	int IdOfTemplateMatcher=200;*/	
	private double match=0;
	private int counter=0;
	private boolean occupied=false;
	private int id;
	private Point predictedState;	
	private int nearestSizeChange=200;
	private double nearestSizeDistance=200;
	private double speedInX=0;
	private double speedInY=0;
	double BhattacharyyaCoeficient=50;
	int numberOfGoodCountours=0;
	//private int DIST_LIMIT = 80;
	private Mat image = new Mat();
	private Rect placement;
	/*private Mat descriptors = new Mat();
	private MatOfKeyPoint keypoints = new MatOfKeyPoint();
	private MatOfDMatch matches = new MatOfDMatch();
	private FeatureDetector Fdetector = FeatureDetector.create(FeatureDetector.ORB);
	private DescriptorExtractor Fdescriptor = DescriptorExtractor.create(DescriptorExtractor.ORB);
	private DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);*/
	private Mat colorHistogram;	
	public Point getPredictedState() {
		return predictedState;
	}
	public void setPredictedState(Point predictedState) {
		this.predictedState = predictedState;
	}
	public int getNumberOfGoodContours()
	{
		return numberOfGoodCountours;
	}
	public void setNumberOfGoodContours(int numberOfGoodContours)
	{
		numberOfGoodCountours=numberOfGoodContours;
	}
	public int getId() 
	{
		return id;
	}
	public void setId(int index) 
	{
		id=index;
	}
	/*public int getIdOfSizeMatcher() {								//STARÁ VERZIA
		return IdOfSizeMatcher;
	}

	public void setIdOfSizeMatcher(int idOfSizeMatcher) {
		IdOfSizeMatcher = idOfSizeMatcher;
	}
	public int getIdOfDistanceMatch() {
		return IdOfDistanceMatch;
	}

	public void setIdOfDistanceMatch(int idOfDistanceMatch) {
		IdOfDistanceMatch = idOfDistanceMatch;
	}
	public int getIdOfContourMatcher() {
		return IdOfContourMatcher;
	}

	public void setIdOfContourMatcher(int idOfContourMatcher) {
		IdOfContourMatcher = idOfContourMatcher;
	}

	public int getIdOfTemplateMatcher() {
		return IdOfTemplateMatcher;
	}

	public void setIdOfTemplateMatcher(int idOfTemplateMatcher) {
		IdOfTemplateMatcher = idOfTemplateMatcher;
	}

	public int getIdOfColorMatcher() {
		return IdOfColorMatcher;
	}

	public void setIdOfColorMatcher(int idOfColorMatcher) {
		IdOfColorMatcher = idOfColorMatcher;
	}*/
	public int getNearestSizeChange() {
		return nearestSizeChange;
	}

	public void setNearestSizeChange(int nearestSizeChange) {
		this.nearestSizeChange = nearestSizeChange;
	}


	public double getNearestSizeDistance() {
		return nearestSizeDistance;
	}

	public void setNearestSizeDistance(double nearestSizeDistance) {
		this.nearestSizeDistance = nearestSizeDistance;
	}



	public double getBhattacharyyaCoeficient() {
		return BhattacharyyaCoeficient;
	}

	public void setBhattacharyyaCoeficient(double bhattacharyyaCoeficient) {
		BhattacharyyaCoeficient = bhattacharyyaCoeficient;
	}

	/*public int getDIST_LIMIT() {
		return DIST_LIMIT;
	}

	public void setDIST_LIMIT(int dIST_LIMIT) {
		DIST_LIMIT = dIST_LIMIT;
	}*/

	public Mat getImage() {
		return image;
	}

	public void setImage(Mat image) {
		this.image = image;
	}

	public Rect getPlacement() {
		return placement;
	}

	public void setPlacement(Rect placement) {
		this.placement = placement;
	}

	/*public Mat getDescriptors() {
		return descriptors;
	}

	public void setDescriptors(Mat descriptors) {
		this.descriptors = descriptors;
	}

	public MatOfKeyPoint getKeypoints() {
		return keypoints;
	}

	public void setKeypoints(MatOfKeyPoint keypoints) {
		this.keypoints = keypoints;
	}

	public MatOfDMatch getMatches() {
		return matches;
	}

	public void setMatches(MatOfDMatch matches) {
		this.matches = matches;
	}

	public FeatureDetector getFdetector() {
		return Fdetector;
	}

	public void setFdetector(FeatureDetector fdetector) {
		Fdetector = fdetector;
	}

	public DescriptorExtractor getFdescriptor() {
		return Fdescriptor;
	}

	public void setFdescriptor(DescriptorExtractor fdescriptor) {
		Fdescriptor = fdescriptor;
	}

	public DescriptorMatcher getMatcher() {
		return matcher;
	}

	public void setMatcher(DescriptorMatcher matcher) {
		this.matcher = matcher;
	}*/

	public Mat getColorHistogram() {
		return colorHistogram;
	}
	public Rect getRect() {
		return placement;
	}
	public Template(Mat frame, Rect placement) {
		// TODO Auto-generated constructor stub
		this.image = frame.submat(placement);
		this.placement = placement;
		/*Fdetector.detect(image, keypoints);					
		Fdescriptor.compute(image, keypoints, descriptors);*/
		this.colorHistogram = histogram();
		
		Point state=new Point();//iniciálna korekcia stavu
		state.x=(placement.x+placement.width)/2;
		state.y=(placement.y+placement.height)/2;
		setPredictedState(state);
	}

	/*public double match(Template otherImage) {								//MATCH Z VERZIE 0.1
		if (otherImage.getDescriptor().cols() <= descriptors.cols()) {
			matcher.match(otherImage.getDescriptor(), descriptors, matches);
		} else {
			matcher.match(descriptors, otherImage.getDescriptor(), matches);
		}
		List<DMatch> matchesList = matches.toList();
		List<DMatch> matches_final = new ArrayList<DMatch>();

		
		for (final DMatch match : matchesList) {
			if (match.distance <= DIST_LIMIT) {
				matches_final.add(match);
			}
		}
		MatOfDMatch matches_final_mat = new MatOfDMatch();
		matches_final_mat.fromList(matches_final);
		if (matchesList.isEmpty()) {
			return 0;
		} else {
			return matches_final.size() / matchesList.size();
		}
	}

	public Mat getDescriptor() {
		return descriptors;
	}*/


	public Mat histogram() 
	{
		Mat imageForHistogram=doBackgroundRemoval(image); 
	    Mat greyimage = new Mat(image.height(), image.width(), CvType.CV_8UC2); 
	    Imgproc.cvtColor(imageForHistogram, greyimage, Imgproc.COLOR_RGB2GRAY);
	    Vector<Mat> bgr_planes = new Vector<Mat>();                                                                                                                                                                                 
	    Core.split(greyimage, bgr_planes);
	    MatOfInt histSize = new MatOfInt(256);
	    final MatOfFloat histRange = new MatOfFloat(0f, 256f);
	    boolean accumulate = false;
	    Mat b_hist = new  Mat();
	    Imgproc.calcHist(bgr_planes, new MatOfInt(0),new Mat(), b_hist, histSize, histRange, accumulate);
	    return b_hist;
	}
	public Point getPoint()
	{
		Point templatePoint= new Point();
		templatePoint.x= (getRect().x+getRect().width)/2;
		templatePoint.y=(getRect().y+getRect().height)/2;
		return templatePoint;
	}
	public double getDistance(Template otherTemp)
	{
		double distance=Math.sqrt(Math.pow(getPoint().x-otherTemp.getPoint().x, 2)+Math.pow(getPoint().y-otherTemp.getPoint().y, 2));
		return distance;
	}
	private Mat doBackgroundRemoval(Mat frame)
	{
		// init
		Mat hsvImg = new Mat();
		List<Mat> hsvPlanes = new ArrayList<>();
		Mat thresholdImg = new Mat();
		
		int thresh_type = Imgproc.THRESH_BINARY_INV;
		
		// threshold the image with the average hue value
		hsvImg.create(frame.size(), CvType.CV_8U);
		Imgproc.cvtColor(frame, hsvImg, Imgproc.COLOR_BGR2HSV);
		Core.split(hsvImg, hsvPlanes);
		
		// get the average hue value of the image
		double threshValue = this.getHistAverage(hsvImg, hsvPlanes.get(0));
		
		Imgproc.threshold(hsvPlanes.get(0), thresholdImg, threshValue, 179.0, thresh_type);
		
		Imgproc.blur(thresholdImg, thresholdImg, new Size(5, 5));
		
		// dilate to fill gaps, erode to smooth edges
		Imgproc.dilate(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 1);
		Imgproc.erode(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 3);
		
		Imgproc.threshold(thresholdImg, thresholdImg, threshValue, 179.0, Imgproc.THRESH_BINARY);
		
		// create the new image
		Mat foreground = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
		frame.copyTo(foreground, thresholdImg);
		
		return foreground;
	}
	private double getHistAverage(Mat hsvImg, Mat hueValues)
	{
		// init
		double average = 0.0;
		Mat hist_hue = new Mat();
		// 0-180: range of Hue values
		MatOfInt histSize = new MatOfInt(180);
		List<Mat> hue = new ArrayList<>();
		hue.add(hueValues);
		
		// compute the histogram
		Imgproc.calcHist(hue, new MatOfInt(0), new Mat(), hist_hue, histSize, new MatOfFloat(0, 179));
		
		// get the average Hue value of the image
		// (sum(bin(h)*h))/(image-height*image-width)
		// -----------------
		// equivalent to get the hue of each pixel in the image, add them, and
		// divide for the image size (height and width)
		for (int h = 0; h < 180; h++)
		{
			// for each bin, get its value and multiply it for the corresponding
			// hue
			average += (hist_hue.get(h, 0)[0] * h);
		}
		
		// return the average hue of the image
		return average = average / hsvImg.size().height / hsvImg.size().width;
	}
	public double getSpeedInY() {
		return speedInY;
	}
	public void setSpeedInY(double speedInY) {
		this.speedInY = speedInY;
	}
	public double getSpeedInX() {
		return speedInX;
	}
	public void setSpeedInX(double speedInX) {
		this.speedInX = speedInX;
	}
	public boolean getOccupied() {
		return occupied;
	}
	public void setOccupied(boolean occupied) {
		this.occupied = occupied;
	}
	public double getMatch() {
		return match;
	}
	public void setMatch(double match) {
		this.match = match;
	}
	public int getCounter() {
		return counter;
	}
	public void incrementCounter() {
		this.counter++;
	}

}
