/*
 * 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 "WebResourceLoader.hpp"
#include "IO/AppFolders.hpp"
#include "Base/Utils.hpp"
#include "Base/Logger.hpp"

#include <fstream>
#include <sstream>

#if __has_include("curl/curl.h")
	#include <curl/curl.h>
#else
	#pragma message ("Missing curl.h even though HAS_CURL is set")
	#undef HAS_CURL
#endif

namespace OpenVulkano
{
#ifdef HAS_CURL
	namespace
	{
		size_t CurlDownloader(void* contents, size_t size, size_t memBlockCount, void* userData)
		{
			size_t totalSize = size * memBlockCount;
			std::vector<char>* buffer = static_cast<std::vector<char>*>(userData);
			buffer->insert(buffer->end(), static_cast<char*>(contents), static_cast<char*>(contents) + totalSize);
			return totalSize;
		}
	}
#endif

	bool WebResourceLoader::IsUrl(const std::string& str)
	{
		return Utils::StartsWith(str, "http://") || Utils::StartsWith(str, "https://") || Utils::StartsWith(str, "ftp://");
	}

	WebResourceLoader::WebResourceLoader()
	{
		m_cacheDirectory = AppFolders::GetAppCacheDir() / "resources";
		std::filesystem::create_directories(m_cacheDirectory);
#ifdef HAS_CURL
		curl_global_init(CURL_GLOBAL_DEFAULT);
#endif
	}

	WebResourceLoader::~WebResourceLoader()
	{
#ifdef HAS_CURL
		curl_global_cleanup();
#endif
	}

	std::filesystem::path WebResourceLoader::GetCacheFilePath(const std::string& url)
	{
		size_t hash = std::hash<std::string> {}(url);
		std::string hashedUrl = std::to_string(hash);

		return m_cacheDirectory / hashedUrl;
	}

	Array<char> WebResourceLoader::DownloadResource(const std::string& url)
	{
		std::vector<char> buffer;
#ifdef HAS_CURL
		CURL* curl = curl_easy_init();
		if (curl)
		{
			curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
			curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlDownloader);
			curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

#ifdef __APPLE__
			curl_easy_setopt(curl, CURLOPT_SSLENGINE, "DarwinSSL");
#endif

			CURLcode result = curl_easy_perform(curl);
			if (result != CURLE_OK)
			{
				std::string error = curl_easy_strerror(result);
				Logger::APP->error("Failed to download resource: '" + url + "' - " + error);
				curl_easy_cleanup(curl);
				return Array<char>();
			}
			curl_easy_cleanup(curl);
		}

		std::filesystem::path cacheFilePath = GetCacheFilePath(url);
		std::ofstream file(cacheFilePath, std::ios::binary);
		file.write(buffer.data(), buffer.size());
#endif

		return Array<char>(buffer);
	}

	Array<char> WebResourceLoader::GetResource(const std::string& resourceName)
	{
		if (IsUrl(resourceName))
		{
			std::filesystem::path cacheFilePath = GetCacheFilePath(resourceName);
			if (std::filesystem::exists(cacheFilePath)) { return Utils::ReadFile(cacheFilePath.string()); }
			else { return DownloadResource(resourceName); }
		}
		return Array<char>();
	}
}
