#include "ShaderPackageCatalog.h"

#include "ShaderPackageRegistry.h"

bool ShaderPackageCatalog::Scan(const std::filesystem::path& shaderRoot, unsigned maxTemporalHistoryFrames, std::string& error)
{
	std::map<std::string, ShaderPackage> packagesById;
	std::vector<std::string> packageOrder;
	std::vector<ShaderPackageStatus> packageStatuses;

	ShaderPackageRegistry registry(maxTemporalHistoryFrames);
	if (!registry.Scan(shaderRoot, packagesById, packageOrder, packageStatuses, error))
		return false;

	mPackagesById.swap(packagesById);
	mPackageOrder.swap(packageOrder);
	mPackageStatuses.swap(packageStatuses);
	return true;
}

ShaderPackageCatalog::Snapshot ShaderPackageCatalog::CaptureSnapshot() const
{
	Snapshot snapshot;
	snapshot.packagesById = mPackagesById;
	snapshot.packageOrder = mPackageOrder;
	return snapshot;
}

bool ShaderPackageCatalog::HasCatalogChangedSince(const Snapshot& snapshot) const
{
	if (snapshot.packageOrder != mPackageOrder || snapshot.packagesById.size() != mPackagesById.size())
		return true;

	for (const auto& item : mPackagesById)
	{
		auto previous = snapshot.packagesById.find(item.first);
		if (previous == snapshot.packagesById.end() || !PackagesEquivalent(previous->second, item.second))
			return true;
	}

	return false;
}

bool ShaderPackageCatalog::HasPackageChangedSince(const Snapshot& snapshot, const std::string& shaderId) const
{
	auto previous = snapshot.packagesById.find(shaderId);
	auto current = mPackagesById.find(shaderId);
	if (previous == snapshot.packagesById.end() || current == mPackagesById.end())
		return previous != snapshot.packagesById.end() || current != mPackagesById.end();

	return !PackagesEquivalent(previous->second, current->second);
}

bool ShaderPackageCatalog::HasPackage(const std::string& shaderId) const
{
	return mPackagesById.find(shaderId) != mPackagesById.end();
}

const ShaderPackage* ShaderPackageCatalog::FindPackage(const std::string& shaderId) const
{
	auto it = mPackagesById.find(shaderId);
	return it == mPackagesById.end() ? nullptr : &it->second;
}

bool ShaderPackageCatalog::CopyPackage(const std::string& shaderId, ShaderPackage& shaderPackage) const
{
	const ShaderPackage* package = FindPackage(shaderId);
	if (!package)
		return false;

	shaderPackage = *package;
	return true;
}

const std::vector<std::string>& ShaderPackageCatalog::PackageOrder() const
{
	return mPackageOrder;
}

const std::vector<ShaderPackageStatus>& ShaderPackageCatalog::PackageStatuses() const
{
	return mPackageStatuses;
}

bool ShaderPackageCatalog::PackagesEquivalent(const ShaderPackage& left, const ShaderPackage& right)
{
	return left.shaderWriteTime == right.shaderWriteTime &&
		left.manifestWriteTime == right.manifestWriteTime &&
		TextureAssetsEqual(left.textureAssets, right.textureAssets) &&
		FontAssetsEqual(left.fontAssets, right.fontAssets);
}

bool ShaderPackageCatalog::TextureAssetsEqual(const std::vector<ShaderTextureAsset>& left, const std::vector<ShaderTextureAsset>& right)
{
	if (left.size() != right.size())
		return false;

	for (std::size_t index = 0; index < left.size(); ++index)
	{
		if (left[index].id != right[index].id ||
			left[index].path != right[index].path ||
			left[index].writeTime != right[index].writeTime)
		{
			return false;
		}
	}

	return true;
}

bool ShaderPackageCatalog::FontAssetsEqual(const std::vector<ShaderFontAsset>& left, const std::vector<ShaderFontAsset>& right)
{
	if (left.size() != right.size())
		return false;

	for (std::size_t index = 0; index < left.size(); ++index)
	{
		if (left[index].id != right[index].id ||
			left[index].path != right[index].path ||
			left[index].writeTime != right[index].writeTime)
		{
			return false;
		}
	}

	return true;
}
