#ifndef _ROAD_SEGMENTATION_H_
#define _ROAD_SEGMENTATION_H_

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
#include<vector>
#include<iomanip>


namespace RoadStateMonitor
{

	using cv::Mat;
	using std::vector;

	enum class ESegment_method
	{
		HSV = 0, CANNY, HOUGH
	};

	class CRoad_segmentation
	{
		Mat segmented_road;
		Mat segmented_image;
		vector<cv::Point> road_indexes;
		Mat histogram;
		static size_t id;

		Mat Canny_track(const Mat&) const;
		Mat Canny(const Mat&) const;
		Mat HSV_split(const Mat&) const;
		Mat Contours(const Mat&, const Mat&) const;
	public:
		CRoad_segmentation() = default;
		/*
		konštruktor pre vytvorenie referenčného objektu, iba tu prebieha samotná segmentácia
		*/
		CRoad_segmentation(const Mat& input, ESegment_method method = ESegment_method::HSV)
		{
			++id;
			segmentation(input, method);
		}

		/*
		konštruktor pre vytvorenie aktuálneho objektu, dostane vypočítané súradnice z konštruktoru referenčného objektu
		*/
		CRoad_segmentation(const Mat& input, const vector<cv::Point>& index): road_indexes(index)
		{
			++id;
			set_segmented_road(input);
			set_segmented_image(input);
			calculate_histogram();

		}
		~CRoad_segmentation() { --id; };



		void segmentation(const Mat&, ESegment_method m = ESegment_method::HSV);
		void set_segmented_road(const Mat&);
		void set_segmented_image(const Mat& input);
		void set_indexes(const Mat&);
		Mat get_road() const { return segmented_road; }
		Mat get_image() const { return segmented_image; }
		Mat get_histogram() const { return histogram; }
		vector<cv::Point> get_indexes() const { return road_indexes; }

		void finish_segmentation(const Mat&, const Mat&);

		void Hough_segmentation(const Mat&);
		void HSV_segmentation(const Mat&);
		void Canny_segmentation(const Mat&);

		inline void draw_road() const;
		inline void draw_image() const;

		inline void calculate_histogram(/*const Mat&, const int* channel_numbers, float channel_range[], const float* channel_ranges, const float* histRange, int hist_size = 256, int number_bins = 256*/);

	};

	void CRoad_segmentation::draw_road() const
	{
		cv::imshow("Segmented Road " + std::to_string(id), segmented_road);
		//cv::waitKey();
		//cv::destroyWindow("Segmented Road " + std::to_string(id));
	}

	void CRoad_segmentation::draw_image() const
	{
		cv::imshow("Segmented Road " + std::to_string(id), segmented_image);
		//cv::waitKey();
		//cv::destroyWindow("Segmented Road " + std::to_string(id));
	}



	void CRoad_segmentation::calculate_histogram()
	{
		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_orig;
		//cv::cvtColor(input, histogram_orig, cv::COLOR_BGR2GRAY);
		//cv::imshow("hist", segmented_road);
		cv::calcHist(&segmented_road, 1, 0, cv::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);
		cv::Mat histImage1(hist_h, hist_w, CV_8UC3, cv::Scalar(0, 0, 0));
		cv::normalize(histogram, histogram, 0, histImage1.rows, cv::NORM_MINMAX, -1, cv::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);
		}



	}

	/*
	
	stanovisko1 -> 1.02 a 0.65
	stanovisko2 -> 1.12 a 0.70
	stanovisko3 -> 1.05 a 0.73
	
	parametrizácia podľa stavu vozovky

	*/
	inline std::string precipitation_intensity(const Mat& ref, const Mat& act)
	{
		if (ref.size() != act.size()) 
			throw std::invalid_argument("Image's sizes aren't equal in precipitation_intensity_test()!"); 

		size_t num_snow_pixels{}, num_rain_pixels{}, num_dry_pixels{};
		for (int r{ 0 }; r < ref.rows; ++r)
			for (int c{ 0 }; c < ref.cols; ++c)
			{
				if (act.at<uchar>(r, c) > (ref.at<uchar>(r, c) * 1.1)) ++num_snow_pixels;
				else if (act.at<uchar>(r, c) < (ref.at<uchar>(r, c) * 0.65)) ++num_rain_pixels;
				else ++num_dry_pixels;
			}
		int pixels = ref.rows * ref.cols;

		//std::cout << std::fixed << std::setprecision(2) << "\nPixels = " << pixels << "\tsnow pixels = "
		//	<< num_snow_pixels << " (" << static_cast<double>(num_snow_pixels) / pixels * 100 << "%)"
		//	<< "\train pixels = " << num_rain_pixels << " (" << static_cast<double>(num_rain_pixels) / pixels * 100 << "%)"
		//	<< "\tdry pixels = " << num_dry_pixels << " (" << static_cast<double>(num_dry_pixels) / pixels * 100 << "%)" << std::endl;

		if (num_dry_pixels > num_rain_pixels && num_dry_pixels > num_snow_pixels) return "DRY ROAD";
		else if (num_rain_pixels > num_dry_pixels && num_rain_pixels > num_snow_pixels) return "WET ROAD " + std::to_string(static_cast<double>(num_rain_pixels) / pixels * 100) + "% ";
		//else if (num_rain_pixels + num_snow_pixels > num_dry_pixels) return "WET & SNOWY ROAD " + std::to_string(static_cast<double>(num_rain_pixels+num_snow_pixels) / pixels * 100) + "% ";
		else return "SNOWY ROAD " + std::to_string(static_cast<double>(num_snow_pixels) / pixels * 100) + "% ";
	}

}
#endif

