
#ifdef HAVE_CONFIG_H
#include "../../ext_config.h"
#endif

#include <php.h>
#include "../../php_ext.h"
#include "../../ext.h"

#include <Zend/zend_operators.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_interfaces.h>

#include "kernel/main.h"
#include "kernel/object.h"
#include "kernel/memory.h"
#include "kernel/operators.h"
#include "kernel/array.h"
#include "kernel/fcall.h"


/**
 * This file is part of the Phalcon Framework.
 *
 * (c) Phalcon Team <team@phalcon.io>
 *
 * For the full copyright and license information, please view the LICENSE.txt
 * file that was distributed with this source code.
 */
/**
 * Allows to manipulate the annotations reflection in an OO manner
 *
 *```php
 * use Phalcon\Annotations\Reader;
 * use Phalcon\Annotations\Reflection;
 *
 * // Parse the annotations in a class
 * $reader = new Reader();
 * $parsing = $reader->parse("MyComponent");
 *
 * // Create the reflection
 * $reflection = new Reflection($parsing);
 *
 * // Get the annotations in the class docblock
 * $classAnnotations = $reflection->getClassAnnotations();
 *```
 */
ZEPHIR_INIT_CLASS(Phalcon_Annotations_Reflection)
{
	ZEPHIR_REGISTER_CLASS(Phalcon\\Annotations, Reflection, phalcon, annotations_reflection, phalcon_annotations_reflection_method_entry, 0);

	/**
	 * @var Collection|null
	 */
	zend_declare_property_null(phalcon_annotations_reflection_ce, SL("classAnnotations"), ZEND_ACC_PROTECTED);
	/**
	 * @var array
	 */
	zend_declare_property_null(phalcon_annotations_reflection_ce, SL("constantAnnotations"), ZEND_ACC_PROTECTED);
	/**
	 * @var array
	 */
	zend_declare_property_null(phalcon_annotations_reflection_ce, SL("propertyAnnotations"), ZEND_ACC_PROTECTED);
	/**
	 * @var array
	 */
	zend_declare_property_null(phalcon_annotations_reflection_ce, SL("methodAnnotations"), ZEND_ACC_PROTECTED);
	/**
	 * @var array
	 */
	zend_declare_property_null(phalcon_annotations_reflection_ce, SL("reflectionData"), ZEND_ACC_PROTECTED);
	phalcon_annotations_reflection_ce->create_object = zephir_init_properties_Phalcon_Annotations_Reflection;

	return SUCCESS;
}

PHP_METHOD(Phalcon_Annotations_Reflection, __construct)
{
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zval *reflectionData_param = NULL;
	zval reflectionData;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&reflectionData);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("reflectionData", 14, 1);
	}

	ZEND_PARSE_PARAMETERS_START(0, 1)
		Z_PARAM_OPTIONAL
		ZEPHIR_Z_PARAM_ARRAY(reflectionData, reflectionData_param)
	ZEND_PARSE_PARAMETERS_END();
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	zephir_fetch_params(1, 0, 1, &reflectionData_param);
	if (!reflectionData_param) {
		ZEPHIR_INIT_VAR(&reflectionData);
		array_init(&reflectionData);
	} else {
		zephir_get_arrval(&reflectionData, reflectionData_param);
	}
	zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 404, &reflectionData);
	ZEPHIR_MM_RESTORE();
}

/**
 * Returns the annotations found in the class docblock
 *
 * @return Collection|null
 */
PHP_METHOD(Phalcon_Annotations_Reflection, getClassAnnotations)
{
	zval reflectionClass, _0, _1$$3, _2$$4;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&reflectionClass);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_1$$3);
	ZVAL_UNDEF(&_2$$4);
	static zend_string *_zephir_prop_0 = NULL;
	static zend_string *_zephir_prop_1 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("classAnnotations", 16, 1);
	}
	if (UNEXPECTED(!_zephir_prop_1)) {
		_zephir_prop_1 = zend_string_init("reflectionData", 14, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 405, PH_NOISY_CC | PH_READONLY);
	if (Z_TYPE_P(&_0) == IS_NULL) {
		zephir_memory_observe(&reflectionClass);
		zephir_read_property_cached(&_1$$3, this_ptr, _zephir_prop_1, 404, PH_NOISY_CC | PH_READONLY);
		if (zephir_array_isset_string_fetch(&reflectionClass, &_1$$3, SL("class"), 0)) {
			ZEPHIR_INIT_VAR(&_2$$4);
			object_init_ex(&_2$$4, phalcon_annotations_collection_ce);
			ZEPHIR_CALL_METHOD(NULL, &_2$$4, "__construct", NULL, 86, &reflectionClass);
			zephir_check_call_status();
			zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 405, &_2$$4);
		}
	}
	RETURN_MM_MEMBER(getThis(), "classAnnotations");
}

/**
 * Returns the annotations found in the constants' docblocks
 *
 * @return Collection[]
 */
PHP_METHOD(Phalcon_Annotations_Reflection, getConstantsAnnotations)
{
	zend_string *_4$$4;
	zend_ulong _3$$4;
	zend_bool _1$$3, _8$$4;
	zval reflectionConstants, constant, reflectionConstant, _0, *_2$$4, _7$$4, _5$$5, _9$$6;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zephir_fcall_cache_entry *_6 = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&reflectionConstants);
	ZVAL_UNDEF(&constant);
	ZVAL_UNDEF(&reflectionConstant);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_7$$4);
	ZVAL_UNDEF(&_5$$5);
	ZVAL_UNDEF(&_9$$6);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("reflectionData", 14, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	zephir_memory_observe(&reflectionConstants);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 404, PH_NOISY_CC | PH_READONLY);
	if (zephir_array_isset_string_fetch(&reflectionConstants, &_0, SL("constants"), 0)) {
		_1$$3 = Z_TYPE_P(&reflectionConstants) == IS_ARRAY;
		if (_1$$3) {
			_1$$3 = !(ZEPHIR_IS_EMPTY(&reflectionConstants));
		}
		if (_1$$3) {
			zephir_is_iterable(&reflectionConstants, 0, "phalcon/Annotations/Reflection.zep", 97);
			if (Z_TYPE_P(&reflectionConstants) == IS_ARRAY) {
				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(&reflectionConstants), _3$$4, _4$$4, _2$$4)
				{
					ZEPHIR_INIT_NVAR(&constant);
					if (_4$$4 != NULL) { 
						ZVAL_STR_COPY(&constant, _4$$4);
					} else {
						ZVAL_LONG(&constant, _3$$4);
					}
					ZEPHIR_INIT_NVAR(&reflectionConstant);
					ZVAL_COPY(&reflectionConstant, _2$$4);
					ZEPHIR_INIT_NVAR(&_5$$5);
					object_init_ex(&_5$$5, phalcon_annotations_collection_ce);
					ZEPHIR_CALL_METHOD(NULL, &_5$$5, "__construct", &_6, 86, &reflectionConstant);
					zephir_check_call_status();
					zephir_update_property_array(this_ptr, SL("constantAnnotations"), &constant, &_5$$5);
				} ZEND_HASH_FOREACH_END();
			} else {
				ZEPHIR_CALL_METHOD(NULL, &reflectionConstants, "rewind", NULL, 0);
				zephir_check_call_status();
				_8$$4 = 1;
				while (1) {
					if (_8$$4) {
						_8$$4 = 0;
					} else {
						ZEPHIR_CALL_METHOD(NULL, &reflectionConstants, "next", NULL, 0);
						zephir_check_call_status();
					}
					ZEPHIR_CALL_METHOD(&_7$$4, &reflectionConstants, "valid", NULL, 0);
					zephir_check_call_status();
					if (!zend_is_true(&_7$$4)) {
						break;
					}
					ZEPHIR_CALL_METHOD(&constant, &reflectionConstants, "key", NULL, 0);
					zephir_check_call_status();
					ZEPHIR_CALL_METHOD(&reflectionConstant, &reflectionConstants, "current", NULL, 0);
					zephir_check_call_status();
						ZEPHIR_INIT_NVAR(&_9$$6);
						object_init_ex(&_9$$6, phalcon_annotations_collection_ce);
						ZEPHIR_CALL_METHOD(NULL, &_9$$6, "__construct", &_6, 86, &reflectionConstant);
						zephir_check_call_status();
						zephir_update_property_array(this_ptr, SL("constantAnnotations"), &constant, &_9$$6);
				}
			}
			ZEPHIR_INIT_NVAR(&reflectionConstant);
			ZEPHIR_INIT_NVAR(&constant);
		}
	}
	RETURN_MM_MEMBER_TYPED(getThis(), "constantAnnotations", IS_ARRAY);
}

/**
 * Returns the annotations found in the properties' docblocks
 *
 * @return Collection[]
 */
PHP_METHOD(Phalcon_Annotations_Reflection, getPropertiesAnnotations)
{
	zend_string *_4$$4;
	zend_ulong _3$$4;
	zend_bool _1$$3, _8$$4;
	zval reflectionProperties, property, reflectionProperty, _0, *_2$$4, _7$$4, _5$$5, _9$$6;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zephir_fcall_cache_entry *_6 = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&reflectionProperties);
	ZVAL_UNDEF(&property);
	ZVAL_UNDEF(&reflectionProperty);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_7$$4);
	ZVAL_UNDEF(&_5$$5);
	ZVAL_UNDEF(&_9$$6);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("reflectionData", 14, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	zephir_memory_observe(&reflectionProperties);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 404, PH_NOISY_CC | PH_READONLY);
	if (zephir_array_isset_string_fetch(&reflectionProperties, &_0, SL("properties"), 0)) {
		_1$$3 = Z_TYPE_P(&reflectionProperties) == IS_ARRAY;
		if (_1$$3) {
			_1$$3 = !(ZEPHIR_IS_EMPTY(&reflectionProperties));
		}
		if (_1$$3) {
			zephir_is_iterable(&reflectionProperties, 0, "phalcon/Annotations/Reflection.zep", 119);
			if (Z_TYPE_P(&reflectionProperties) == IS_ARRAY) {
				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(&reflectionProperties), _3$$4, _4$$4, _2$$4)
				{
					ZEPHIR_INIT_NVAR(&property);
					if (_4$$4 != NULL) { 
						ZVAL_STR_COPY(&property, _4$$4);
					} else {
						ZVAL_LONG(&property, _3$$4);
					}
					ZEPHIR_INIT_NVAR(&reflectionProperty);
					ZVAL_COPY(&reflectionProperty, _2$$4);
					ZEPHIR_INIT_NVAR(&_5$$5);
					object_init_ex(&_5$$5, phalcon_annotations_collection_ce);
					ZEPHIR_CALL_METHOD(NULL, &_5$$5, "__construct", &_6, 86, &reflectionProperty);
					zephir_check_call_status();
					zephir_update_property_array(this_ptr, SL("propertyAnnotations"), &property, &_5$$5);
				} ZEND_HASH_FOREACH_END();
			} else {
				ZEPHIR_CALL_METHOD(NULL, &reflectionProperties, "rewind", NULL, 0);
				zephir_check_call_status();
				_8$$4 = 1;
				while (1) {
					if (_8$$4) {
						_8$$4 = 0;
					} else {
						ZEPHIR_CALL_METHOD(NULL, &reflectionProperties, "next", NULL, 0);
						zephir_check_call_status();
					}
					ZEPHIR_CALL_METHOD(&_7$$4, &reflectionProperties, "valid", NULL, 0);
					zephir_check_call_status();
					if (!zend_is_true(&_7$$4)) {
						break;
					}
					ZEPHIR_CALL_METHOD(&property, &reflectionProperties, "key", NULL, 0);
					zephir_check_call_status();
					ZEPHIR_CALL_METHOD(&reflectionProperty, &reflectionProperties, "current", NULL, 0);
					zephir_check_call_status();
						ZEPHIR_INIT_NVAR(&_9$$6);
						object_init_ex(&_9$$6, phalcon_annotations_collection_ce);
						ZEPHIR_CALL_METHOD(NULL, &_9$$6, "__construct", &_6, 86, &reflectionProperty);
						zephir_check_call_status();
						zephir_update_property_array(this_ptr, SL("propertyAnnotations"), &property, &_9$$6);
				}
			}
			ZEPHIR_INIT_NVAR(&reflectionProperty);
			ZEPHIR_INIT_NVAR(&property);
		}
	}
	RETURN_MM_MEMBER_TYPED(getThis(), "propertyAnnotations", IS_ARRAY);
}

/**
 * Returns the annotations found in the methods' docblocks
 *
 * @return Collection[]
 */
PHP_METHOD(Phalcon_Annotations_Reflection, getMethodsAnnotations)
{
	zend_string *_4$$4;
	zend_ulong _3$$4;
	zend_bool _1$$3, _8$$4;
	zval reflectionMethods, methodName, reflectionMethod, _0, *_2$$4, _7$$4, _5$$5, _9$$6;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
	zephir_fcall_cache_entry *_6 = NULL;
	zend_long ZEPHIR_LAST_CALL_STATUS;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&reflectionMethods);
	ZVAL_UNDEF(&methodName);
	ZVAL_UNDEF(&reflectionMethod);
	ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_7$$4);
	ZVAL_UNDEF(&_5$$5);
	ZVAL_UNDEF(&_9$$6);
	static zend_string *_zephir_prop_0 = NULL;
	if (UNEXPECTED(!_zephir_prop_0)) {
		_zephir_prop_0 = zend_string_init("reflectionData", 14, 1);
	}
	ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
	zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);

	zephir_memory_observe(&reflectionMethods);
	zephir_read_property_cached(&_0, this_ptr, _zephir_prop_0, 404, PH_NOISY_CC | PH_READONLY);
	if (zephir_array_isset_string_fetch(&reflectionMethods, &_0, SL("methods"), 0)) {
		_1$$3 = Z_TYPE_P(&reflectionMethods) == IS_ARRAY;
		if (_1$$3) {
			_1$$3 = !(ZEPHIR_IS_EMPTY(&reflectionMethods));
		}
		if (_1$$3) {
			zephir_is_iterable(&reflectionMethods, 0, "phalcon/Annotations/Reflection.zep", 141);
			if (Z_TYPE_P(&reflectionMethods) == IS_ARRAY) {
				ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(&reflectionMethods), _3$$4, _4$$4, _2$$4)
				{
					ZEPHIR_INIT_NVAR(&methodName);
					if (_4$$4 != NULL) { 
						ZVAL_STR_COPY(&methodName, _4$$4);
					} else {
						ZVAL_LONG(&methodName, _3$$4);
					}
					ZEPHIR_INIT_NVAR(&reflectionMethod);
					ZVAL_COPY(&reflectionMethod, _2$$4);
					ZEPHIR_INIT_NVAR(&_5$$5);
					object_init_ex(&_5$$5, phalcon_annotations_collection_ce);
					ZEPHIR_CALL_METHOD(NULL, &_5$$5, "__construct", &_6, 86, &reflectionMethod);
					zephir_check_call_status();
					zephir_update_property_array(this_ptr, SL("methodAnnotations"), &methodName, &_5$$5);
				} ZEND_HASH_FOREACH_END();
			} else {
				ZEPHIR_CALL_METHOD(NULL, &reflectionMethods, "rewind", NULL, 0);
				zephir_check_call_status();
				_8$$4 = 1;
				while (1) {
					if (_8$$4) {
						_8$$4 = 0;
					} else {
						ZEPHIR_CALL_METHOD(NULL, &reflectionMethods, "next", NULL, 0);
						zephir_check_call_status();
					}
					ZEPHIR_CALL_METHOD(&_7$$4, &reflectionMethods, "valid", NULL, 0);
					zephir_check_call_status();
					if (!zend_is_true(&_7$$4)) {
						break;
					}
					ZEPHIR_CALL_METHOD(&methodName, &reflectionMethods, "key", NULL, 0);
					zephir_check_call_status();
					ZEPHIR_CALL_METHOD(&reflectionMethod, &reflectionMethods, "current", NULL, 0);
					zephir_check_call_status();
						ZEPHIR_INIT_NVAR(&_9$$6);
						object_init_ex(&_9$$6, phalcon_annotations_collection_ce);
						ZEPHIR_CALL_METHOD(NULL, &_9$$6, "__construct", &_6, 86, &reflectionMethod);
						zephir_check_call_status();
						zephir_update_property_array(this_ptr, SL("methodAnnotations"), &methodName, &_9$$6);
				}
			}
			ZEPHIR_INIT_NVAR(&reflectionMethod);
			ZEPHIR_INIT_NVAR(&methodName);
		}
	}
	RETURN_MM_MEMBER_TYPED(getThis(), "methodAnnotations", IS_ARRAY);
}

/**
 * Returns the raw parsing intermediate definitions used to construct the
 * reflection
 *
 * @return array
 */
PHP_METHOD(Phalcon_Annotations_Reflection, getReflectionData)
{

	RETURN_MEMBER_TYPED(getThis(), "reflectionData", IS_ARRAY);
}

zend_object *zephir_init_properties_Phalcon_Annotations_Reflection(zend_class_entry *class_type)
{
		zval _0, _2, _4, _6, _1$$3, _3$$4, _5$$5, _7$$6;
	zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL;
		ZVAL_UNDEF(&_0);
	ZVAL_UNDEF(&_2);
	ZVAL_UNDEF(&_4);
	ZVAL_UNDEF(&_6);
	ZVAL_UNDEF(&_1$$3);
	ZVAL_UNDEF(&_3$$4);
	ZVAL_UNDEF(&_5$$5);
	ZVAL_UNDEF(&_7$$6);
	

		ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0);
		zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__);
	
	{
		zval local_this_ptr, *this_ptr = &local_this_ptr;
		ZEPHIR_CREATE_OBJECT(this_ptr, class_type);
		zephir_read_property_ex(&_0, this_ptr, ZEND_STRL("reflectionData"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_0) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_1$$3);
			array_init(&_1$$3);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("reflectionData"), &_1$$3);
		}
		zephir_read_property_ex(&_2, this_ptr, ZEND_STRL("methodAnnotations"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_2) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_3$$4);
			array_init(&_3$$4);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("methodAnnotations"), &_3$$4);
		}
		zephir_read_property_ex(&_4, this_ptr, ZEND_STRL("propertyAnnotations"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_4) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_5$$5);
			array_init(&_5$$5);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("propertyAnnotations"), &_5$$5);
		}
		zephir_read_property_ex(&_6, this_ptr, ZEND_STRL("constantAnnotations"), PH_NOISY_CC | PH_READONLY);
		if (Z_TYPE_P(&_6) == IS_NULL) {
			ZEPHIR_INIT_VAR(&_7$$6);
			array_init(&_7$$6);
			zephir_update_property_zval_ex(this_ptr, ZEND_STRL("constantAnnotations"), &_7$$6);
		}
		ZEPHIR_MM_RESTORE();
		return Z_OBJ_P(this_ptr);
	}
}

