#include "../../common.h"

#include "../../ds/ds_vector.h"
#include "../objects/php_vector.h"
#include "php_vector_iterator.h"

static void php_ds_vector_iterator_dtor(zend_object_iterator *iter)
{
    php_ds_vector_iterator_t *iterator = (php_ds_vector_iterator_t *) iter;

    OBJ_RELEASE(iterator->object);
}

static int php_ds_vector_iterator_valid(zend_object_iterator *iter)
{
    php_ds_vector_iterator_t *iterator = (php_ds_vector_iterator_t *) iter;

    return iterator->position < iterator->vector->size ? SUCCESS : FAILURE;
}

static zval *php_ds_vector_iterator_get_current_data(zend_object_iterator *iter)
{
    php_ds_vector_iterator_t *iterator = (php_ds_vector_iterator_t *) iter;

    return &iterator->vector->buffer[iterator->position];
}

static void php_ds_vector_iterator_get_current_key(zend_object_iterator *iter, zval *key)
{
    ZVAL_LONG(key, ((php_ds_vector_iterator_t *) iter)->position);
}

static void php_ds_vector_iterator_move_forward(zend_object_iterator *iter)
{
    ((php_ds_vector_iterator_t *) iter)->position++;
}

static void php_ds_vector_iterator_rewind(zend_object_iterator *iter)
{
    ((php_ds_vector_iterator_t *) iter)->position = 0;
}

static zend_object_iterator_funcs php_ds_vector_iterator_funcs = {
    php_ds_vector_iterator_dtor,
    php_ds_vector_iterator_valid,
    php_ds_vector_iterator_get_current_data,
    php_ds_vector_iterator_get_current_key,
    php_ds_vector_iterator_move_forward,
    php_ds_vector_iterator_rewind
};

static zend_object_iterator *php_ds_vector_create_iterator(zval *obj, int by_ref)
{
    php_ds_vector_iterator_t *iterator;

    if (by_ref) {
        ITERATION_BY_REF_NOT_SUPPORTED();
        return NULL;
    }

    iterator = ecalloc(1, sizeof(php_ds_vector_iterator_t));

    zend_iterator_init((zend_object_iterator*) iterator);

    iterator->intern.funcs  = &php_ds_vector_iterator_funcs;
    iterator->vector        = Z_DS_VECTOR_P(obj);
    iterator->object        = Z_OBJ_P(obj);
    iterator->position      = 0;

    // Add a reference to the object so that it doesn't get collected when
    // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
#if PHP_VERSION_ID >= 70300
    GC_ADDREF(iterator->object);
#else
    ++GC_REFCOUNT(iterator->object);
#endif

    return (zend_object_iterator *) iterator;
}

zend_object_iterator *php_ds_vector_get_iterator(zend_class_entry *ce, zval *obj, int by_ref)
{
    return php_ds_vector_create_iterator(obj, by_ref);
}
