<?php
namespace Helhum\ClassAliasLoader\Composer;

/*
 * This file is part of the class alias loader package.
 *
 * (c) Helmut Hummel <info@helhum.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Composer\Package\RootPackageInterface;
use Composer\Util\Filesystem;

/**
 * Class ClassAliasLoader
 */
class ClassAliasGenerator {

	/**
	 * @param \Composer\Script\Event $event
	 * @param bool $calledByPlugin
	 * @return bool
	 * @throws \Exception
	 */
	static public function generateAliasMap(\Composer\Script\Event $event, $calledByPlugin = false) {
		if (!$calledByPlugin) {
			$event->getIO()->write('
<warning>Please remove the section "post-autoload-dump": "Helhum\\ClassAliasLoader\\Composer\\ClassAliasGenerator::generateAliasMap" from your composer.json! It is obsolete.</warning>
');
			return false;
		}
		$event->getIO()->write('<info>Generating class alias map files</info>');

		$composer = $event->getComposer();
		$config = $composer->getConfig();

		$filesystem = new Filesystem();
		$filesystem->ensureDirectoryExists($config->get('vendor-dir'));
		$basePath = $filesystem->normalizePath(realpath(getcwd()));
		$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
		$targetDir = $vendorPath . '/composer';
		$filesystem->ensureDirectoryExists($targetDir);

		$mainPackage = $composer->getPackage();
		$autoLoadGenerator = $composer->getAutoloadGenerator();
		$localRepo = $composer->getRepositoryManager()->getLocalRepository();
		$packageMap = $autoLoadGenerator->buildPackageMap($composer->getInstallationManager(), $mainPackage, $localRepo->getCanonicalPackages());

		$caseSensitiveClassLoading = self::caseSensitiveClassLoading($mainPackage, $targetDir) ? 'true' : 'false';

		$aliasToClassNameMapping = array();
		$classNameToAliasMapping = array();
		$mappingFound = false;

		foreach ($packageMap as $item) {
			list($package, $installPath) = $item;
			$extra = $package->getExtra();
			if (!empty($extra['class-alias-maps'])) {
				if (!is_array($extra['class-alias-maps'])) {
					throw new \Exception('"class-alias-maps" must be an array');
				}
				foreach ($extra['class-alias-maps'] as $mapFile) {
					$mapFilePath = ($installPath ?: $basePath) . '/' . $filesystem->normalizePath($mapFile);
					if (is_file($mapFilePath)) {
						$packageAliasMap = require $mapFilePath;
						if (!is_array($packageAliasMap)) {
							throw new \Exception('"class alias maps" must return an array', 1422625075);
						}
						if (!empty($packageAliasMap)) {
							$mappingFound = true;
						}
						foreach ($packageAliasMap as $aliasClassName => $className) {
							$lowerCasedAliasClassName = strtolower($aliasClassName);
							$aliasToClassNameMapping[$lowerCasedAliasClassName] = $className;
							$classNameToAliasMapping[$className][$lowerCasedAliasClassName] = $lowerCasedAliasClassName;
						}
					}
				}
			}
		}

		if (!$mappingFound) {
			return false;
		}

		$exportArray = array(
			'aliasToClassNameMapping' => $aliasToClassNameMapping,
			'classNameToAliasMapping' => $classNameToAliasMapping
		);

		$fileContent = '<?php' . chr(10) . 'return ';
		$fileContent .= var_export($exportArray, true);
		$fileContent .= ';';

		file_put_contents($targetDir . '/autoload_classaliasmap.php', $fileContent);
		$originalAutoloadFileContent = file_get_contents($vendorPath . '/autoload.php');
		preg_match('/return ComposerAutoloaderInit[^;]*;/', $originalAutoloadFileContent, $matches);
		$originalAutoloadFileContent = str_replace($matches[0], '', $originalAutoloadFileContent);
		$composerClassLoaderInit = str_replace(array('return ', ';'), '', $matches[0]);
		$autoloadFileContent = <<<EOF
$originalAutoloadFileContent

// autoload.php @generated by Helhum/ClassAliasLoader

return call_user_func(function() {
	\$composerClassLoader = $composerClassLoaderInit;
	\$aliasClassLoader = new Helhum\ClassAliasLoader\Composer\ClassAliasLoader(\$composerClassLoader);

	\$classAliasMap = require __DIR__ . '/composer/autoload_classaliasmap.php';
	\$aliasClassLoader->setAliasMap(\$classAliasMap);
	\$aliasClassLoader->setCaseSensitiveClassLoading($caseSensitiveClassLoading);
	spl_autoload_register(array(\$aliasClassLoader, 'loadClassWithAlias'), true, true);

	return \$aliasClassLoader;
});

EOF;

		file_put_contents($vendorPath . '/autoload.php', $autoloadFileContent);

		return true;
	}

	/**
	 * Rewrites the class map to have lowercased keys to be able to load classes with wrong casing
	 * Defaults to case sensitivity (composer loader default)
	 *
	 * @param RootPackageInterface $mainPackage
	 * @param string $targetDir
	 * @return bool
	 */
	static protected function caseSensitiveClassLoading(RootPackageInterface $mainPackage, $targetDir) {
		$extra = $mainPackage->getExtra();
		$caseSensitiveClassLoading = true;
		if (isset($extra['autoload-case-sensitivity'])) {
			$caseSensitiveClassLoading = (bool)$extra['autoload-case-sensitivity'];
		}
		if (!$caseSensitiveClassLoading) {
			$classMapContents = file_get_contents($targetDir . '/autoload_classmap.php');
			$classMapContents = preg_replace_callback(
				'/    \'[^\']*\' => /',
				function($match) {
					return strtolower($match[0]);
				},
				$classMapContents
			);
			file_put_contents($targetDir . '/autoload_classmap.php', $classMapContents);
		}
		return $caseSensitiveClassLoading;
	}
}