/*
** Copyright (c) 2014-2018, Eren Okka
**
** This Source Code Form is subject to the terms of the Mozilla Public
** License, v. 2.0. If a copy of the MPL was not distributed with this
** file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

#include <algorithm>

#include "keyword.h"
#include "token.h"

namespace anitomy {

KeywordManager keyword_manager;

KeywordManager::KeywordManager() {
	const KeywordOptions options_default;
	const KeywordOptions options_invalid{true, true, false};
	const KeywordOptions options_unidentifiable{false, true, true};
	const KeywordOptions options_unidentifiable_invalid{false, true, false};
	const KeywordOptions options_unidentifiable_unsearchable{false, false, true};

	Add(kElementAnimeSeasonPrefix, options_unidentifiable, {U"saison", U"season"});

	Add(kElementAnimeType, options_unidentifiable,
		{U"gekijouban", U"movie", U"oad", U"oav", U"ona", U"ova", U"special", U"specials", U"tv"});
	Add(kElementAnimeType, options_unidentifiable_unsearchable, {U"sp"}); // e.g. "Yumeiro Patissiere SP Professional"
	Add(kElementAnimeType, options_unidentifiable_invalid,
		{U"ed", U"ending", U"nced", U"ncop", U"op", U"opening", U"preview", U"pv"});

	Add(kElementAudioTerm, options_default,
		{// Audio channels
		 U"2.0ch", U"2ch", U"5.1", U"5.1ch", U"dts", U"dts-es", U"dts5.1", U"truehd5.1",
		 // Audio codec
		 U"aac", U"aacx2", U"aacx3", U"aacx4", U"ac3", U"eac3", U"e-ac-3", U"flac", U"flacx2", U"flacx3", U"flacx4",
		 U"lossless", U"mp3", U"ogg", U"vorbis",
		 // Audio language
		 U"dualaudio", U"dual audio"});

	Add(kElementDeviceCompatibility, options_default, {U"ipad3", U"iphone5", U"ipod", U"ps3", U"xbox", U"xbox360"});
	Add(kElementDeviceCompatibility, options_unidentifiable, {U"android"});

	Add(kElementEpisodePrefix, options_default,
		{U"ep", U"ep.", U"eps", U"eps.", U"episode", U"episode.", U"episodes", U"capitulo", U"episodio",
		 U"epis\u00f3dio", U"folge"});
	Add(kElementEpisodePrefix, options_invalid,
		{U"e", U"\x7b2c"}); // single-letter episode keywords are not valid tokens

	Add(kElementFileExtension, options_default,
		{U"3gp", U"avi", U"divx", U"flv", U"m2ts", U"mkv", U"mov", U"mp4", U"mpg", U"ogm", U"rm", U"rmvb", U"ts",
		 U"webm", U"wmv"});
	Add(kElementFileExtension, options_invalid,
		{U"aac", U"aiff", U"flac", U"m4a", U"mp3", U"mka", U"ogg", U"wav", U"wma", U"7z", U"rar", U"zip", U"ass",
		 U"srt"});

	Add(kElementLanguage, options_default, {U"eng", U"english", U"espanol", U"jap", U"pt-br", U"spanish", U"vostfr"});
	Add(kElementLanguage, options_unidentifiable, {U"esp", U"ita"}); // e.g. "Tokyo ESP", "Bokura ga Ita"

	Add(kElementOther, options_default,
		{U"remaster", U"remastered", U"uncensored", U"uncut", U"ts", U"vfr", U"widescreen", U"ws"});

	Add(kElementReleaseGroup, options_default, {U"thora"});

	Add(kElementReleaseInformation, options_default, {U"batch", U"complete", U"patch", U"remux"});
	Add(kElementReleaseInformation, options_unidentifiable,
		{U"end", U"final"}); // e.g. "The End of Evangelion", "Final Approach"

	Add(kElementReleaseVersion, options_default, {U"v0", U"v1", U"v2", U"v3", U"v4"});

	Add(kElementSource, options_default,
		{U"bd",		 U"bdrip",	 U"bluray",	 U"blu-ray", U"dvd",	 U"dvd5",	U"dvd9",
		 U"dvd-r2j", U"dvdrip",	 U"dvd-rip", U"r2dvd",	 U"r2j",	 U"r2jdvd", U"r2jdvdrip",
		 U"hdtv",	 U"hdtvrip", U"tvrip",	 U"tv-rip",	 U"webcast", U"webrip"});

	Add(kElementSubtitles, options_default,
		{U"ass", U"big5", U"dub", U"dubbed", U"hardsub", U"hardsubs", U"raw", U"softsub", U"softsubs", U"sub",
		 U"subbed", U"subtitled"});

	Add(kElementVideoTerm, options_default,
		{// Frame rate
		 U"23.976fps", U"24fps", U"29.97fps", U"30fps", U"60fps", U"120fps",
		 // Video codec
		 U"8bit", U"8-bit", U"10bit", U"10bits", U"10-bit", U"10-bits", U"hi10", U"hi10p", U"hi444", U"hi444p",
		 U"hi444pp", U"h264", U"h265", U"h.264", U"h.265", U"x264", U"x265", U"x.264", U"avc", U"hevc", U"hevc2",
		 U"divx", U"divx5", U"divx6", U"xvid", U"av1",
		 // Video format
		 U"avi", U"rmvb", U"wmv", U"wmv3", U"wmv9",
		 // Video quality
		 U"hq", U"lq",
		 // Video resolution
		 U"hd", U"sd"});

	Add(kElementVolumePrefix, options_default, {U"vol", U"vol.", U"volume"});
}

void KeywordManager::Add(ElementCategory category, const KeywordOptions& options,
						 const std::initializer_list<string_t>& keywords) {
	auto& keys = GetKeywordContainer(category);
	for (const auto& keyword : keywords) {
		if (keyword.empty())
			continue;
		if (keys.find(keyword) != keys.end())
			continue;
		keys.insert(std::make_pair(keyword, Keyword{category, options}));
	}
}

bool KeywordManager::Find(ElementCategory category, const string_t& str) const {
	const auto& keys = GetKeywordContainer(category);

	auto it = keys.find(FullCaseFold(str));
	if (it != keys.end() && it->second.category == category)
		return true;

	return false;
}

bool KeywordManager::Find(const string_t& str, ElementCategory& category, KeywordOptions& options) const {
	const auto& keys = GetKeywordContainer(category);
	auto it = keys.find(str);
	if (it != keys.end()) {
		if (category == kElementUnknown) {
			category = it->second.category;
		} else if (it->second.category != category) {
			return false;
		}
		options = it->second.options;
		return true;
	}

	return false;
}

string_t KeywordManager::Normalize(const string_t& str) const {
	return FullCaseFold(str);
}

KeywordManager::keyword_container_t& KeywordManager::GetKeywordContainer(ElementCategory category) const {
	return category == kElementFileExtension ? const_cast<keyword_container_t&>(file_extensions_)
											 : const_cast<keyword_container_t&>(keys_);
}

////////////////////////////////////////////////////////////////////////////////

void KeywordManager::Peek(const string_t& filename, const TokenRange& range, Elements& elements,
						  std::vector<TokenRange>& preidentified_tokens) const {
	using entry_t = std::pair<ElementCategory, std::vector<string_t>>;
	static const std::vector<entry_t> entries{
		{kElementAudioTerm,		{U"Dual Audio"}					   },
		{kElementVideoTerm,		{U"H264", U"H.264", U"h264", U"h.264"}},
		{kElementVideoResolution, {U"480p", U"720p", U"1080p"}		  },
		{kElementSource,			 {U"Blu-ray"}							 }
	  };

	auto it_begin = filename.begin() + range.offset;
	auto it_end = it_begin + range.size;

	for (const auto& entry : entries) {
		for (const auto& keyword : entry.second) {
			auto it = std::search(it_begin, it_end, keyword.begin(), keyword.end());
			if (it != it_end) {
				const auto offset = static_cast<size_t>(std::distance(filename.begin(), it));
				elements.insert(entry.first, keyword);
				preidentified_tokens.push_back(TokenRange{offset, keyword.size()});
			}
		}
	}
}

} // namespace anitomy
