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

#include <iostream>
#include <fstream>

#include "libctor.h"
#include "settings.h"
#include "execute.h"
#include "util.h"

TaskSO::TaskSO(const BuildConfiguration& config,
               const Settings& settings,
               const std::string& target,
               const std::vector<std::string>& objects,
               const std::string& sourcePath)
	: Task(config)
	, config(config)
	, settings(settings)
{
	std::filesystem::path base = settings.builddir;
	base /= sourcePath;
	std::filesystem::create_directories(base);

	targetFile = base / target;
	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)
	{
		std::filesystem::path depFile = settings.builddir;
		depFile /= dep;
		depFiles.push_back(depFile);
	}

	flagsFile = base / targetFile.stem();
	flagsFile += ".flags";

	target_type = TargetType::DynamicLibrary;
	source_language = 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 = Language::Cpp;
		}
	}
}

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

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

	for(const auto& objectFile : objectFiles)
	{
		if(std::filesystem::last_write_time(targetFile) <=
		   std::filesystem::last_write_time(objectFile))
		{
			return true;
		}
	}

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

	return false;
}

int TaskSO::runInner()
{
	std::string objectlist;
	for(const auto& objectFile : objectFiles)
	{
		if(!objectlist.empty())
		{
			objectlist += " ";
		}
		objectlist += objectFile.string();
	}

	std::vector<std::string> args;

	args.push_back("-fPIC");
	args.push_back("-shared");

	args.push_back("-o");
	args.push_back(targetFile.string());

	for(const auto& objectFile : objectFiles)
	{
		args.push_back(objectFile.string());
	}

	for(const auto& depFile : depFiles)
	{
		args.push_back(depFile.string());
	}

	for(const auto& flag : config.ldflags)
	{
		args.push_back(flag);
	}

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

	if(settings.verbose == 0)
	{
		std::cout << "LD => " << targetFile.string() << "\n";
	}

	auto tool = compiler();
	return execute(tool, args, settings.verbose > 0);
}

int TaskSO::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> TaskSO::depends() const
{
	std::vector<std::string> deps;
	for(const auto& objectFile : objectFiles)
	{
		deps.push_back(objectFile.string());
	}

	for(const auto& depFile : depFiles)
	{
		deps.push_back(depFile.string());
	}

	return deps;
}

std::string TaskSO::target() const
{
	return targetFile.string();
}

std::string TaskSO::flagsString() const
{
	std::string flagsStr = compiler();
	for(const auto& flag : config.ldflags)
	{
		flagsStr += " " + flag;
	}
	flagsStr += "\n";

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

	return flagsStr;
}