// Write-mode pack builder: recursively packs every file under a source folder
// into <dbname>.eix/<dbname>.epk in an output folder, using the same CEterPack
// class the game client itself uses to read packs (so the result is guaranteed
// compatible). Always rebuilds the pack from scratch (deletes any existing
// <dbname>.eix/.epk in the output folder first) so removed source files don't
// linger as stale entries.
//
// Usage: PackMake.exe <sourceFolder> <outputPackFolder> <dbname>
//   e.g. PackMake.exe "Y:\Metin2Dev\Exec_Client\Eternexus\root" "Y:\scratch\pack" root

#include <windows.h>
#include <cstdio>
#include <string>
#include <filesystem>

#include "../EterPack/EterPack.h"
#include "../EterBase/lzo.h"

namespace fs = std::filesystem;

int main(int argc, char** argv)
{
	// CLZO is a CSingleton<T> that registers itself via ctor side effect (no
	// lazy init) - EncryptIndexFile() calls CLZO::Instance() internally, so an
	// instance must exist before that or Instance() dereferences a null pointer.
	CLZO lzoInstance;

	if (argc < 4)
	{
		fprintf(stderr, "Usage: %s <sourceFolder> <outputPackFolder> <dbname>\n", argv[0]);
		return 1;
	}

	const std::string sourceFolder = argv[1];
	const std::string outputFolder = argv[2];
	const char* dbname = argv[3];

	if (!fs::is_directory(sourceFolder))
	{
		fprintf(stderr, "Source folder does not exist: %s\n", sourceFolder.c_str());
		return 2;
	}

	if (!fs::is_directory(outputFolder))
	{
		fprintf(stderr, "Output pack folder does not exist: %s\n", outputFolder.c_str());
		return 2;
	}

	if (!SetCurrentDirectoryA(outputFolder.c_str()))
	{
		fprintf(stderr, "FAILED to chdir to '%s'\n", outputFolder.c_str());
		return 4;
	}

	std::string eixPath = std::string(dbname) + ".eix";
	std::string epkPath = std::string(dbname) + ".epk";

	if (fs::exists(eixPath) || fs::exists(epkPath))
	{
		printf("Removing existing %s / %s in output folder for a clean rebuild\n", eixPath.c_str(), epkPath.c_str());
		fs::remove(eixPath);
		fs::remove(epkPath);
	}

	CEterFileDict dict;
	CEterPack pack;

	if (!pack.Create(dict, dbname, "", /*bReadOnly=*/false))
	{
		fprintf(stderr, "FAILED to create pack '%s' in '%s'\n", dbname, outputFolder.c_str());
		return 3;
	}

	long fileCount = 0;
	long failCount = 0;

	for (auto it = fs::recursive_directory_iterator(sourceFolder); it != fs::recursive_directory_iterator(); ++it)
	{
		if (!it->is_regular_file())
			continue;

		std::string relPath = fs::relative(it->path(), sourceFolder).generic_string();
		std::string srcPath = it->path().string();

		if (!pack.Put(relPath.c_str(), srcPath.c_str(), COMPRESSED_TYPE_NONE, ""))
		{
			fprintf(stderr, "FAILED to put '%s' (source: %s)\n", relPath.c_str(), srcPath.c_str());
			++failCount;
			continue;
		}

		++fileCount;
	}

	if (!pack.EncryptIndexFile())
	{
		fprintf(stderr, "FAILED to encrypt index for pack '%s'\n", dbname);
		return 5;
	}

	printf("Packed %ld files (%ld failures) into %s / %s\n", fileCount, failCount, eixPath.c_str(), epkPath.c_str());

	return failCount > 0 ? 6 : 0;
}
