/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "Utils.hpp"

#ifdef _MSC_VER
	#include <Windows.h>
#else
	#include <pthread.h>
#endif
#include <fstream>
#include <filesystem>
#include <string>
#include <locale>
#include <codecvt>

namespace OpenVulkano
{
	std::atomic_int Utils::uniqueTypeID = 0;

	void Utils::SetThreadName(const std::string& name)
	{
#ifdef _MSC_VER
		auto thisThread = ::GetCurrentThread();
		std::wstring namew(name.begin(), name.end());
		SetThreadDescription(thisThread, namew.c_str());
#elif __APPLE__
		pthread_setname_np(name.c_str());
#else
		pthread_setname_np(pthread_self(), name.c_str());
#endif
	}

	std::string Utils::GetThreadName()
	{
#ifdef _MSC_VER
		auto thisThread = ::GetCurrentThread();
		PWSTR threadName;
		GetThreadDescription(thisThread, &threadName);
		std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
		std::string str = converter.to_bytes(threadName);
		LocalFree(threadName);
		return str;
#else
		// NOTE(vb): Accoring to linux man page size of this array must be _at least_ 16 bytes...
		char threadName[64];
		pthread_getname_np(pthread_self(), threadName, sizeof(threadName));
		std::string str = threadName;
		return str;
#endif
	}

	uint64_t Utils::GetThreadId()
	{
#ifdef _MSC_VER
		return (uint64_t)::GetThreadId(::GetCurrentThread());
#else
		return (uint64_t) pthread_self();
#endif
	}

	template<typename T>
	Array<char> Utils::ReadFile(const T& filePath, bool emptyOnMissing, bool nullTerminateString)
	{
		std::ifstream file(filePath, std::ios::ate | std::ios::binary);
		if (!file.is_open())
		{
			if (emptyOnMissing) return {};
			if constexpr (std::is_same_v<std::remove_reference_t<std::remove_cv_t<T>>, std::filesystem::path>)
			{
				throw std::runtime_error("Failed to open file '" + filePath.string() + "'!");
			}
			else
			{
				throw std::runtime_error("Failed to open file '" + filePath + "'!");
			}
		}
		const size_t fileSize = static_cast<size_t>(file.tellg());
		Array<char> data(fileSize + nullTerminateString);
		file.seekg(0);
		file.read(data.Data(), fileSize);
		if (nullTerminateString) data[fileSize] = '\0';
		file.close();
		return data;
	}

	template Array<char> Utils::ReadFile<std::string>(const std::string& filePath, bool emptyOnMissing, bool nullTerminateString);
	template Array<char> Utils::ReadFile<std::filesystem::path>(const std::filesystem::path& filePath,
	                                                            bool emptyOnMissing, bool nullTerminateString);

}
