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

#include <filesystem>
#include <iostream>

#include <cassert>

ToolChain getToolChain(OutputSystem system)
{
	std::string compiler;
	switch(system)
	{
	case OutputSystem::Host:
		compiler = getConfiguration(cfg::host_cxx, "g++");
		break;
	case OutputSystem::Build:
		compiler = getConfiguration(cfg::build_cxx, "g++");
		break;
	}

	std::filesystem::path cc(compiler);
	auto cc_cmd = cc.stem().string();

	// Note: "g++" is a substring of "clang++" so "clang++" must be tested first.
	if(cc_cmd.find("clang++") != std::string::npos)
	{
		return ToolChain::clang;
	}
	else if(cc_cmd.find("g++") != std::string::npos)
	{
		return ToolChain::gcc;
	}

	std::cerr << "Unsupported output system.\n";
	return ToolChain::gcc;
}

namespace
{
std::vector<std::string> getOptionGcc(opt option, const std::string& arg)
{
	switch(option)
	{
	case opt::output:
		return {"-o", arg};
	case opt::debug:
		return {"-g"};
	case opt::strip:
		return {"-s"};
	case opt::warn_all:
		return {"-Wall"};
	case opt::warnings_as_errors:
		return {"-Werror"};
	case opt::generate_dep_tree:
		return {"-MMD"};
	case opt::no_link:
		return {"-c"};
	case opt::include_path:
		return {"-I" + arg};
	case opt::library_path:
		return {"-L" + arg};
	case opt::link:
		return {"-l" + arg};
	case opt::cpp_std:
		return {"-std=" + arg};
	case opt::build_shared:
		return {"-shared"};
	case opt::threads:
		return {"-pthread"};
	case opt::optimization:
		return {"-O" + arg};
	case opt::position_independent_code:
		return {"-fPIC"};
	case opt::position_independent_executable:
		return {"-fPIE"};
	case opt::custom:
		return {arg};
	}

	std::cerr << "Unsupported compiler option.\n";
	return {};
}
}

std::vector<std::string> getOption(ToolChain tool_chain,
                                   opt option,
                                   const std::string& arg)
{
	switch(tool_chain)
	{
	case ToolChain::gcc:
	case ToolChain::clang:
		return getOptionGcc(option, arg);
	}

	std::cerr << "Unsupported tool-chain.\n";
	return {};
}

namespace {
std::pair<opt, std::string> getOptionGcc(const std::string& flag)
{
	if(flag.substr(0, 2) == "-I")
	{
		std::string path = flag.substr(2);
		path.erase(0, path.find_first_not_of(' '));
		return { opt::include_path, path };
	}

	if(flag.substr(0, 2) == "-L")
	{
		std::string path = flag.substr(2);
		path.erase(0, path.find_first_not_of(' '));
		return { opt::library_path, path };
	}

	return { opt::custom, flag };
}
}

std::pair<opt, std::string> getOption(const std::string& flag,
                                      ToolChain tool_chain)
{
	switch(tool_chain)
	{
	case ToolChain::gcc:
	case ToolChain::clang:
		return getOptionGcc(flag);
	}

	std::cerr << "Unsupported tool-chain.\n";
	return { opt::custom, flag };
}