<?php

/**
 * PHPEdit PHP reference generator
 *
 * @version $Id$
 * @copyright 2009
 */

#region constants
define('NS', 'http://www.waterproof.fr/PHPEdit/Extensions/PHPLanguage/Reference');
define('ReferenceNode', 'Reference');
define('ReferenceNodeVersionAttribute', 'version');
define('ReferenceNodeVersionValue', '3.4');
define('ExtensionNode', 'Extension');
define('NameAttribute', 'name');
define('ConstantNode', 'Constant');
define('ValueAttribute', 'value');
define('DescriptionNode', 'Description');
define('FunctionNode', 'Function');
define('VisibilityAttribute', 'visibility');
define('StaticAttribute', 'static');
define('AbstractAttribute', 'abstract');
define('ClassNode', 'Class');
define('InterfaceNode', 'Interface');
define('MembersNode', 'Members');
define('PrototypesNode', 'Prototypes');
define('PrototypeNode', 'Prototype');
define('TextNode', 'Text');
define('ReturnTypeAttribute', 'returnType');
define('ParametersNode', 'Parameters');
define('ParameterNode', 'Parameter');
define('TypeAttribute', 'type');
define('RequiredAttribute', 'required');
define('DefaultValueAttribute', 'defaultValue');
#endregion

function WriteBooleanAttribute($xml, $name, $value)
{
	if ($value)
	{
		$xml->writeAttribute($name, 'true');
	}
	else
	{
		$xml->writeAttribute($name, 'false');
	}
}

function GenerateConstant($xml, $name, $value)
{
	$xml->startElement(ConstantNode);
	$xml->writeAttribute(NameAttribute, $name);
	$xml->writeAttribute(ValueAttribute, (string)$value);
	$xml->endElement();
}

function GenerateParameter($xml, $parameter)
{
	$xml->startElement(ParameterNode);
	$xml->writeAttribute(NameAttribute, $parameter->getName());
	WriteBooleanAttribute($xml, RequiredAttribute, !$parameter->isOptional());
	try
	{
		$type = $parameter->getClass();
		if (isset($type))
		{
			$xml->writeAttribute(TypeAttribute, $type->getName());
		}
	}
	catch (Exception $e)
	{
		// the class hasn't been found
	}
	if ($parameter->isDefaultValueAvailable())
	{
		$xml->writeAttribute(DefaultValueAttribute, $parameter->getDefaultValue());
	}
	$xml->endElement();
}

function GeneratePrototypes($xml, $function)
{
	if ($function->getNumberOfParameters() > 0)
	{
		$xml->startElement(PrototypesNode);
		$xml->startElement(PrototypeNode);

		$xml->startElement(ParametersNode);
		foreach ($function->getParameters() as $parameter)
		{
			GenerateParameter($xml, $parameter);
		}
		$xml->endElement();

		$xml->endElement();
		$xml->endElement();
	}
}

function GenerateFunction($xml, $function)
{
	$xml->startElement(FunctionNode);
	$xml->writeAttribute(NameAttribute, $function->getName());

	GeneratePrototypes($xml, $function);

	$xml->endElement();
}

function GenerateMethod($xml, $function)
{
	$xml->startElement(FunctionNode);
	$xml->writeAttribute(NameAttribute, $function->getName());
	if ($function->isPrivate())
	{
		$xml->writeAttribute(VisibilityAttribute, 'Private');
	}
	else
	if ($function->isProtected())
	{
		$xml->writeAttribute(VisibilityAttribute, 'Protected');
	}
	else
	if ($function->isPublic())
	{
		$xml->writeAttribute(VisibilityAttribute, 'Public');
	}

	if ($function->isStatic())
	{
		$xml->writeAttribute(StaticAttribute, 'true');
	}

	if ($function->isAbstract())
	{
		$xml->writeAttribute(AbstractAttribute, 'true');
	}

	GeneratePrototypes($xml, $function);

	$xml->endElement();
}

function GenerateClass($xml, $class)
{
	$xml->startElement($class->isInterface() ? InterfaceNode : ClassNode);
	$xml->writeAttribute(NameAttribute, $class->getName());

	$xml->startElement(MembersNode);

	foreach ($class->getConstants() as $constantName => $constantValue)
	{
		GenerateConstant($xml, $constantName, $constantValue);
	}

	foreach ($class->getMethods() as $function)
	{
		GenerateMethod($xml, $function);
	}

	$xml->endElement();
	$xml->endElement();
}

function GenerateReference($target)
{
    echo "Generating reference in $target...";

	$xml = new XMLWriter();
	if (!$xml->openURI($target))
	{
		exit(1);
	}

	$constants = array();
	$reference = array();

	#region	retrieve reference
	foreach (get_loaded_extensions() as $extensionName)
	{
		//$xml->startElement(ExtensionNode);
		//$xml->writeAttribute(NameAttribute, $extensionName);

		$extension = new ReflectionExtension($extensionName);

		foreach ($extension->getConstants() as $constantName => $constantValue)
		{
			$constants[$constantName] = 1;
			$reference['constants'][$constantName] = $constantValue;
		}

		foreach ($extension->getFunctions() as $function)
		{
			$reference['functions'][$function->getName()] = $function;
		}

		foreach ($extension->getClasses() as $class)
		{
			$reference['classes'][$class->getName()] = $class;
		}

		//$xml->endElement();
	}

	// missing constants
	foreach (get_defined_constants(true) as $category => $categoryConstants)
	{
		if ($category != 'user')
		{
			foreach ($categoryConstants as $constantName => $constantValue)
			{
				if (!array_key_exists($constantName, $constants))
				{
					$reference['constants'][$constantName] = $constantValue;
				}
			}
		}
	}
	#endregion

	#region	write the xml
	$xml->setIndent(TRUE);
	$xml->startDocument('1.0', 'UTF-8');

	$xml->startElementNS(NULL, ReferenceNode, NS);
	$xml->writeAttribute(ReferenceNodeVersionAttribute, ReferenceNodeVersionValue);

	$xml->startElement(ExtensionNode);
	$xml->writeAttribute(NameAttribute, '');

	ksort($reference['constants']);
	ksort($reference['functions']);
	ksort($reference['classes']);

	foreach ($reference['constants'] as $constantName => $constantValue)
	{
		$constants[$constantName] = 1;
		GenerateConstant($xml, $constantName, $constantValue);
	}

	foreach ($reference['functions'] as $function)
	{
		GenerateFunction($xml, $function);
	}

	foreach ($reference['classes'] as $class)
	{
		GenerateClass($xml, $class);
	}

	$xml->endElement();

	$xml->endElement();
	$xml->endDocument();
	#endregion
}

if (isset($_GET['target']))
{
	$target = $_GET['target'];
}
else
{
	$target = $argv[1];
}
GenerateReference($target);

?>