#include <selectorParse.h>
#include <stdexcept>
#include <cctype>

namespace lcamera_dev {

namespace {

SelectorCriterionKind parseCriterionKind(const std::string& prefixToken)
{
	if (prefixToken == "lcamera-id") {
		return SelectorCriterionKind::LibcameraId;
	}
	if (prefixToken == "index") {
		return SelectorCriterionKind::Index;
	}
	if (prefixToken == "model-substr") {
		return SelectorCriterionKind::ModelSubstr;
	}
	if (prefixToken == "model") {
		return SelectorCriterionKind::Model;
	}
	if (prefixToken == "location") {
		return SelectorCriterionKind::Location;
	}

	throw std::runtime_error(
		"Unknown deviceSelector prefix: " + prefixToken);
}

SelectorCriterion parseCriterionClause(const std::string& clause)
{
	const std::string trimmedClause = trimWhitespace(clause);
	if (trimmedClause.empty()) {
		throw std::runtime_error("Empty deviceSelector clause");
	}

	const size_t colonPos = trimmedClause.find(':');
	if (colonPos == std::string::npos)
	{
		return SelectorCriterion{
			SelectorCriterionKind::LibcameraId,
			trimmedClause
		};
	}

	const std::string prefixToken = trimWhitespace(
		trimmedClause.substr(0, colonPos));
	const std::string value = trimWhitespace(
		trimmedClause.substr(colonPos + 1));

	if (value.empty())
	{
		throw std::runtime_error(
			"deviceSelector clause has empty value for prefix "
			+ prefixToken);
	}

	return SelectorCriterion{
		parseCriterionKind(prefixToken),
		value
	};
}

} // namespace

std::string trimWhitespace(const std::string& text)
{
	size_t start = 0;
	while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start]))) {
		++start;
	}

	size_t end = text.size();
	while (end > start
		&& std::isspace(static_cast<unsigned char>(text[end - 1])))
	{
		--end;
	}

	return text.substr(start, end - start);
}

std::vector<SelectorCriterion> parseDeviceSelector(
	const std::string& deviceSelector)
{
	const std::string trimmedSelector = trimWhitespace(deviceSelector);
	if (trimmedSelector.empty()) {
		throw std::runtime_error("deviceSelector is empty");
	}

	std::vector<SelectorCriterion> criteria;
	std::string currentClause;
	currentClause.reserve(trimmedSelector.size());

	for (size_t i = 0; i < trimmedSelector.size(); ++i)
	{
		const char ch = trimmedSelector[i];
		if (ch == '\\' && i + 1 < trimmedSelector.size()
			&& trimmedSelector[i + 1] == ';')
		{
			currentClause.push_back(';');
			++i;
			continue;
		}

		if (ch == ';')
		{
			criteria.push_back(parseCriterionClause(currentClause));
			currentClause.clear();
			continue;
		}

		currentClause.push_back(ch);
	}

	criteria.push_back(parseCriterionClause(currentClause));
	return criteria;
}

} // namespace lcamera_dev
