// -*- c++ -*-
// Distributed under the BSD 2-Clause License.
// See accompanying file LICENSE for details.
#include "task.h"

#include <iostream>
#include <algorithm>
#include <utility>

Task::Task(const ctor::build_configuration& config_, const ctor::settings& settings_,
           std::string sourceDir_)
	: config(config_)
	, output_system(config.system)
	, settings(settings_)
	, sourceDir(std::move(sourceDir_))
{
}

int Task::registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks)
{
	for(const auto& depStr : depends())
	{
		bool found{false};
		for(const auto& task : tasks)
		{
			if(*task == depStr)
			{
				if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end())
				{
					dependsTasks.push_back(task);
				}
				found = true;
			}
		}
		if(!found)
		{
			std::cerr << "Could not find dependency " << depStr << " needed by " <<
				target() << " target\n";
			return 1;
		}
	}

	return registerDepTasksInner(tasks);
}

bool Task::operator==(const std::string& depStr)
{
	std::filesystem::path generated_output = sourceDir;
	generated_output /= target();
	return
		(!derived() && name() == depStr) || // compare to name
		(!derived() && config.target == depStr) || // compare to stated target (ex. foo.a)
		target() == depStr || // compare to derived (derived to foo.lib on msvc)
		generated_output  == depStr || // not sure what this is for?!
		targetFile().string() == depStr // compare to target output file
		;
}

std::string Task::name() const
{
	return config.name;
}

bool Task::dirty()
{
	for(const auto& task : dependsTasks)
	{
		if(task->dirty())
		{
			return true;
		}
	}

	return dirtyInner();
}

bool Task::ready()
{
	for(const auto& task : dependsTasks)
	{
		if(task->dirty() || task->state() == State::Running)
		{
			return false;
		}
	}

	task_state.store(State::Ready);
	return true;
}

int Task::run()
{
	if(task_state.load() == State::Done)
	{
		return 0;
	}

	task_state.store(State::Running);
	auto ret = runInner();
	if(ret == 0)
	{
		task_state.store(State::Done);
	}
	else
	{
		task_state.store(State::Error);
	}

	return ret;
}

State Task::state() const
{
	return task_state.load();
}

const ctor::build_configuration& Task::buildConfig() const
{
	return config;
}

ctor::target_type Task::targetType() const
{
	return target_type;
}

ctor::language Task::sourceLanguage() const
{
	return source_language;
}

ctor::output_system Task::outputSystem() const
{
	return output_system;
}

std::string Task::compiler() const
{
	const auto& c = ctor::get_configuration();
	switch(sourceLanguage())
	{
	case ctor::language::c:
		switch(outputSystem())
		{
		case ctor::output_system::host:
			return c.get(ctor::cfg::host_cc, "/usr/bin/gcc");
		case ctor::output_system::build:
			return c.get(ctor::cfg::build_cc, "/usr/bin/gcc");
		}
		break;
	case ctor::language::cpp:
		switch(outputSystem())
		{
		case ctor::output_system::host:
			return c.get(ctor::cfg::host_cxx, "/usr/bin/g++");
		case ctor::output_system::build:
			return c.get(ctor::cfg::build_cxx, "/usr/bin/g++");
		}
		break;
	default:
		std::cerr << "Unknown CC target type\n";
		exit(1);
		break;
	}

	std::cerr << "Unhandled compiler!\n";
	exit(1);
}

std::vector<std::shared_ptr<Task>> Task::getDependsTasks()
{
	return dependsTasks;
}