#include "rebuild.h"

#include <iostream>
#include <filesystem>
#include <algorithm>

#include "execute.h"
#include "configure.h"
#include "settings.h"
#include "libcppbuild.h"

std::array<BuildConfigurationEntry, 1024> configFiles;
std::size_t numConfigFiles{0};

// TODO: Use c++20 when ready, somehing like this:
//int reg(const std::source_location location = std::source_location::current())
int reg(const char* location, std::vector<BuildConfiguration> (*cb)())
{
	// NOTE: std::cout cannot be used here
	if(numConfigFiles >= configFiles.size())
	{
		fprintf(stderr, "Max %d build configurations currently supported.\n",
		        (int)configFiles.size());
		exit(1);
	}

	configFiles[numConfigFiles].file = location;
	configFiles[numConfigFiles].cb = cb;
	++numConfigFiles;

	return 0;
}

int unreg(const char* location)
{
	std::size_t found{0};
	for(std::size_t i = 0; i < numConfigFiles;)
	{
		if(std::string(location) == configFiles[i].file)
		{
			++found;
			for(std::size_t j = i; j < numConfigFiles; ++j)
			{
				configFiles[j] = configFiles[j + 1];
			}
			--numConfigFiles;
		}
		else
		{
			++i;
		}
	}

	return found;
}

void recompileCheck(const Settings& settings, int argc, char* argv[], bool force)
{
	bool dirty{force};

	std::vector<std::string> args;
	args.push_back("-s");
	args.push_back("-O3");
	args.push_back("-std=c++17");
	args.push_back("-pthread");

	std::filesystem::path binFile("cppbuild");

	if(std::filesystem::exists(configurationFile))
	{
		args.push_back(configurationFile.string());

		if(std::filesystem::last_write_time(binFile) <=
		   std::filesystem::last_write_time(configurationFile))
		{
			dirty = true;
		}

		const auto& c = configuration();
		if(&c == &default_configuration)
		{
			// configuration.cc exists, but currently compiled with the default one.
			dirty = true;
		}
	}

	if(settings.verbose > 1)
	{
		std::cout << "Recompile check (" << numConfigFiles << "):\n";
	}

	for(std::size_t i = 0; i < numConfigFiles; ++i)
	{
		std::string location = configFiles[i].file;
		if(settings.verbose > 1)
		{
			std::cout << " - " << location << "\n";
		}
		std::filesystem::path configFile(location);
		if(std::filesystem::last_write_time(binFile) <=
		   std::filesystem::last_write_time(configFile))
		{
			dirty = true;
		}

		// Support adding multiple config functions from the same file
		if(std::find(args.begin(), args.end(), location) == std::end(args))
		{
			args.push_back(location);
		}
	}
	args.push_back("libcppbuild.a");
	args.push_back("-o");
	args.push_back(binFile.string());

	if(dirty)
	{
		std::cout << "Rebuilding config\n";
		auto tool = getConfiguration(cfg::build_cxx, "/usr/bin/g++");
		auto ret = execute(tool, args, settings.verbose > 0);
		if(ret != 0)
		{
			std::cerr << "Failed: ." << ret << "\n";
			exit(1);
		}
		else
		{
			std::cout << "Re-launch\n";
			std::vector<std::string> args;
			for(int i = 1; i < argc; ++i)
			{
				args.push_back(argv[i]);
			}
			exit(execute(argv[0], args, settings.verbose > 0));
		}
	}
}