/*
Copyright (C) 2001-2009, Parrot Foundation.
$Id: stringiterator.pmc 47643 2010-06-15 16:52:49Z NotFound $

=head1 NAME

src/pmc/stringiterator.pmc - StringIterator PMC

=head1 DESCRIPTION

Implementation of Iterator for String PMC.

=head1 SYNOPSIS


=head1 Methods

=over 4

=cut

*/


/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */

pmclass StringIterator auto_attrs extends Iterator {
    ATTR PMC    *string;    /* String to iterate over */
    ATTR INTVAL  pos;       /* Current position of iterator for forward iterator */
                            /* Previous position of iterator for reverse iterator */
    ATTR INTVAL  length;    /* Length of C<string> */
    ATTR INTVAL  reverse;   /* Direction of iteration. 1 - for reverse iteration */

/*

=item C<void init_pmc()>

Initialize StringIterator.

=cut

*/
    VTABLE void init_pmc(PMC *string) {
        SET_ATTR_string(INTERP, SELF, string);

        /* by default, iterate from start */
        SELF.set_integer_native(ITERATE_FROM_START);
        PObj_custom_mark_SET(SELF);
    }

/*

=item C<void mark()>

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

=cut

*/

    VTABLE void mark() {
        PMC *string;
        GET_ATTR_string(INTERP, SELF, string);
        Parrot_gc_mark_PMC_alive(INTERP, string);
    }

/*

=item C<PMC *clone()>

=cut

*/
    VTABLE PMC* clone() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);
        PMC                              * const clone =
                Parrot_pmc_new_init(INTERP, enum_class_StringIterator, attrs->string);
        Parrot_StringIterator_attributes * const clone_attrs =
                PARROT_STRINGITERATOR(clone);

        clone_attrs->pos     = attrs->pos;
        clone_attrs->reverse = attrs->reverse;
        return clone;
    }

/*

=item C<INTVAL get_bool()>

Returns true if there is more elements to iterate over.

=cut

*/

    VTABLE INTVAL get_bool() {
        return SELF.elements() > 0;
    }

/*

=item C<INTVAL elements()>

Returns the number of remaining elements in the C<string>.

=cut

*/

    VTABLE INTVAL elements() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);
        if (attrs->reverse)
            return attrs->pos;
        else
            return attrs->length - attrs->pos;
    }

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

/*

=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_END          ... Iterate from end

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);
        switch (value) {
          case ITERATE_FROM_START:
            attrs->reverse   = 0;
            attrs->pos       = 0;
            attrs->length    = VTABLE_elements(INTERP, attrs->string);
            break;
          case ITERATE_FROM_END:
            attrs->reverse   = 1;
            attrs->pos       = attrs->length
                             = VTABLE_elements(INTERP, attrs->string);
            break;
          default:
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                    "Wrong direction for StringIterator");
        }
    }

/*

=item C<PMC *get_pmc()>

Returns this Iterator's string.

=cut

*/

    VTABLE PMC *get_pmc() {
        PMC *string;
        GET_ATTR_string(INTERP, SELF, string);
        return string ? string : PMCNULL;
    }

/*

=item C<STRING *shift_pmc()>

Shift next character from C<string> as PMC.

=cut

*/
    VTABLE PMC *shift_pmc() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);
        PMC *ret;

        if (attrs->pos >= attrs->length)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        ret = Parrot_pmc_new(INTERP, Parrot_get_ctx_HLL_type(interp, enum_class_String));
        VTABLE_set_string_native(INTERP, ret,
                VTABLE_get_string_keyed_int(INTERP, attrs->string, attrs->pos++));
        return ret;
    }

/*

=item C<STRING *shift_string()>

Shift next character from C<string>.

=cut

*/
    VTABLE STRING *shift_string() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);

        if (attrs->pos >= attrs->length)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        return VTABLE_get_string_keyed_int(INTERP, attrs->string, attrs->pos++);
    }

/*

=item C<INTVAL shift_integer()>

Shift next character code from C<string>.

=cut

*/
    VTABLE INTVAL shift_integer() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);

        if (attrs->pos >= attrs->length)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        return VTABLE_get_integer_keyed_int(INTERP, attrs->string, attrs->pos++);
    }

/*

=item C<STRING *pop_pmc()>

Shift "next" character from C<string> for reverse iterator as PMC.

=cut

*/
    VTABLE PMC *pop_pmc() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);
        PMC *ret;

        if (!STATICSELF.get_bool())
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        ret = Parrot_pmc_new(INTERP, Parrot_get_ctx_HLL_type(interp, enum_class_String));
        VTABLE_set_string_native(INTERP, ret,
                VTABLE_get_string_keyed_int(INTERP, attrs->string, --attrs->pos));
        return ret;
    }

/*

=item C<STRING *pop_string()>

Shift "next" character from C<string> for reverse iterator.

=cut

*/
    VTABLE STRING *pop_string() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);

        if (!STATICSELF.get_bool())
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        return VTABLE_get_string_keyed_int(INTERP, attrs->string, --attrs->pos);
    }

/*

=item C<INTVAL pop_integer()>

Shift "next" character code from C<string> for reverse iterator.

=cut

*/
    VTABLE INTVAL pop_integer() {
        Parrot_StringIterator_attributes * const attrs =
                PARROT_STRINGITERATOR(SELF);

        if (!STATICSELF.get_bool())
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "StopIteration");

        return VTABLE_get_integer_keyed_int(INTERP, attrs->string, --attrs->pos);
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL idx)>

Get integer value of current position plus idx.

=cut

*/

    VTABLE INTVAL get_integer_keyed_int(INTVAL idx) {
        return VTABLE_get_integer_keyed_int(INTERP, STATICSELF.get_pmc(),
                PARROT_STRINGITERATOR(SELF)->pos + idx);
    }

/*

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

Get string value of current position plus idx.

=cut

*/

    VTABLE STRING *get_string_keyed_int(INTVAL idx) {
        return VTABLE_get_string_keyed_int(INTERP, STATICSELF.get_pmc(),
                PARROT_STRINGITERATOR(SELF)->pos + idx);
    }
}


/*

=back

=cut

*/

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