/*
 * 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 "ArchiveBase.hpp"
#include "Data/Containers/Array.hpp"
#include <string_view>
#include <optional>
#include <functional>
#include <ostream>
#include <queue>

namespace OpenVulkano
{
	class ArchiveReader final : public ArchiveBase
	{
		bool m_open = false;
		bool m_eof = false;

		std::queue<std::string> m_archivesToRead;

	public:
		explicit ArchiveReader(const std::shared_ptr<spdlog::logger>& logger = nullptr);

		explicit ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr);

		explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr);

		ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr<spdlog::logger>& logger = nullptr);

		~ArchiveReader() override;

		bool Open(const char* archiveFile);

		bool Open(const std::string& archiveFile);

		bool OpenMemory(const void* archiveBuffer, size_t size);

		bool Open(const std::vector<std::string>& archiveFiles);

		bool Open(const std::filesystem::path& dir, const std::string& fileNamePattern, size_t* outSize = nullptr);

		[[nodiscard]] bool IsOpen() const { return m_open; }

		size_t ExtractRemaining(std::string_view targetDir);

		// Element wise operations
		[[nodiscard]] bool HasNext() const;

		void SkipNext();

		bool SkipTill(std::filesystem::file_type type = std::filesystem::file_type::regular);

		[[nodiscard]] FileDescription GetNextDescription() const;

		std::optional<FileDescription> GetNextDirectory();

		std::optional<FileDescription> ExtractNext(std::string_view targetDir);

		std::optional<std::pair<FileDescription, Array<char>>> GetNextFile();

		std::optional<std::pair<FileDescription, std::vector<char>>> GetNextFileAsVector();

		std::optional<FileDescription> StreamNextFile(std::ostream& stream);

		bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader);

	private:
		void ReadNextHeader();

		void PrepOpen();
	};
}
