#include<iostream>
#include<numeric>

#include "weather.h"



size_t CFog::id = 0;




/*
metóda detekcie zrážok s použitím modelu prostredia
*/
int image_subtraction(const Mat& background, const Mat& actual)
{
	cv::Mat tresh_back, gray_back, tresh_act, gray_act;

	//cv::imshow("background", background);
	//cv::imshow("actual", actual);

	cv::cvtColor(background, gray_back, cv::COLOR_BGR2GRAY);
	cv::cvtColor(actual, gray_act, cv::COLOR_BGR2GRAY);


	/*
	stanovisko1 tresh = 120
	stanovisko2 tresh = 95
	stanovisko3 tresh = 120
	*/
	cv::threshold(gray_back, tresh_back, 95, 255, cv::THRESH_BINARY /*+ cv::THRESH_OTSU*/);
	cv::threshold(gray_act, tresh_act, 95, 255, cv::THRESH_BINARY /*+ cv::THRESH_OTSU*/);

	//cv::imshow("tresh_back", tresh_back);
	//cv::imshow("tresh_act", tresh_act);
	cv::Mat res = tresh_act - tresh_back;
	

	std::vector<std::vector<cv::Point>> conts;
	cv::findContours(res, conts, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

	std::vector<int> indexes(conts.size());
	std::iota(indexes.begin(), indexes.end(), 0);

	sort(indexes.begin(), indexes.end(), [&conts](int lhs, int rhs) 
		{
			return conts[lhs].size() > conts[rhs].size();
		});

	double area{}, tresh_area{7};
	size_t index = 0;

	for (size_t i = 0; i < indexes.size(); i++)
	{
		area = cv::contourArea(conts[indexes[i]], false);
		if (area >= tresh_area)
		{
			drawContours(res, conts, static_cast<int>(indexes[i]), cv::Scalar(0, 0, 0), cv::FILLED, 0);
		}

		else break;
	}

	//cv::imshow("actual", tresh_act);
	//cv::imshow("background", tresh_back);
	//cv::imshow("rozdiel", res);
	//cv::waitKey();
	
	//cv::imwrite("model1-model2.png", res);
	return cv::countNonZero(res);
}



void CFog::calculate_histogram(const Mat& input)
{
	int hist_size = 256; //pocet intenzit
	const int* channel_numbers = { 0 }; //0pre grayscale image
	float channel_range[] = { 0.0, 256.0 };
	const float* channel_ranges = channel_range;
	const float* hist_range = { channel_range };
	int number_bins = hist_size;

	//Mat histogram;
	//cv::cvtColor(input, histogram_orig, cv::COLOR_BGR2GRAY);

	cv::calcHist(&input, 1, 0, Mat(), histogram, 1, &number_bins, &channel_ranges);

	//histogram = hist;


	int hist_w = 512, hist_h = 400;
	int bin_w = cvRound((double)hist_w / hist_size);
	Mat histImage1(hist_h, hist_w, CV_8UC3, cv::Scalar(0, 0, 0));
	cv::normalize(histogram, histogram, 0, histImage1.rows, cv::NORM_MINMAX, -1, Mat());
	for (int i = 1; i < hist_size; ++i)
	{
		cv::line(histImage1, cv::Point(bin_w * (i - 1), hist_h - cvRound(histogram.at<float>(i - 1))), cv::Point(bin_w * (i), hist_h - cvRound(histogram.at<float>(i))), cv::Scalar(255, 0, 0), 2, 8, 0);
	}

	//cv::imshow("orig", segmented_road);
	//cv::imshow("hist orig", histImage1);
	//cv::imshow("hist orig" + std::to_string(id), histImage1);

}

/*
výpočet počtu hrán s automatickým Canny detektorom
*/
void CFog::Canny(const Mat& input)
{
	cv::Mat tresh, canny_mat, gray;
	cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);

	if (high_tresh == 0)
	{
		double otsu_thresh_val = cv::threshold(gray, tresh, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);

		high_tresh = otsu_thresh_val;
	}

	cv::Canny(input, canny_mat, high_tresh/2, high_tresh);
	//cv::imshow("Canny edges" + std::to_string(id), canny_mat);
	//cv::waitKey();
	edges = cv::countNonZero(canny_mat);
}

/*
Canny detektor s pomocou trackbaru
*/
void CFog::Canny_track(const Mat& input) 
{
	
	Mat canny_mat;

	cv::namedWindow("Canny track " + std::to_string(id));
	cv::resizeWindow("Canny track " + std::to_string(id), 500, 500);
	int tresh1 = 0;
	int tresh2 = 0;
	cv::createTrackbar("Tresh1", "Canny track " + std::to_string(id), 0, 255);
	cv::createTrackbar("Tresh2", "Canny track " + std::to_string(id), 0, 255);
	while (1)
	{
		int tresh1 = cv::getTrackbarPos("Tresh1", "Canny track " + std::to_string(id));
		int tresh2 = cv::getTrackbarPos("Tresh2", "Canny track " + std::to_string(id));

		cv::Canny(input, canny_mat, tresh1, tresh2, 3);
		cv::imshow("Canny track " + std::to_string(id), canny_mat);
		char key = cv::waitKey(1) & 0xFF;
		if (key == ('q'))
		{
			edges = cv::countNonZero(canny_mat);
			std::cout << "Canny track = " << edges << std::endl;
			cv::destroyWindow("Canny");
			//cv::imwrite("canny" + std::to_string(id) + ".png", canny_mat);
			return;
		}
	}
}

/*
výpočet kontrastu
*/
void CFog::calculate_contrast(const Mat& input)
{
	Mat sobel_mat;
	cv::Sobel(input, sobel_mat, CV_16S, 0, 1, 5);



	cv::Scalar smean, sstdev;
	cv::meanStdDev(sobel_mat, smean, sstdev);
	
	contrast = abs(smean[0]);

}

/*
analýza obrazu vo frekvenčnej oblasti
*/
double CFog::FFT(const Mat& input)
{
	cv::Mat gray;
	cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);
	Mat FFT_mat = computeDFT(gray);
	fft_mat = magnitude_spectrum(FFT_mat);
	cv::Scalar _mean = cv::mean(fft_mat);


	return _mean[0];
}

cv::Mat computeDFT(const Mat& image) {
	Mat padded;                           

	/*
	rozšírenie obrázku na vhodné rozmery doplnení núl na hranice
	*/
	int optimal_rows = cv::getOptimalDFTSize(image.rows);
	int optimal_cols = cv::getOptimalDFTSize(image.cols); 
	copyMakeBorder(image, padded, 0, optimal_rows - image.rows, 0, optimal_cols - image.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	Mat planes[] = { cv::Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
	Mat complex;
	/*
	rozšírenie o ďalší kanál pre uloženie komplexných čísel
	*/
	merge(planes, 2, complex);         

	/*
	samotný výpočet diskrétnej Fourierovej transformácie
	*/
	dft(complex, complex, cv::DFT_COMPLEX_OUTPUT);  
	return complex;
}

void fft_Shift(Mat magI) {

	/*
	úprava riadku/stĺpca na sudé číslo
	*/
	magI = magI(cv::Rect(0, 0, magI.cols & -2, magI.rows & -2));

	int cx = magI.cols / 2;
	int cy = magI.rows / 2;

	/*
	jednotlivé kvadranty
	*/
	Mat q0(magI, cv::Rect(0, 0, cx, cy));   
	Mat q1(magI, cv::Rect(cx, 0, cx, cy));  
	Mat q2(magI, cv::Rect(0, cy, cx, cy));  
	Mat q3(magI, cv::Rect(cx, cy, cx, cy)); 


	/*
	presun kvadrantov, aby v strede boli nízke frekvencie
	*/
	Mat tmp;                            
	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);
	q1.copyTo(tmp);                     
	q2.copyTo(q1);
	tmp.copyTo(q2);
}


Mat magnitude_spectrum(const Mat& complex) {
	Mat magI;
	Mat planes[] = {
		Mat::zeros(complex.size(), CV_32F),
		Mat::zeros(complex.size(), CV_32F)
	};

	/*
	rozdelenie an reálnu a imaginárnu zložku
	výpočet magnitúdy
	prevod do logaritmických mierok
	*/
	split(complex, planes);
	magnitude(planes[0], planes[1], magI); 
	magI += cv::Scalar::all(1);
	
	log(magI, magI); 
	
	fft_Shift(magI); 
	/*
	normalizácia do zobraziteľného formátu
	*/
	normalize(magI, magI, 1, 0, cv::NORM_INF);
	//imshow("Magnitude Spectrum", magI);

	//cv::Scalar d = cv::mean(magI);
	//std::cout << d << std::endl;

	return magI;
}

void CFog::weather_analysis(const Mat& input, EWeather_method method)
{
	if (method == EWeather_method::CANNY) Canny(input);
	else if(method == EWeather_method::CANNY_TRACK) Canny_track(input);
	else if (method == EWeather_method::CONTRAST) calculate_contrast(input);
	else FFT(input);
}

