#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/utility.hpp>
#include<opencv2/features2d.hpp>

#include<iostream>
#include<vector>
#include<fstream>
#include<chrono>
#include<stdexcept>
#include<filesystem>
#include<sstream>


#include"Road_segmentation.h"
#include"weather.h"
#include"testing_helpers.h"

using namespace RoadStateMonitor;
//using namespace WeatherAnalysis;


/*

finálna spúšťacia metóda, ktorá vyhodnocuje stav vozovky, detekciu hmly a detekciu zrážok + parametrizácia
výsledky sú zapísané do textového súboru a pre ukážku aj priamo do obrázku
*/

bool run(const std::vector<std::string>& image_names, const std::vector<cv::Mat>& loaded_images)
{
	if (image_names.empty() ||
		loaded_images.empty() ||
		image_names.size() != loaded_images.size())
	{
		throw std::invalid_argument("Input file corrupted in test_road_state()!\n");
	}

	std::ofstream out_file{ "rychlotest.txt" };

	int tresh{};
	EWeather_method method_weather = EWeather_method::CANNY;
	cv::Mat ref_image_clone = loaded_images[0].clone();
	cv::cvtColor(ref_image_clone, ref_image_clone, cv::COLOR_BGR2GRAY);
 

	/*
	príprava oblasti pre detekciu hmly a výpočet referencie
	*/
	cv::Mat cropped_image = loaded_images[0](cv::Rect(0, 0, loaded_images[0].cols, 10 * loaded_images[0].rows / 100));
	CFog ref_cfog(cropped_image, tresh, method_weather);
	ref_cfog.calculate_histogram(ref_image_clone);
	ref_cfog.calculate_contrast(cropped_image);
	double ref_cfog_fft = ref_cfog.FFT(cropped_image);


	/*
	segmentácia vozovky
	*/
	ESegment_method method_segment = ESegment_method::HSV;
	CRoad_segmentation ref_croad(loaded_images[0], method_segment);
	ref_croad.calculate_histogram();
	ref_croad.draw_image();

	int res_prev = 0, intensity{ -1 };


	out_file << "Ref. image = " << image_names[0] << "\n\n";


	/*
	cyklus, ktorý spracováva všetky obrázky v priečinku, porovnáva aktuálny s referencioua  výsledok zapíše do textového súboru a aktuálneho obrázku
	*/
	for (size_t i{ 1 }; i < image_names.size(); ++i)
	{
		cv::Mat result = loaded_images[i].clone();
		ref_image_clone = loaded_images[i].clone();
		cv::cvtColor(ref_image_clone, ref_image_clone, cv::COLOR_BGR2GRAY);
		//cv::imshow("clone1", ref_clone);

		/*
		detekcia hmly
		*/
		cropped_image = loaded_images[i](cv::Rect(0, 0, loaded_images[i].cols, 10 * loaded_images[i].rows / 100));
		auto start = std::chrono::high_resolution_clock::now();
		CFog act_cfog(cropped_image, ref_cfog.get_high_tresh(), method_weather);
		act_cfog.calculate_histogram(ref_image_clone);
		act_cfog.calculate_contrast(cropped_image);
		ref_cfog_fft = act_cfog.FFT(cropped_image);
		//act.is_foggy(ref);
		double hist_corell_fog = cv::compareHist(ref_cfog.get_histogram(), act_cfog.get_histogram(), cv::HISTCMP_CORREL);
		out_file << "Actual image (number" << i <<") = " << image_names[i] << "\n"
			<< std::setw(35) << std::right << act_cfog.is_foggy(ref_cfog) << std::setw(35) << std::right << "Estimated visibility = " << act_cfog.visibility(ref_cfog) << "%" << "\n";

		/*
		detekcia zrážok
		*/
		int res = image_subtraction(loaded_images[i - 1], loaded_images[i]);

		out_file << std::setw(35) << std::right;

		
		if (res >= res_prev + 400)
		{
			intensity = intensity + ((res - res_prev) / 400);
			out_file << " -> Precipitation is increasing\t";
		}
		else if (res <= res_prev - 400)
		{
			intensity = intensity - ((res_prev - res) / 400);
			//if (intensity < 0) intensity = 0;
			out_file << " -> Precipitation is decreasing\t";
		}
		else out_file << " -> Precipitation is the same\t\t\t";

		if (intensity < 0) intensity = 0;

		std::cout << "Intensity == " << intensity << "\n\n\n";
		out_file << std::setw(40) << std::right << "Precipitation intensity = " << intensity << "\n";
		res_prev = res;

		/*
		analýza vozovky
		*/
		CRoad_segmentation actual_road(loaded_images[i], ref_croad.get_indexes());
		actual_road.calculate_histogram();
		double hist_corell_road = cv::compareHist(ref_croad.get_histogram(), actual_road.get_histogram(), cv::HISTCMP_CORREL);

		auto stop = std::chrono::high_resolution_clock::now();
		auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);

		out_file << std::setw(75) << std::right << RoadStateMonitor::precipitation_intensity(ref_croad.get_road(), actual_road.get_road())  << "\nCalculation time = " << duration.count() << "ms \n\n";


		std::ostringstream strs;
		strs << act_cfog.visibility(ref_cfog);
		std::string string_visibility = strs.str();

		std::string fog_out = act_cfog.is_foggy(ref_cfog) + string_visibility;
		fog_out += "%";
		std::string road_out = RoadStateMonitor::precipitation_intensity(ref_croad.get_road(), actual_road.get_road());

		std::string precipitation_out;

		/*
		parametrizácia intenzity zrážok
		*/
		if (intensity >= 0 && intensity <= 2)
			precipitation_out = "No or Light precipitation";
		else if(intensity >= 3 && intensity <= 5)
			precipitation_out = "Medium precipitation";
		else if (intensity >= 6)
			precipitation_out = "High precipitation";
		//else
		//	precipitation_out = "No precipitation";


		std::cout << "------------------------------------------------------------\n";
		std::cout << fog_out << "\t" << road_out << "\t" << precipitation_out << "\n";



		cv::putText(result, fog_out, cv::Point(15, 10), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
		cv::putText(result, road_out, cv::Point(15, 30), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
		cv::putText(result, precipitation_out, cv::Point(15, 50), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);



		cv::imshow("Output", result);
		cv::waitKey(1500);
	}

	out_file.close();

	return true;
}

/*
metóda detekcie zrážok, ktorá vyhľadáva podobné objekty v obraze podľanastavených parametrov
*/
void blob_detector(cv::Mat& input)
{
	cv::SimpleBlobDetector::Params params;

	params.thresholdStep = 5;
	params.minThreshold = 100;
	params.maxThreshold = 255;
	params.minRepeatability = 1;
	params.minDistBetweenBlobs = 2;

	params.filterByArea = true;
	params.minArea = 5;
	params.maxArea = 20;

	params.filterByColor = true;
	params.blobColor = 255;

	params.filterByCircularity = false;
	params.minCircularity = 0.0f;
	params.maxCircularity = std::numeric_limits<float>::max();

	params.filterByConvexity = false;
	params.minConvexity = 0.0f;
	params.maxConvexity = std::numeric_limits<float>::max();

	params.filterByInertia = false;

	cv::Ptr<cv::SimpleBlobDetector> detector = cv::SimpleBlobDetector::create(params);
	std::vector<cv::KeyPoint> keypoints;
	detector->detect(input, keypoints);
	drawKeypoints(input, keypoints, input, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::imshow("ref", input);
	//cv::imwrite("blob.png", res);
	cv::waitKey(0);

}



int main()
{
	try 
	{
		/*
		priečinok, z ktorého budú načítané obrázky
		*/
		const std::string dir_path = ".\\pocasie\\rychlotest_snezenie\\";
		vector<std::string> image_names;
		vector<Mat> loaded_images = load_images(dir_path, image_names);



		//cv::imshow("vstup", loaded_images[0]);

		//test_road_state(image_names, loaded_images);
		//test_fog(image_names, loaded_images);
		//test_snowing(image_names, loaded_images);

		if (run(image_names, loaded_images)) std::cout << "Succes\n";
		else std::cerr << "Error\n";

		cv::waitKey();
		

		return 0;
		
	}
	catch (cv::Exception& e)
	{
		std::cerr << "Exception caught in main()!\n" << e.what();
	}
	catch (std::exception& e)
	{
		std::cerr << "Exception caught in main()!\n" << e.what();
	}
}