/*
 * anime.cpp: defining of custom anime-related
 * datatypes & variables
 */
#include "core/anime.h"
#include "core/date.h"
#include "core/session.h"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <string>
#include <vector>

namespace Anime {

/* User list data */
bool Anime::IsInUserList() const {
	if (list_info_.get())
		return true;
	return false;
}

void Anime::AddToUserList() {
	list_info_.reset(new ListInformation);
}

void Anime::RemoveFromUserList() {
	list_info_.reset();
}

ListStatus Anime::GetUserStatus() const {
	assert(list_info_.get());
	return list_info_->status;
}

int Anime::GetUserProgress() const {
	assert(list_info_.get());
	return list_info_->progress;
}

int Anime::GetUserScore() const {
	assert(list_info_.get());
	return list_info_->score;
}

Date Anime::GetUserDateStarted() const {
	assert(list_info_.get());
	return list_info_->started;
}

Date Anime::GetUserDateCompleted() const {
	assert(list_info_.get());
	return list_info_->completed;
}

bool Anime::GetUserIsPrivate() const {
	assert(list_info_.get());
	return list_info_->is_private;
}

unsigned int Anime::GetUserRewatchedTimes() const {
	assert(list_info_.get());
	return list_info_->rewatched_times;
}

bool Anime::GetUserIsRewatching() const {
	assert(list_info_.get());
	return list_info_->rewatching;
}

uint64_t Anime::GetUserTimeUpdated() const {
	assert(list_info_.get());
	return list_info_->updated;
}

std::string Anime::GetUserNotes() const {
	assert(list_info_.get());
	return list_info_->notes;
}

void Anime::SetUserStatus(ListStatus status) {
	assert(list_info_.get());
	list_info_->status = status;
}

void Anime::SetUserScore(int score) {
	assert(list_info_.get());
	list_info_->score = score;
}

void Anime::SetUserProgress(int progress) {
	assert(list_info_.get());
	list_info_->progress = progress;
}

void Anime::SetUserDateStarted(Date const& started) {
	assert(list_info_.get());
	list_info_->started = started;
}

void Anime::SetUserDateCompleted(Date const& completed) {
	assert(list_info_.get());
	list_info_->completed = completed;
}

void Anime::SetUserIsPrivate(bool is_private) {
	assert(list_info_.get());
	list_info_->is_private = is_private;
}

void Anime::SetUserRewatchedTimes(int rewatched) {
	assert(list_info_.get());
	list_info_->rewatched_times = rewatched;
}

void Anime::SetUserIsRewatching(bool rewatching) {
	assert(list_info_.get());
	list_info_->rewatching = rewatching;
}

void Anime::SetUserTimeUpdated(uint64_t updated) {
	assert(list_info_.get());
	list_info_->updated = updated;
}

void Anime::SetUserNotes(std::string const& notes) {
	assert(list_info_.get());
	list_info_->notes = notes;
}

/* Series data */
int Anime::GetId() const {
	return info_.id;
}

std::string Anime::GetRomajiTitle() const {
	return info_.title.romaji;
}

std::string Anime::GetEnglishTitle() const {
	return info_.title.english;
}

std::string Anime::GetNativeTitle() const {
	return info_.title.native;
}

std::vector<std::string> Anime::GetTitleSynonyms() const {
	std::vector<std::string> result;
#define IN_VECTOR(v, k) (std::count(v.begin(), v.end(), k))
#define ADD_TO_SYNONYMS(v, k) \
	if (!k.empty() && !IN_VECTOR(v, k) && k != GetUserPreferredTitle()) \
	v.push_back(k)
	ADD_TO_SYNONYMS(result, info_.title.english);
	ADD_TO_SYNONYMS(result, info_.title.romaji);
	ADD_TO_SYNONYMS(result, info_.title.native);
	for (auto& synonym : info_.synonyms) {
		ADD_TO_SYNONYMS(result, synonym);
	}
#undef ADD_TO_SYNONYMS
#undef IN_VECTOR
	return result;
}

int Anime::GetEpisodes() const {
	return info_.episodes;
}

SeriesStatus Anime::GetAiringStatus() const {
	return info_.status;
}

Date Anime::GetAirDate() const {
	return info_.air_date;
}

std::vector<std::string> Anime::GetGenres() const {
	return info_.genres;
}

std::vector<std::string> Anime::GetProducers() const {
	return info_.producers;
}

SeriesFormat Anime::GetFormat() const {
	return info_.format;
}

SeriesSeason Anime::GetSeason() const {
	return info_.season;
}

int Anime::GetAudienceScore() const {
	return info_.audience_score;
}

std::string Anime::GetSynopsis() const {
	return info_.synopsis;
}

int Anime::GetDuration() const {
	return info_.duration;
}

std::string Anime::GetPosterUrl() const {
	return info_.poster_url;
}

std::string Anime::GetServiceUrl() const {
	return "https://anilist.co/anime/" + std::to_string(GetId());
}

void Anime::SetId(int id) {
	info_.id = id;
}

void Anime::SetRomajiTitle(std::string const& title) {
	info_.title.romaji = title;
}

void Anime::SetEnglishTitle(std::string const& title) {
	info_.title.english = title;
}

void Anime::SetNativeTitle(std::string const& title) {
	info_.title.native = title;
}

void Anime::SetTitleSynonyms(std::vector<std::string> const& synonyms) {
	info_.synonyms = synonyms;
}

void Anime::AddTitleSynonym(std::string const& synonym) {
	info_.synonyms.push_back(synonym);
}

void Anime::SetEpisodes(int episodes) {
	info_.episodes = episodes;
}

void Anime::SetAiringStatus(SeriesStatus status) {
	info_.status = status;
}

void Anime::SetAirDate(Date const& date) {
	info_.air_date = date;
}

void Anime::SetGenres(std::vector<std::string> const& genres) {
	info_.genres = genres;
}

void Anime::SetProducers(std::vector<std::string> const& producers) {
	info_.producers = producers;
}

void Anime::SetFormat(SeriesFormat format) {
	info_.format = format;
}

void Anime::SetSeason(SeriesSeason season) {
	info_.season = season;
}

void Anime::SetAudienceScore(int audience_score) {
	info_.audience_score = audience_score;
}

void Anime::SetSynopsis(std::string synopsis) {
	info_.synopsis = synopsis;
}

void Anime::SetDuration(int duration) {
	info_.duration = duration;
}

void Anime::SetPosterUrl(std::string url) {
	info_.poster_url = url;
}

std::string Anime::GetUserPreferredTitle() const {
	switch (session.config.anime_list.language) {
		case TitleLanguage::NATIVE: return (GetNativeTitle().empty()) ? GetRomajiTitle() : GetNativeTitle();
		case TitleLanguage::ENGLISH: return (GetEnglishTitle().empty()) ? GetRomajiTitle() : GetEnglishTitle();
		default: break;
	}
	return GetRomajiTitle();
}

} // namespace Anime
