/*
 * Copyright © 2020-2026 Pierrick Caillon <pierrick.caillon+zipdirfs@megami.fr>
 */
#include "ZipDirFs/Utilities/FileSystem.h"
#include <chrono>
#include <filesystem>

namespace ZipDirFs::Utilities
{
	namespace fs = std::filesystem;

	namespace
	{
		struct DirectoryIteratorWrapper : EntryIterator::Wrapper
		{
			DirectoryIteratorWrapper(fs::directory_iterator&& it);
			DirectoryIteratorWrapper(const fs::directory_iterator& it);
			~DirectoryIteratorWrapper() override;

		protected:
			Wrapper* clone() const override;
			reference dereference() const override;
			void increment() override;
			bool equals(const Wrapper&) const override;
			fs::directory_iterator currentIt;
			std::string value;
			bool valid;
		};

		EntryIterator fileSystemDirectoryIteratorFromPath(const fs::path& p)
		{
			return EntryIterator(new DirectoryIteratorWrapper(fs::directory_iterator(p)));
		}

		EntryIterator fileSystemDirectoryIteratorEnd()
		{
			return EntryIterator(new DirectoryIteratorWrapper(fs::directory_iterator()));
		}

		fs::file_status fileSystemStatus(const fs::path& p) { return fs::status(p); }
	} // namespace

	FileSystem::FileSystem() {}

	void FileSystem::reset()
	{
		assign(last_write_time, fs::last_write_time);
		assign(directory_iterator_from_path, fileSystemDirectoryIteratorFromPath);
		assign(directory_iterator_end, fileSystemDirectoryIteratorEnd);
		assign(status, fileSystemStatus);
	}

	std::function<fs::file_time_type(const fs::path&)> FileSystem::last_write_time;
	std::function<EntryIterator(const fs::path&)> FileSystem::directory_iterator_from_path;
	std::function<EntryIterator()> FileSystem::directory_iterator_end;
	std::function<fs::file_status(const fs::path&)> FileSystem::status;

	bool init = []
	{
		FileSystem::reset();
		return true;
	}();

	DirectoryIteratorWrapper::DirectoryIteratorWrapper(fs::directory_iterator&& it) :
		currentIt(std::move(it)), value(""), valid(false)
	{
	}
	DirectoryIteratorWrapper::DirectoryIteratorWrapper(const fs::directory_iterator& it) :
		currentIt(it), value(""), valid(false)
	{
	}
	DirectoryIteratorWrapper::~DirectoryIteratorWrapper() {}
	DirectoryIteratorWrapper::Wrapper* DirectoryIteratorWrapper::clone() const
	{
		return new DirectoryIteratorWrapper(currentIt);
	}
	DirectoryIteratorWrapper::reference DirectoryIteratorWrapper::dereference() const
	{
		if (!valid)
		{
			*const_cast<std::string*>(&value) = currentIt->path().filename().native();
			*const_cast<bool*>(&valid) = true;
		}
		return value;
	}
	void DirectoryIteratorWrapper::increment()
	{
		++currentIt;
		valid = false;
	}
	bool DirectoryIteratorWrapper::equals(const Wrapper& w) const
	{
		const DirectoryIteratorWrapper* other = dynamic_cast<const DirectoryIteratorWrapper*>(&w);
		return (other != nullptr) && (currentIt == other->currentIt);
	}

} // namespace ZipDirFs::Utilities
