/*
Copyright (C) 2001-2003, The Perl Foundation.
$Id: iterator.pmc 16275 2006-12-28 01:20:44Z particle $

=head1 NAME

src/pmc/iterator.pmc - Iterator PMC

=head1 DESCRIPTION

These are the vtable functions for the Iterator base class

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"

pmclass Iterator no_ro {

    void class_init() {
        if (pass) {
            /* Make the shift_pmc vtable function available as
             * a method named "next" */
            enter_nci_method(INTERP, enum_class_Iterator,
                    F2DPTR(Parrot_Iterator_shift_pmc), "next", "PJO");
        }
    }

/*

=item C<void init()>

Raises an exception. Use C<init_pmc()>.

=cut

*/

    void init() {
        real_exception(INTERP, NULL, E_Exception,
                "Iterator init without aggregate");
    }

/*

=item C<void init_pmc(PMC *initializer)>

Initializes the iterator with an aggregate PMC.
Defaults iteration mode to iterate from start.

=cut

*/

    void init_pmc(PMC* aggregate) {
        PMC_pmc_val(SELF) = aggregate;
        PMC_struct_val(SELF) = NULL;
        PObj_custom_mark_SET(SELF);
        /* by default, iterate from start */
        DYNSELF.set_integer_native(ITERATE_FROM_START);
    }

/*

=item C<void mark()>

Marks the current idx/key and the aggregate as live.

=cut

*/

    void mark() {
        /* the KEY */
        if (PMC_struct_val(SELF))
             pobject_lives(INTERP, (PObj *) PMC_struct_val(SELF));

        /* the aggregate */
        if (PMC_pmc_val(SELF))
             pobject_lives(INTERP, (PObj *) PMC_pmc_val(SELF));
    }

/*

=item C<PMC *clone()>

Unimplemented. Calls the C<default> method.

=cut

*/

    PMC* clone() {
        return SUPER();
        /* XXX (#40799): TODO */
    }

/*

=item C<INTVAL get_integer()>

Get number of remaining elements. Does not work for hashes yet.
TODO: keep track of current position and direction

=cut

*/

    INTVAL get_integer() {
        return SELF.elements();
    }

/*

=item C<STRING *get_string()>

Returns a textual representation of the iterator.

=cut

*/

    STRING* get_string() {
        PMC * const key = PMC_struct_val(SELF);
        return string_from_int(INTERP, key_integer(INTERP, key));
    }

/*

=item C<INTVAL get_integer_keyed(PMC *key)>

=cut

*/

    INTVAL get_integer_keyed(PMC* key) {
        return VTABLE_get_integer_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL idx)>

Get integer value of current position plus idx.

=cut

*/

    INTVAL get_integer_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_integer_keyed_int(INTERP, agg, PMC_int_val(key) +idx);
    }

/*

=item C<FLOATVAL get_number_keyed(PMC *key)>

=cut

*/

    FLOATVAL get_number_keyed(PMC* key) {
        return VTABLE_get_number_keyed(INTERP,(PMC *)PMC_pmc_val(SELF), key );
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL idx)>

Get number value of current position plus idx.

=cut

*/

    FLOATVAL get_number_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_number_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }



/*

=item C<STRING *get_string_keyed(PMC *key)>

=cut

*/

    STRING* get_string_keyed(PMC* key) {
        return VTABLE_get_string_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<STRING *get_string_keyed_int(INTVAL idx)>

Get string value of current position plus idx.

=cut

*/

    STRING* get_string_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_string_keyed_int(INTERP, agg, PMC_int_val(key) + idx);
    }

/*

=item C<INTVAL get_bool()>

Returns true if the idx/key is not C<-1>.

=cut

*/

    INTVAL get_bool() {
        PMC * const key = PMC_struct_val(SELF);
        return key && PMC_int_val(key) != -1;
    }

/*

=item C<INTVAL elements()>

Returns the number of remaining elements in the aggregate. TODO.

=cut

*/

    INTVAL elements() {
        return VTABLE_elements(INTERP, (PMC *)PMC_pmc_val(SELF));
    }

/*

=item C<PMC *get_pmc_keyed(PMC *key)>

Returns the element for C<*key>.

=cut

*/

    PMC* get_pmc_keyed(PMC* key) {
        return VTABLE_get_pmc_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<PMC *get_pmc_keyed_int(INTVAL key)>

Returns the element for C<key>.

=cut

*/

    PMC* get_pmc_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_get_pmc_keyed_int(INTERP, agg, PMC_int_val(key) + idx );
    }

/*

=item C<void set_integer_native(INTVAL value)>

Reset the Iterator. C<value> must be one of

 ITERATE_FROM_START        ... Iterate from start
 ITERATE_FROM_START_KEYS   ... OrderedHash by keys
 ITERATE_FROM_END          ... Arrays and PerlString only

=cut

*/

    void set_integer_native(INTVAL value) {
        PMC *key, *agg;
        if (value < ITERATE_FROM_START || value > ITERATE_FROM_END)
            real_exception(INTERP, NULL, E_TypeError,
                    "Illegal set_integer on iterator");
        /* reset iterator on aggregate */
        agg = (PMC*) PMC_pmc_val(SELF);
        if (agg->vtable->base_type == enum_class_Slice) {
            /* its an xrange serving as its own aggregate */
            PMC_struct_val(SELF) =
                VTABLE_nextkey_keyed(INTERP, agg, NULL, value);
        }
        else {
            if (!PMC_struct_val(SELF)) {
                PMC_struct_val(SELF) = key_new(INTERP);
            }
            key = PMC_struct_val(SELF);
            PMC_struct_val(SELF) =
                VTABLE_nextkey_keyed(INTERP, key, agg, value);
        }
    }

/*

=item C<INTVAL pop_integer()>

=cut

*/

    INTVAL pop_integer() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        const INTVAL ret = VTABLE_get_integer_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);
        return ret;
    }

/*

=item C<FLOATVAL pop_float()>

=cut

*/

    FLOATVAL pop_float() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        const FLOATVAL ret = VTABLE_get_number_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);
        return ret;
    }

/*

=item C<STRING *pop_string()>

=cut

*/

    STRING* pop_string() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        STRING * const ret = VTABLE_get_string_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);
        return ret;
    }

/*

=item C<PMC *pop_pmc()>

Returns the element for the current idx/key and sets the idx/key to
the previous one.

=cut

*/

    PMC* pop_pmc() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        PMC * const ret = VTABLE_get_pmc_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_PREV);
        return ret;
    }

/*

=item C<INTVAL shift_integer()>

Returns the element for the current idx/key and sets the idx/key to
the next one.

=cut

*/

    INTVAL shift_integer() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        const INTVAL ret = VTABLE_get_integer_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }

/*

=item C<void* invoke(void* next)>

Return the next element of the aggregate. The return type may depend on
the aggregate. If there are no more items in the aggregate I1 .. I4 are
zero.

See F<docs/pdds/pdd03_calling_conventions.pod>.

=cut

*/
    void* invoke(void *next) {
        PMC *agg, *key, *res;
        INTVAL ires;

        if (REG_INT(3) == 1) {       /* iterator constructor */
            PMC *iter, *arg = REG_PMC(5);
            if (PObj_is_object_TEST(arg)) {
                REG_PMC(5) = VTABLE_get_iter(INTERP, arg);
                return next;
            }
            REG_PMC(5) = iter = pmc_new_init(INTERP,
                    enum_class_Iterator, arg);
            VTABLE_set_integer_native(INTERP, iter, 0);
            return next;
        }
        else if (REG_INT(3) == 2)   /* TODO function+sentinel */
            real_exception(INTERP, NULL, E_AssertionError,
                    "Iterator: invoke 2 args");

        REG_INT(1) = REG_INT(2) = REG_INT(3) = REG_INT(4) = 0;
        key = PMC_struct_val(SELF);
        agg = PMC_pmc_val(SELF);
        switch (agg->vtable->base_type) {
            case enum_class_IntList:
                ires = DYNSELF.shift_integer();
                REG_INT(1) = 1;
                REG_INT(5) = ires;
                return next;
            default:
                res = DYNSELF.shift_pmc();
                REG_INT(3) = 1;
                REG_PMC(5) = res;
                return next;
        }
        return NULL;
    }

/*

=item C<FLOATVAL shift_float()>

=cut

*/

    FLOATVAL shift_float() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        const FLOATVAL ret = VTABLE_get_number_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }


/*

=item C<STRING *shift_string()>

=cut

*/

    STRING* shift_string() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        STRING * const ret = VTABLE_get_string_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }

/*

=item C<PMC* shift_pmc()>

Returns the element for the current idx/key and sets the idx/key to
the next one.

=cut

*/

    PMC* shift_pmc() {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        PMC *ret;
        if (key_integer(INTERP, key) == -1) {
            real_exception(INTERP, NULL, E_StopIteration, "StopIteration");
            return NULL;
        }
        ret = VTABLE_get_pmc_keyed(INTERP, agg, key);
        PMC_struct_val(SELF) =
            VTABLE_nextkey_keyed(INTERP, key, agg, ITERATE_GET_NEXT);
        return ret;
    }

/*

=item C<INTVAL exists_keyed(PMC *key)>

Returns whether an element for C<*key> exists in the aggregate.

=cut

*/

    INTVAL exists_keyed(PMC* key) {
        return VTABLE_exists_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL idx)>

Returns whether an element for C<idx> exists in the aggregate.

=cut

*/

    INTVAL exists_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_exists_keyed_int(INTERP, agg, PMC_int_val(key) + idx );
    }

/*

=item C<INTVAL defined()>

Returns whether the iterator contains an aggregate.

=cut

*/

    INTVAL defined() {
        return (INTVAL) (PMC_pmc_val(SELF) != 0);
    }

/*

=item C<INTVAL defined_keyed(PMC *key)>

=cut

*/

    INTVAL defined_keyed(PMC* key) {
        return VTABLE_defined_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Returns the result of calling C<defined_keyed(key)> on the aggregate.

=cut

*/

    INTVAL defined_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_defined_keyed_int(INTERP, agg, PMC_int_val(key) + idx );
    }

/*

=item C<INTVAL type_keyed(PMC *key)>

Returns the result of calling C<type_keyed(key)> on the aggregate.

=cut

*/

    INTVAL type_keyed(PMC* key) {
        return VTABLE_type_keyed(INTERP, (PMC *)PMC_pmc_val(SELF), key);
    }

/*

=item C<INTVAL type_keyed_int(PMC *idx)>

Returns the result of calling C<type_keyed(key)> on the aggregate.

=cut

*/

    INTVAL type_keyed_int(INTVAL idx) {
        PMC * const key = PMC_struct_val(SELF);
        PMC * const agg = PMC_pmc_val(SELF);
        return VTABLE_type_keyed_int(INTERP, agg, PMC_int_val(key) + idx );
    }

    PMC* get_iter() {
        return SELF;
    }
}

/*

=back

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
