#include "common.h"

#include <algorithm>
#include <vector>
#include <iterator>

const int HIST_MAX = 20;

void htmlencode(std::string& data) {
	std::string tmp;
	tmp.reserve(data.size() + 20);

	for (size_t i = 0; i != data.size(); i++) {
		switch (data[i]) {
			case '&':
				tmp += "&amp;";
				break;
			case '\"':
				tmp += "&quot;";
				break;
			case '\'':
				tmp += "&apos;";
				break;
			case '<':
				tmp += "&lt;";
				break;
			case '>':
				tmp += "&gt;";
				break;
			default:
				tmp += data[i];
				break;
		}
	}

	data.swap(tmp);
}

void show_histogram(const Caches& cc, std::string Cache::*ptr, const std::string& caption, bool html, bool sort_by_val) {
	std::map<std::string, int> histogram;
	std::vector<std::pair<std::string, int>> pairs;

	for (auto el : cc)
		histogram[el.*ptr]++;
	std::copy(histogram.begin(), histogram.end(), std::back_inserter(pairs));

	if (sort_by_val)
		sort(pairs.begin(), pairs.end(), [&](std::pair<std::string, int>& a, std::pair<std::string, int>& b) { return a.second > b.second; });
	else
		sort(pairs.begin(), pairs.end(), [&](std::pair<std::string, int>& a, std::pair<std::string, int>& b) { return a.first < b.first; });

	if (html) {
		int max;
		if (sort_by_val)
			max = pairs[0].second;
		else
			max = std::max_element(pairs.begin(), pairs.end(), [&](std::pair<std::string, int>& a, std::pair<std::string, int>& b) { return a.second < b.second; })->second;
		int i = 0;
		std::cout << "<h2>" << caption << "</h2>\n";
		std::cout << "<dl class=\"histogram\">\n";
		for (auto own : pairs) {
			htmlencode(own.first);
			if (own.first.empty()) own.first = "[unknown]";

			if (i < HIST_MAX)
				std::cout << "<dd class=\"bar\" style=\"--percent: " << 100 * own.second / max << "%;\"><span class=\"text\">" << own.first << ": " << own.second << "</span></dd>\n";
			else if (i == HIST_MAX) {
				std::cout << "</dl>\n";
				std::cout << "<div class=\"histogram_others\">";
				std::cout << "  <input id=\"histo_others\" class=\"toggle\" type=\"checkbox\">\n  <label for=\"histo_others\" class=\"lbl-toggle\">See more</label>\n  <div class=\"collapsible-content\">\n    <div class=\"content-inner\">\n";
				std::cout << own.first << " (" << own.second << ")";
			}
			if (i > HIST_MAX)
				std::cout << ", " << own.first << " (" << own.second << ")";
			i++;
		}
		if (i < HIST_MAX)
			std::cout << "</dl>\n";
		else
			std::cout << "   </div>\n  </div>\n</div>\n";
	} else {
		std::cout << caption << '\n';
		for (auto own : pairs)
			std::cout << own.first << ": " << own.second << '\n';
	}
}

static bool same_day(const std::tm& a, const std::tm& b) {
	return (a.tm_year == b.tm_year && a.tm_mon == b.tm_mon && a.tm_mday == b.tm_mday);
}

static void next_day(std::tm& a) {
	a.tm_mday++;
	mktime(&a);
}

int find_streak(const std::multimap<std::time_t, const Cache*>& cc, std::tm& start) {
	int max_str = 0;
	int cur_str = 0;

	std::tm cur_tm;
	cur_tm.tm_year = 0;
	std::tm cur_start_tm = cur_tm;
	start = cur_tm;

	for (auto i : cc) {
		if (same_day(cur_tm, *std::localtime(&i.first)))
			continue;
		next_day(cur_tm);
		if (same_day(cur_tm, *std::localtime(&i.first))) {
			cur_str++;
			continue;
		}
		if (max_str < cur_str) {
			max_str = cur_str;
			start = cur_start_tm;
		}
		cur_str = 1;
		cur_tm = cur_start_tm = *std::localtime(&i.first);
		if (max_str == 0) {
			max_str = 1;
			start = cur_tm;
		}
	}

	if (max_str < cur_str) {
		max_str = cur_str;
		start = cur_start_tm;
	}

	return max_str;
}

long int get_num(char c, char* opt)  {
	try {
		if (std::stoi(opt) > 0) {
			return std::stoi(opt);
		} else {
			std::cout << "Option \"-" << c << "\" requires a valid number as an argument\n";
			std::exit(EXIT_FAILURE);
		}
	}
	catch (...) {
		std::cout << "Option \"-" << c << "\" requires a valid number as an argument\n";
		std::exit(EXIT_FAILURE);
	}
}
