/*
 * 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/.
 */

#pragma once

#include "ArchiveOStream.hpp"
#include "IO/FileDescription.hpp"
#include <functional>
#include <vector>

namespace OpenVulkano
{
	class IArchiveWriter
	{
	protected:
		IArchiveWriter() = default;

	public:
		virtual ~IArchiveWriter() = default;

		virtual void SetShouldCompressFunction(const std::function<bool(const FileDescription&)>& shouldComp) = 0;

		virtual bool AddFile(const FileDescription& description, const void* buffer) = 0;
		virtual bool AddFile(const FileDescription& description, const std::vector<std::pair<const void*, size_t>>& buffers) = 0;
		virtual bool AddFile(const char* fileName, const char* inArchiveName) = 0;

		/**
		 * Adds a new file to the archive as a stream.
		 * If used with ZIP files the size in the description might be set to UNKNOWN_SIZE.
		 * @param description The files description.
		 * @return An output stream that allows the file to be written in a streaming fashion. The stream is only valid till either the file has been fully written (reached the size given in the file description) or the next file is added to the archive.
		 */
		[[nodiscard]] virtual ArchiveOStream AddFileStream(const FileDescription& description) = 0;

		bool AddFile(const char* filePath)
		{
			std::filesystem::path path(filePath);
			std::string fileName = path.filename().string();
			if (std::filesystem::is_regular_file(path))
			{
				return AddFile(filePath, fileName.c_str());
			}
			else if (std::filesystem::is_directory(path))
			{
				AddFiles(filePath, fileName.c_str());
			}
			return false;
		}

		bool AddFile(const char* inArchiveName, const void* buffer, size_t length)
		{
			FileDescription description = FileDescription::MakeDescriptionForFile(inArchiveName, length);
			return AddFile(description, buffer);
		}

		bool AddFile(const char* fileName, const std::vector<std::pair<const void*, size_t>>& buffers)
		{
			size_t size = 0;
			for(const auto& buffer : buffers)
			{
				if (buffer.first == nullptr) continue;
				size += buffer.second;
			}
			FileDescription description = FileDescription::MakeDescriptionForFile(fileName, size);
			return AddFile(description, buffers);
		}

		bool AddFiles(const char* dirName)
		{
			return AddFiles(dirName, "");
		}

		bool AddFiles(const char* dirName, const char* inArchiveDirName)
		{
			return AddFiles(std::filesystem::path(dirName), std::string(inArchiveDirName));
		}

		bool AddFiles(const std::filesystem::path& dirName, const std::string& inArchiveDirName)
		{
			std::string sDirName = dirName.string();
			AddFile(sDirName.c_str(), inArchiveDirName.c_str());
			for(const auto& entry : std::filesystem::directory_iterator(dirName))
			{
				std::string fPath = inArchiveDirName + "/" + entry.path().filename().string();
				if (entry.is_directory()) AddFiles(entry, fPath);
				else
				{
					std::string entryName = entry.path().string();
					AddFile(entryName.c_str(), fPath.c_str());
				}
			}
			return true;
		}
	};
}