/*
 * 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 "ArchiveWriter.hpp"
#include "Math/ByteSize.hpp"
#include <memory>

namespace OpenVulkano
{
	class MultiPartArchiveWriter final : public IArchiveWriter
	{
		size_t m_fileSizeLimit;
		uint32_t m_archiveId;
		ArchiveConfiguration m_archiveConfig;
		std::filesystem::path m_dir;
		std::string m_fileNamePattern;
		std::unique_ptr<ArchiveWriter> m_writer;
		std::shared_ptr<spdlog::logger> m_logger;
		std::vector<std::filesystem::path> m_archives;
		std::function<bool(const FileDescription&)> m_shouldCompress;
		bool m_lazyCreation;

		void StartNewFile();

		void CheckSize(size_t size);

	public:
		MultiPartArchiveWriter(const std::string& dir, const std::string& fileNamePattern, const ArchiveConfiguration& archiveConfiguration, size_t sizeLimit = 2_GiB, bool lazyCreation = false, const std::shared_ptr<spdlog::logger>& logger = nullptr);

		~MultiPartArchiveWriter() override;

		bool AddFile(const FileDescription& description, const void* buffer) override;
		bool AddFile(const FileDescription& description, const std::vector<std::pair<const void*, size_t>>& buffers) override;
		bool AddFile(const char* fileName, const char* inArchiveName) override;
		using IArchiveWriter::AddFile;
		[[nodiscard]] virtual ArchiveOStream AddFileStream(const FileDescription& description) override;

		void SetShouldCompressFunction(const std::function<bool(const FileDescription&)>& shouldComp) override;

		void SetLogger(const std::shared_ptr<spdlog::logger>& logger) { m_logger = logger; }
		[[nodiscard]] std::shared_ptr<spdlog::logger> GetLogger() const { return m_logger; }

		void SetMaxArchiveFileSize(size_t sizeLimit) { m_fileSizeLimit = sizeLimit; }
		[[nodiscard]] size_t GetMaxArchiveFileSize() const { return m_fileSizeLimit; }

		void Move(const std::filesystem::path& newDir);

		[[nodiscard]] const std::filesystem::path& GetDir() const { return m_dir; }

		void Split();
	};
}
