/*
 * Mirai. Copyright (C) 2024 Vyn
 * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
 * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
 */

#include "View.h"
#include "utils.h"
#include <iostream>
#include <iterator>
#include <ranges>

namespace mirai
{

View::View(Mirai *mirai) : mirai_(mirai)
{
}

void View::hideCompletedTasks(bool hide)
{
	shouldHideCompletedTasks_ = hide;
}

bool View::shouldHideCompletedTasks() const
{
	return shouldHideCompletedTasks_;
}

std::vector<Date> View::getDates()
{
	std::vector<Date> datesVector;
	for (auto &date : dates) {
		datesVector.push_back(date.first);
	}
	return datesVector;
}

std::vector<Task> View::getTasksForDate(const Date &date)
{
	return dates.at(date).tasks;
}

std::vector<Event> View::getEventsForDate(const Date &date)
{
	return dates.at(date).events;
}

std::vector<Task> View::getUnscheduledTasks()
{
	return unscheduledTasks_;
}

void View::update()
{
	dates.clear();
	unscheduledTasks_.clear();

	auto todayDate = Date(std::chrono::system_clock::now());

	for (int sourceId : sourcesIds_) {
		Source *source = mirai_->getSourceById(sourceId);
		for (auto day : source->getDays()) {
			// day's tasks
			auto sourceTasks = day.tasks() | std::ranges::views::filter([&](const Task &task) {
								   if (day.date() >= todayDate) {
									   return true;
								   }
								   if (shouldHideCompletedTasks()) {
									   return task.checked() == false;
								   }
								   return true;
							   });
			if (day.date() >= todayDate || std::ranges::distance(sourceTasks) > 0) {

				if (!dates.contains(day.date())) {
					dates.insert_or_assign(day.date(), DateView{});
				}
				auto &tasks = dates.at(day.date()).tasks;
				tasks.insert(tasks.end(), sourceTasks.begin(), sourceTasks.end());
			}

			// day's events
			auto sourceEvents = day.events() | std::ranges::views::filter([&](const Event &event) {
									return (day.date() >= todayDate) ||
										   findFirst(event.queryTasks(), [&](const Task &task) {
											   return task.checked() == false;
										   }).has_value();
								});

			if (day.date() >= todayDate || std::ranges::distance(sourceEvents) > 0) {
				if (!dates.contains(day.date())) {
					dates.insert_or_assign(day.date(), DateView{});
				}
				auto &events = dates.at(day.date()).events;
				events.insert(events.end(), sourceEvents.begin(), sourceEvents.end());
			}
		}
		// unscheduled tasks
		auto tasks = source->getUnscheduledTasks();
		unscheduledTasks_.insert(unscheduledTasks_.end(), tasks.begin(), tasks.end());
	}
}

void View::removeSources()
{
	sourcesIds_.clear();
}

void View::addSource(const Source &source)
{
	sourcesIds_.push_back(source.id);
}

void View::setSources(const std::vector<Source> &sources)
{
	sourcesIds_.clear();
	std::transform(
		sources.begin(), sources.end(), std::back_inserter(sourcesIds_),
		[](const Source &source) {
			return source.id;
		}
	);
}

void View::setAllSources()
{
	const auto &sources = mirai_->getSources();
	sourcesIds_.clear();
	std::transform(
		sources.begin(), sources.end(), std::back_inserter(sourcesIds_),
		[](const std::unique_ptr<Source> &source) {
			return source->id;
		}
	);
}

bool View::isSourceSelected(const Source &source) const
{
	for (auto &sourceId : sourcesIds_) {
		if (sourceId == source.id) {
			return true;
		}
	}
	return false;
}

size_t View::activeSourceCount() const
{
	return sourcesIds_.size();
}

} // namespace mirai
