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

=head1 NAME

src/pmc/fixedpmcarray.pmc - fixed size array for PMCs only

=head1 DESCRIPTION

This class, FixedPMCArray, implements an array of fixed size, which stores PMCs,
it puts things into Integer, Float, or String PMCs as appropriate

=head2 Note

The flag C<PObj_private0_FLAG> is used in the C<NameSpace> PMC and should
never be set for user arrays.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"

static void
swap(void **x, void **y)
{
    void *t = *x;
    *x = *y;
    *y = t;
}

typedef INTVAL(*sort_func_t)(Interp *, void*, void*);

static INTVAL
COMPARE(Interp *interp, void *a, void *b, PMC *cmp)
{
    if (!cmp)
        return mmd_dispatch_i_pp(interp, a, b, MMD_CMP);
    if (cmp->vtable->base_type == enum_class_NCI) {
        sort_func_t f = (sort_func_t)D2FPTR(PMC_struct_val(cmp));
        return f(interp, a, b);
    }
    return Parrot_runops_fromc_args_reti(interp, cmp, "IPP", a, b);
}

static void
quicksort(Interp *interp, void **data, UINTVAL n, PMC *cmp)
{
    UINTVAL i, j, ln, rn;

    while (n > 1) {
        swap(&data[0], &data[n/2]);
        for (i = 0, j = n; ; ) {
            do
                --j;
            while (COMPARE(interp, data[j], data[0], cmp) > 0);
            do
                ++i;
            while (i < j && COMPARE(interp, data[i], data[0], cmp) < 0);
            if (i >= j)
                break;
            swap(&data[i], &data[j]);
        }
        swap(&data[j], &data[0]);
        ln = j;
        rn = n - ++j;
        if (ln < rn) {
            quicksort(interp, data, ln, cmp);
            data += j;
            n = rn;
        }
        else {
            quicksort(interp, data + j, rn, cmp);
            n = ln;
        }
    }
}

pmclass FixedPMCArray need_ext does array {

/*

=item C<METHOD void sort(PMC* cmp_func)>

Sort this array, optionally using the provided cmp_func

=cut

*/

METHOD void sort(PMC *cmp_func) {
    UINTVAL n;


    n = (UINTVAL) PMC_int_val(SELF);
    if (n <= 1)
        return;
    if (PMC_IS_NULL(cmp_func))
        cmp_func = NULL;
    quicksort(interp, PMC_data(SELF), n, cmp_func);
}

/*

=back

=head2 Methods

=over 4

=item C<void init()>

Initializes the array.

=cut

*/

    void init() {
        PMC_int_val(SELF) = 0;
        PMC_data(SELF) = NULL;
        PObj_active_destroy_SET(SELF);
        PObj_data_is_PMC_array_SET(SELF);
    }

/*

=item C<void morph(INTVAL type)>

Const PMCs must have a C<morph()>.

=cut

*/

    void morph(INTVAL type) {
        SUPER(type);
    }


/*

=item C<void destroy()>

Destroys the array.

=cut

*/

    void destroy() {
        if (PMC_data(SELF)) {
            mem_sys_free(PMC_data(SELF));
            PMC_data(SELF) = NULL;
        }
        PMC_int_val(SELF) = 0;
    }

/*

=item C<PMC *clone()>

Creates and returns a copy of the array.

=cut

*/

    PMC* clone() {
        PMC * dest = pmc_new(INTERP, SELF->vtable->base_type);
        INTVAL size = PMC_int_val(SELF);
        if (size) {
            PMC_int_val(dest) = size;
            PMC_data(dest) = mem_sys_allocate(size * sizeof (PMC*));
            mem_sys_memcopy(PMC_data(dest), PMC_data(SELF), size*sizeof (PMC*));
            PObj_data_is_PMC_array_SET(dest);
        }
        return dest;
    }

/*

=item C<INTVAL get_bool()>

Returns whether the array has any elements (meaning been initialized, for a
fixed sized array).

=cut

*/
    INTVAL get_bool() {
        INTVAL size = DYNSELF.elements();
        return (INTVAL)(size != 0);
    }

/*

=item C<INTVAL elements()>

=cut

*/

    INTVAL elements() {
        return PMC_int_val(SELF);
    }

/*

=item C<INTVAL get_integer()>

Returns the number of elements in the array.

=cut

*/

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

/*

=item C<STRING *get_string()>

Returns the number of elements in the array as a Parrot string. (??? -leo)

=item C<STRING *get_repr()>

Returns a string representation of the array contents.
TODO implement freeze/thaw and use that instead.

=cut

*/

    STRING* get_string() {
        return string_from_int(INTERP, DYNSELF.elements());
    }

    STRING* get_repr() {
        STRING *res;
        INTVAL j, n;
        PMC *val;

        res = string_from_cstring(INTERP, "(", 0);
        n = VTABLE_elements(INTERP, SELF);
        for (j = 0; j < n; ++j) {
            val = SELF.get_pmc_keyed_int(j);
            res = string_append(INTERP, res,
                    VTABLE_get_repr(INTERP, val));
            if (n == 1)
                res = string_append(INTERP, res,
                        const_string(INTERP, ","));
            else if (j < n - 1)
                res = string_append(INTERP, res,
                        const_string(INTERP, ", "));
        }
        res = string_append(INTERP, res,
                const_string(INTERP, ")"));
        return res;

    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

Returns the integer value of the element at index C<key>.

=cut

*/

    INTVAL get_integer_keyed_int(INTVAL key) {
        PMC *tempPMC = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_integer(INTERP, tempPMC);
    }

/*

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

Returns the integer value of the element at index C<*key>.

=cut

*/

    INTVAL get_integer_keyed(PMC* key) {
        PMC *tempPMC;

        tempPMC = DYNSELF.get_pmc_keyed(key);
        return VTABLE_get_integer(INTERP, tempPMC);
    }


/*

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

Returns the floating-point value of the element at index C<key>.

=cut

*/

    FLOATVAL get_number_keyed_int(INTVAL key) {
        PMC *tempPMC = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_number(INTERP, tempPMC);
    }

/*

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

Returns the floating-point value of the element at index C<*key>.

=cut

*/

    FLOATVAL get_number_keyed(PMC* key) {
        PMC *tempPMC;

        tempPMC = DYNSELF.get_pmc_keyed(key);
        return VTABLE_get_number(INTERP, tempPMC);
    }

/*

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

Returns the Parrot string value of the element at index C<key>.

=cut

*/

    STRING* get_string_keyed_int(INTVAL key) {
        PMC *tempPMC = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_string(INTERP, tempPMC);
    }

/*

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

Returns the Parrot string value of the element at index C<*key>.

=cut

*/

    STRING* get_string_keyed(PMC* key) {
        PMC *tempPMC;

        tempPMC = DYNSELF.get_pmc_keyed(key);
        return VTABLE_get_string(INTERP, tempPMC);
    }


/*

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

Returns the PMC value of the element at index C<key>.

=cut

*/

    PMC* get_pmc_keyed_int(INTVAL key) {
        PMC **data;
        if (key < 0 || key >= PMC_int_val(SELF))
            real_exception(INTERP, NULL, E_IndexError,
                "FixedPMCArray: index out of bounds!");

        data = (PMC **)PMC_data(SELF);
        return data[key];
    }

/*

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

Returns the PMC value of the element at index C<*key>.

=cut

*/

    PMC* get_pmc_keyed(PMC* key) {
        INTVAL k = key_integer(INTERP, key);
        PMC *box, *nextkey;

        nextkey = key_next(INTERP, key);
        if (!nextkey)
            return DYNSELF.get_pmc_keyed_int(k);
        box = SELF.get_pmc_keyed_int(k);
        if (box == NULL)
            box = pmc_new(INTERP, enum_class_Undef);
        return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
    }

/*

=item C<void set_integer_native(INTVAL size)>

Resizes the array to C<size> elements.

=cut

*/

    void set_integer_native(INTVAL size) {
        int i;
        PMC **data;

        if (PMC_int_val(SELF) && size)
            real_exception(INTERP, NULL, E_IndexError,
                    "FixedPMCArray: Can't resize!");
        if (!size)
            return;
        PMC_int_val(SELF) = size;
        data = (PMC**)mem_sys_allocate(size * sizeof (PMC*));
        for (i = 0; i < size; i++)
            data[i] = PMCNULL;
        PMC_data(SELF) = data;
    }

    void set_pmc(PMC *value) {
        INTVAL size;

        if (SELF == value)
            return;

        if (value->vtable->base_type != enum_class_FixedPMCArray &&
                value->vtable->base_type != enum_class_ResizablePMCArray)
            real_exception(INTERP, NULL, E_TypeError,
                    "Can't set self from this type");

        if (PMC_data(SELF))
            mem_sys_free(PMC_data(SELF));
        size = PMC_int_val(SELF) = PMC_int_val(value);
        PMC_data(SELF) = mem_sys_allocate(size * sizeof (PMC*));
        mem_sys_memcopy(PMC_data(SELF), PMC_data(value), size*sizeof (PMC*));
        PMC_int_val2(SELF) = size;
        PObj_data_is_PMC_array_SET(SELF);
    }
/*

=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>

Sets the integer value of the element at index C<key> to C<value>.

=cut

*/

    void set_integer_keyed_int(INTVAL key, INTVAL value) {
        PMC *val;

        val = pmc_new(INTERP,
                Parrot_get_ctx_HLL_type(INTERP,
                    enum_class_Integer));
        VTABLE_set_integer_native(INTERP, val, value);
        DYNSELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_integer_keyed(PMC *key, INTVAL value)>

Sets the integer value of the element at index C<key> to C<value>.

=cut

*/

    void set_integer_keyed(PMC *key, INTVAL value) {
        PMC *val;

        val = pmc_new(INTERP,
                Parrot_get_ctx_HLL_type(INTERP,
                    enum_class_Integer));
        VTABLE_set_integer_native(INTERP, val, value);
        /* Let set_pmc_keyed worry about multi keys */
        DYNSELF.set_pmc_keyed(key, val);
    }

/*

=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>

Sets the floating-point value of the element at index C<key> to
C<value>.

=cut

*/

    void set_number_keyed_int(INTVAL key, FLOATVAL value) {
        PMC *val;

        val = pmc_new(INTERP,
                Parrot_get_ctx_HLL_type(INTERP,
                    enum_class_Float));
        VTABLE_set_number_native(INTERP, val, value);
        DYNSELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_number_keyed(PMC *key, FLOATVAL value)>

Sets the floating-point value of the element at index C<key> to
C<value>.

=cut

*/

    void set_number_keyed(PMC *key, FLOATVAL value) {
        INTVAL k;
        PMC*   nextkey;

        k = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            DYNSELF.set_number_keyed_int(k, value);
        }
        else {
            PMC* box;

            box = SELF.get_pmc_keyed_int(k);
            if (box == NULL) {
                /* TODO: autovivify an Array and insert it in SELF */
                box = pmc_new(INTERP, DYNSELF.type());
            }
            VTABLE_set_number_keyed(INTERP, box, nextkey, value);
        }
    }

/*

=item C<void set_string_keyed_int(INTVAL key, STRING *value)>

Sets the Parrot string value of the element at index C<key> to C<value>.

=cut

*/

    void set_string_keyed_int(INTVAL key, STRING* value) {
        PMC *val;

        val = pmc_new(INTERP,
                Parrot_get_ctx_HLL_type(INTERP,
                    enum_class_String));
        VTABLE_set_string_native(INTERP, val, value);
        DYNSELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_string_keyed(PMC *key, STRING* value)>

Sets the string value of the element at index C<key> to
C<value>.

=cut

*/

    void set_string_keyed(PMC *key, STRING* value) {
        PMC *val;

        val = pmc_new(INTERP,
                Parrot_get_ctx_HLL_type(INTERP,
                    enum_class_String));
        VTABLE_set_string_native(INTERP, val, value);
        /* Let set_pmc_keyed worry about multi keys */
        DYNSELF.set_pmc_keyed(key, val);
    }

/*

=item C<void set_pmc_keyed_int(INTVAL key, PMC *src)>

Sets the PMC value of the element at index C<key> to C<*src>.

=cut

*/

    void set_pmc_keyed_int(INTVAL key, PMC* src) {
        PMC **data;
        if (key < 0 || key >= PMC_int_val(SELF))
            real_exception(INTERP, NULL, E_IndexError,
                "FixedPMCArray: index out of bounds!");

        data = (PMC**)PMC_data(SELF);
        DOD_WRITE_BARRIER(INTERP, SELF, data[key], src);
        data[key] = src;
    }

/*

=item C<void set_pmc_keyed(PMC *key, PMC* value)>

Sets the PMC at index C<key> to C<value>.

=cut

*/

    void set_pmc_keyed(PMC *key, PMC* value) {
        INTVAL k;
        PMC*   nextkey;

        k = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (!nextkey) {
            DYNSELF.set_pmc_keyed_int(k, value);
        }
        else {
            PMC* box;

            box = SELF.get_pmc_keyed_int(k);
            if (box == NULL) {
                /* TODO: autovivify an Array and insert it in SELF */
                box = pmc_new(INTERP, DYNSELF.type());
            }
            VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
        }
    }

/*

=item C<INTVAL is_equal(PMC* value)>

The C<==> operation. Compares two array to hold equal elements.

=cut

*/

    INTVAL is_equal(PMC* value) {
        INTVAL j, n;

        if (value->vtable->base_type != enum_class_FixedPMCArray)
            return 0;
        n = SELF.elements();
        if (VTABLE_elements(INTERP, value) != n)
            return 0;
        for (j = 0; j < n; ++j) {
            PMC *item1, *item2;
            item1 = SELF.get_pmc_keyed_int(j);
            item2 = VTABLE_get_pmc_keyed_int(INTERP, value, j);
            if (item1 == item2)
                continue;
            if (!mmd_dispatch_i_pp(INTERP, item1, item2, MMD_EQ))
                return 0;
        }
        return 1;
    }

/*

=item C<PMC* slice(PMC *key, INTVAL f)>

Return a new iterator for the slice PMC C<key> if f == 0.

Return a new pythonic array slice if f == 1.

=item C<PMC* get_iter()>

Return a new iterator for SELF.

=cut

*/

    PMC* slice(PMC* key, INTVAL f) {
        switch (f) {
            case 0:
                {
                    PMC *iter = pmc_new_init(INTERP,
                            enum_class_Iterator, SELF);
                    PMC_struct_val(iter) = key;
                    return iter;
                }
        }
        real_exception(INTERP, NULL, E_IndexError, "Array: Unknown slice type");
        return NULL;
    }

    PMC* get_iter() {
        PMC *iter = pmc_new_init(INTERP, enum_class_Iterator, SELF);
        PMC *key =  pmc_new(INTERP, enum_class_Key);
        PMC_struct_val(iter) = key;
        PObj_get_FLAGS(key) |= KEY_integer_FLAG;
        PMC_int_val(key) = 0;
        if (PMC_int_val(SELF) == 0)
            PMC_int_val(key) = -1;
        return iter;
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL key)>

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

Returns TRUE is the element at C<key> exists; otherwise returns false.

=cut

*/
    INTVAL exists_keyed_int(INTVAL key) {
        PMC **data;
        if (key < 0 || key >= PMC_int_val(SELF))
            real_exception(INTERP, NULL, E_IndexError,
                "FixedPMCArray: index out of bounds!");

        data = (PMC**)PMC_data(SELF);
        return !PMC_IS_NULL(data[key]);
    }

    INTVAL exists_keyed(PMC* key) {
        INTVAL ix = key_integer(INTERP, key);
        return SELF.exists_keyed_int(ix);
    }

/*

=item C<void visit(visit_info *info)>

This is used by freeze/thaw to visit the contents of the array.

C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).

=item C<void freeze(visit_info *info)>

Used to archive the array.

=item C<void thaw(visit_info *info)>

Used to unarchive the array.

=cut

*/

    void visit(visit_info *info) {
        INTVAL i, n;
        PMC **pos;

        n = VTABLE_elements(INTERP, SELF);
        pos = (PMC **)PMC_data(SELF);
        for (i = 0; i < n; ++i, ++pos) {
            info->thaw_ptr = pos;
            (info->visit_pmc_now)(INTERP, *pos, info);
        }
        SUPER(info);
    }

    void freeze(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        io->vtable->push_integer(INTERP, io, VTABLE_elements(INTERP, SELF));
    }

    void thaw(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        if (info->extra_flags == EXTRA_IS_NULL)
            DYNSELF.set_integer_native(io->vtable->shift_integer(INTERP, io));
    }

/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Returns TRUE is the element at C<key> is defined; otherwise returns false.

=cut

*/

    INTVAL defined_keyed_int(INTVAL key) {
        PMC* val;

        val = DYNSELF.get_pmc_keyed_int(key);
        if (PMC_IS_NULL(val))
            return 0;

        return VTABLE_defined(INTERP, val);
    }

}

/*

=back

=head1 TODO

=head1 SEE ALSO

F<docs/pdds/pdd17_basic_types.pod>.

=head1 HISTORY

Initial version 2004.06.11 by Matt Fowles

=cut

*/

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