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

#include <iostream>
#include <fstream>

#include "ctor.h"
#include "execute.h"
#include "util.h"
#include "tools.h"

TaskAR::TaskAR(const ctor::build_configuration& config_,
               const ctor::settings& settings_,
               const std::string& target,
               const std::vector<std::string>& objects,
               const std::string& sourceDir_)
	: Task(config_, settings_, sourceDir_)
	, _targetFile(target)
	, config(config_)
	, settings(settings_)
	, sourceDir(sourceDir_)
{
	target_type = ctor::target_type::static_library;
	output_system = config.system;

	auto toolchain = getToolChain(config.system);
	_targetFile = extension(toolchain, target_type, config.system, _targetFile);
	for(const auto& object : objects)
	{
		std::filesystem::path objectFile = object;
		objectFiles.push_back(objectFile);
		dependsStr.push_back(objectFile.string());
	}

	for(const auto& dep : config.depends)
	{
		depFiles.emplace_back(dep);
	}

	flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
	flagsFile += ".flags";

	source_language = ctor::language::c;
	for(const auto& source : config.sources)
	{
		std::filesystem::path sourceFile(source.file);
		// TODO: Use task languages instead
		if(sourceFile.extension().string() != ".c")
		{
			source_language = ctor::language::cpp;
		}
	}

	std::filesystem::create_directories(targetFile().parent_path());
}

bool TaskAR::dirtyInner()
{
	if(!std::filesystem::exists(targetFile()))
	{
		return true;
	}

	if(!std::filesystem::exists(flagsFile))
	{
		return true;
	}

	{
		auto lastFlags = readFile(flagsFile.string());
		if(flagsString() != lastFlags)
		{
			//std::cout << "The compiler flags changed\n";
			return true;
		}
	}

	return false;
}

int TaskAR::runInner()
{
	auto toolchain = getToolChain(config.system);

	std::vector<std::string> args;
	for(const auto& flag : config.flags.arflags)
	{
		append(args, to_strings(toolchain, flag));
	}
	append(args, ar_option(toolchain, ctor::ar_opt::replace));
	append(args, ar_option(toolchain, ctor::ar_opt::add_index));
	append(args, ar_option(toolchain, ctor::ar_opt::create));
	append(args, ar_option(toolchain, ctor::ar_opt::output, targetFile().string()));
	for(const auto& task : getDependsTasks())
	{
		args.push_back(task->targetFile().string());
	}

	{ // Write flags to file.
		std::ofstream flagsStream(flagsFile);
		flagsStream << flagsString();
	}

	if(settings.verbose == 0)
	{
		std::string output = "AR => " + targetFile().string() + '\n';
		std::cout << output << std::flush;
	}

	const auto& c = ctor::get_configuration();
	std::string tool;
	switch(outputSystem())
	{
	case ctor::output_system::host:
		tool = c.get(ctor::cfg::host_ar, "/usr/bin/ar");
		break;
	case ctor::output_system::build:
		tool = c.get(ctor::cfg::build_ar, "/usr/bin/ar");
		break;
	}

	return execute(settings, tool, args, c.env);
}

int TaskAR::clean()
{
	if(std::filesystem::exists(targetFile()))
	{
		std::cout << "Removing " << targetFile().string() << "\n";
		std::filesystem::remove(targetFile());
	}

	if(std::filesystem::exists(flagsFile))
	{
		std::cout << "Removing " << flagsFile.string() << "\n";
		std::filesystem::remove(flagsFile);
	}

	return 0;
}

std::vector<std::string> TaskAR::depends() const
{
	std::vector<std::string> deps;
	for(const auto& objectFile : objectFiles)
	{
		deps.push_back(objectFile.string());
	}

	for(const auto& dep : config.depends)
	{
		deps.push_back(dep);
	}

	return deps;
}

std::string TaskAR::target() const
{
	return _targetFile.string();
}

std::filesystem::path TaskAR::targetFile() const
{
	return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
}

bool TaskAR::derived() const
{
	return false;
}

std::string TaskAR::flagsString() const
{
	auto toolchain = getToolChain(config.system);
	std::string flagsStr;
	bool first{true};
	for(const auto& flag : config.flags.ldflags)
	{
		for(const auto& str : to_strings(toolchain, flag))
		{
			if(first)
			{
				flagsStr += " ";
				first = false;
			}
			flagsStr += str;
		}
	}

	for(const auto& dep : config.depends)
	{
		if(dep != config.depends[0])
		{
			flagsStr += " ";
		}
		flagsStr += dep;
	}

	auto deps = depends();
	for(const auto& dep : deps)
	{
		flagsStr += " ";
		flagsStr += dep;
	}

	return flagsStr;
}