<?php

/*
 *
 *  ____            _        _   __  __ _                  __  __ ____
 * |  _ \ ___   ___| | _____| |_|  \/  (_)_ __   ___      |  \/  |  _ \
 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
 * |  __/ (_) | (__|   <  __/ |_| |  | | | | | |  __/_____| |  | |  __/
 * |_|   \___/ \___|_|\_\___|\__|_|  |_|_|_| |_|\___|     |_|  |_|_|
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * @author PocketMine Team
 * @link http://www.pocketmine.net/
 *
 *
 */

declare(strict_types=1);

/**
 * This script is used to generate a version-to-version item upgrade schema.
 * The following inputs are required:
 * - A full item mapping table from a current version (e.g. r16_to_current_block_map.json)
 * - A directory containing schemas for previous versions' incremental updates
 */
namespace pocketmine\tools\generate_item_upgrade_schema;

use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path;
use function count;
use function dirname;
use function file_put_contents;
use function fwrite;
use function is_array;
use function json_decode;
use function json_encode;
use function ksort;
use function scandir;
use const JSON_FORCE_OBJECT;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;
use const SCANDIR_SORT_ASCENDING;
use const SORT_STRING;
use const STDERR;

require dirname(__DIR__) . '/vendor/autoload.php';

if(count($argv) !== 4){
	\GlobalLogger::get()->error("Required arguments: path to mapping table, path to current schemas, path to output file");
	exit(1);
}

[, $mappingTableFile, $upgradeSchemasDir, $outputFile] = $argv;

$target = json_decode(Filesystem::fileGetContents($mappingTableFile), true, JSON_THROW_ON_ERROR);
if(!is_array($target) || !isset($target["simple"]) || !is_array($target["simple"]) || !isset($target["complex"]) || !is_array($target["complex"])){
	\GlobalLogger::get()->error("Invalid mapping table file");
	exit(1);
}

$files = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => scandir($upgradeSchemasDir, SCANDIR_SORT_ASCENDING));

$merged = [];
foreach($files as $file){
	if($file === "." || $file === ".."){
		continue;
	}
	\GlobalLogger::get()->info("Processing schema file $file");
	$data = json_decode(Filesystem::fileGetContents(Path::join($upgradeSchemasDir, $file)), associative: true, flags: JSON_THROW_ON_ERROR);
	if(!is_array($data)){
		\GlobalLogger::get()->error("Invalid schema file $file");
		exit(1);
	}
	foreach(($data["renamedIds"] ?? []) as $oldId => $newId){
		if(isset($merged["simple"][$oldId])){
			\GlobalLogger::get()->warning("Duplicate rename for $oldId in file $file (was " . $merged["simple"][$oldId] . ", now $newId)");
		}
		$merged["simple"][$oldId] = $newId;
	}

	foreach(($data["remappedMetas"] ?? []) as $oldId => $mappings){
		foreach($mappings as $meta => $newId){
			if(isset($merged["complex"][$oldId][$meta])){
				\GlobalLogger::get()->warning("Duplicate meta remap for $oldId meta $meta in file $file (was " . $merged["complex"][$oldId][$meta] . ", now $newId)");
			}
			$merged["complex"][$oldId][$meta] = $newId;
		}
	}
}

$newDiff = [];

foreach(Utils::promoteKeys($target["simple"]) as $oldId => $newId){
	$previousNewId = $merged["simple"][$oldId] ?? null;
	if(
		$previousNewId === $newId || //if previous schemas already accounted for this
		($previousNewId !== null && isset($target["simple"][$previousNewId])) //or the item's ID has been changed for a second time
	){
		continue;
	}
	$newDiff["renamedIds"][$oldId] = $newId;
}
if(isset($newDiff["renamedIds"])){
	ksort($newDiff["renamedIds"], SORT_STRING);
}

foreach(Utils::promoteKeys($target["complex"]) as $oldId => $mappings){
	if(!is_array($mappings)){
		fwrite(STDERR, "Complex mapping for $oldId is not an array\n");
		exit(1);
	}
	foreach(Utils::promoteKeys($mappings) as $meta => $newId){
		if(($merged["complex"][$oldId][$meta] ?? null) !== $newId){
			if($oldId === "minecraft:spawn_egg" && $meta === 130 && ($newId === "minecraft:axolotl_bucket" || $newId === "minecraft:axolotl_spawn_egg")){
				//TODO: hack for vanilla bug workaround
				continue;
			}
			$newDiff["remappedMetas"][$oldId][$meta] = $newId;
		}
	}
	if(isset($newDiff["remappedMetas"][$oldId])){
		ksort($newDiff["remappedMetas"][$oldId], SORT_STRING);
	}
}
if(isset($newDiff["remappedMetas"])){
	ksort($newDiff["remappedMetas"], SORT_STRING);
}
ksort($newDiff, SORT_STRING);

\GlobalLogger::get()->info("Writing output file to $outputFile");
file_put_contents($outputFile, json_encode($newDiff, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT));