/* array.pmc
 *  Copyright: 2001-2003 The Perl Foundation.  All Rights Reserved.
 *  CVS Info
 *     $Id: array.pmc,v 1.65 2003/08/29 11:30:15 leo Exp $
 *  Overview:
 *     These are the vtable functions for the Array base class
 *  Data Structure and Algorithms:
 *  History:
 *  Notes:
 *     TODO: Create global immutable undef object
 *     TODO: PerlUndef should really be just Undef
 *  References:
 */

#include "parrot/parrot.h"


static PMC* undef(struct Parrot_Interp* interpreter)
{
    return pmc_new(interpreter, enum_class_PerlUndef);
}

static PMC* retval(Interp *interp, void *ret)
{
    PMC *value;
    if (ret == 0)
	internal_exception(OUT_OF_BOUNDS, "Array index out of bounds!\n");
    /* XXX getting non existant value, exception or undef?
     * current is for perlarray */
    if (ret == (void*) -1)
	value = undef(interp);
    else {
	value = *(PMC**) ret;
	if (value == NULL)	/* XXX same here */
	    value = undef(interp);
    }
    return value;
}

static PMC*
Parrot_Array_get_pmc_ptr(Interp *interp, List *list, INTVAL key)
{
    return retval(interp, list_get(interp, list, key, enum_type_PMC));
}

static PMC*
Parrot_Array_pop_pmc_ptr(Interp *interp, List *list)
{
    return retval(interp, list_pop(interp, list, enum_type_PMC));
}

static PMC*
Parrot_Array_shift_pmc_ptr(Interp *interp, List *list)
{
    return retval(interp, list_shift(interp, list, enum_type_PMC));
}



static PMC*
Parrot_Array_set_pmc_ptr(Interp *interp, List *list, INTVAL key)
{
    void * ret = list_get(interp, list, key, enum_type_PMC);
    PMC *value;

    if (ret == 0)
	internal_exception(OUT_OF_BOUNDS, "Array index out of bounds!\n");
    /* assign into a sparse or not yet set value */
    if (ret == (void*) -1 || *(PMC**)ret == 0) {
	value = undef(interp);
	list_assign(interp, list, key, value, enum_type_PMC);
    }
    else
	value = *(PMC**) ret;
    return value;
}


pmclass Array need_ext {

    void class_init() {
        /* class_init_code */
	make_bufferlike_pool(INTERP, sizeof(List_chunk));
	make_bufferlike_pool(INTERP, sizeof(List));
    }

    void init () {
        PMC_data(SELF) = list_new(INTERP, enum_type_PMC);
        PObj_custom_mark_SET(SELF);
    }

    /*
     * list_new_init uses these initializers:
     * 0 ... size (set initial size of list)
     * 1 ... array dimensions (multiarray)
     *
     */
    void init_pmc (PMC *init) {
        PMC_data(SELF) = list_new_init(INTERP, enum_type_PMC, init);
        PObj_custom_mark_SET(SELF);
    }


    void mark () {
        list_mark(INTERP, (List *) PMC_data(SELF));
    }

    void clone (PMC *dest) {
        PObj_custom_mark_SET(dest);
        PMC_data(dest) = list_clone(INTERP, (List *) PMC_data(SELF));
    }

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

    INTVAL elements () {
        return ((List *) PMC_data(SELF))->length;
    }

    FLOATVAL get_number () {
        return (FLOATVAL)(DYNSELF.elements());
    }

    STRING* get_string () {
	return Parrot_sprintf_c(INTERP, "array[%p]", SELF);
    }

    INTVAL get_integer_keyed_int (INTVAL key) {
        PMC* value;

        value = Parrot_Array_get_pmc_ptr(INTERP, (List*)PMC_data(SELF), key);
        return VTABLE_get_integer(INTERP, value);
    }

    INTVAL get_integer_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_integer_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_integer_keyed(INTERP, box, nextkey);
    }


    FLOATVAL get_number_keyed_int (INTVAL key) {
        PMC* value;

        value = Parrot_Array_get_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        return VTABLE_get_number(INTERP, value);

    }

    FLOATVAL get_number_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_number_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_number_keyed(INTERP, box, nextkey);
    }


    STRING* get_string_keyed_int (INTVAL key) {
        PMC* value;

        value = Parrot_Array_get_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        return VTABLE_get_string(INTERP, value);
    }

    STRING* get_string_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_string_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_string_keyed(INTERP, box, nextkey);
    }

    PMC* get_pmc_keyed_int (INTVAL key) {

        return Parrot_Array_get_pmc_ptr(INTERP, (List *)PMC_data(SELF), key);
    }

    PMC* get_pmc_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_pmc_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
    }

    void set_integer (PMC* value) {
        INTVAL size = VTABLE_get_integer(INTERP,value);
        list_set_length(INTERP, (List *) PMC_data(SELF) ,size);
    }

    void set_integer_native (INTVAL size) {
        list_set_length(INTERP, (List *) PMC_data(SELF) ,size);
    }

    void set_integer_same (PMC* value) {
        INTVAL size = DYNSELF.elements();
        list_set_length(INTERP, (List *) PMC_data(SELF) ,size);
    }

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

	ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_integer_native(INTERP, ptr, value);
    }

    void set_integer_keyed (PMC* key, INTVAL value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
	    DYNSELF.set_integer_keyed_int(ix, value);
	    return;
	}
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
	    /* autovivify an Array */
	    box = pmc_new(INTERP, DYNSELF.type());
	}
        VTABLE_set_integer_keyed(INTERP, box, nextkey, value);
    }

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

	ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_number_native(INTERP, ptr, value);
    }

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

        if (!key) return;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
	    SELF.set_number_keyed_int(ix, value);
	    return;
	}
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
	    /* autovivify an Array */
	    box = pmc_new(INTERP, DYNSELF.type());
	}
        VTABLE_set_number_keyed(INTERP, box, nextkey, value);
    }

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

	ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_string_native(INTERP, ptr, value);
    }

    void set_string_keyed (PMC* key, STRING* value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
	    VTABLE_set_string_keyed_int(INTERP, SELF, ix, value);
	    return;
	}
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
	    /* autovivify an Array */
	    box = pmc_new(INTERP, DYNSELF.type());
	}
        VTABLE_set_string_keyed(INTERP, box, nextkey, value);
    }

    void set_pmc_keyed_int (INTVAL idx, PMC* src) {

	INTVAL length;
        length = ((List *) PMC_data(SELF))->length;
	if (idx >= length || -idx > length) {
	    internal_exception(OUT_OF_BOUNDS, "Array index out of bounds!\n");
	}

	list_assign(INTERP, (List *) PMC_data(SELF), idx,
		(void*)src, enum_type_PMC);
    }

    void set_pmc_keyed (PMC* key, PMC* value) {
	INTVAL ix = key_integer(INTERP, key);
        PMC* nextkey;
        PMC* box;

        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
	    VTABLE_set_pmc_keyed_int(INTERP, SELF, ix, value);
	    return;
	}
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
	    /* autovivify an Array */
	    box = pmc_new(INTERP, DYNSELF.type());
	}
	VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
    }

    void push_integer (INTVAL value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_integer_keyed_int(nextix, value);
    }

    void push_float (FLOATVAL value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_number_keyed_int(nextix, value);
    }

    void push_string (STRING* value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_string_keyed_int(nextix, value);
    }

    void push_pmc (PMC* value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_pmc_keyed_int(nextix, value);
    }

    void unshift_integer (INTVAL value) {
	PMC * val = undef(INTERP);
	list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
	VTABLE_set_integer_native(INTERP, val, value);
    }

    void unshift_float (FLOATVAL value) {
	PMC * val = undef(INTERP);
	list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
	VTABLE_set_number_native(INTERP, val, value);
    }

    void unshift_string (STRING *value) {
	PMC * val = undef(INTERP);
	list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
	VTABLE_set_string_native(INTERP, val, value);
    }

    void unshift_pmc (PMC *value) {
	list_unshift(INTERP, (List*)PMC_data(SELF), value, enum_type_PMC);
    }

    INTVAL pop_integer () {
	PMC *ptr = Parrot_Array_pop_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_integer(INTERP, ptr);
    }

    FLOATVAL pop_float () {
	PMC *ptr = Parrot_Array_pop_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_number(INTERP, ptr);
    }

    STRING* pop_string () {
	PMC *ptr = Parrot_Array_pop_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_string(INTERP, ptr);
    }

    PMC* pop_pmc () {
	return Parrot_Array_pop_pmc_ptr(INTERP, (List*) PMC_data(SELF));
    }

    INTVAL shift_integer () {
	PMC *ptr = Parrot_Array_shift_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_integer(INTERP, ptr);
    }

    FLOATVAL shift_float () {
	PMC *ptr = Parrot_Array_shift_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_number(INTERP, ptr);
    }

    STRING* shift_string () {
	PMC *ptr = Parrot_Array_shift_pmc_ptr(INTERP, (List*) PMC_data(SELF));
	return VTABLE_get_string(INTERP, ptr);
    }

    PMC* shift_pmc () {
	return Parrot_Array_shift_pmc_ptr(INTERP, (List*) PMC_data(SELF));
    }

    void splice(PMC* value, INTVAL offset, INTVAL count) {
	if (SELF->vtable->base_type != value->vtable->base_type)
	    internal_exception(1, "Type mismatch in splice\n");
	list_splice(INTERP, (List*) PMC_data(SELF), value, offset, count);
    }

    INTVAL defined_keyed_int (INTVAL key) {
        PMC* value;
	void *ret;

	ret = list_get(INTERP, (List *) PMC_data(pmc), key, enum_type_PMC);
	if (ret == 0 || ret == (void *) -1)
	    return 0;
	value = *(PMC**) ret;
	if (!value)
	    return 0;
        return VTABLE_defined(INTERP, value);
    }

    INTVAL defined_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL)
	    return SELF.defined_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL)
	    return 0;
        return VTABLE_defined_keyed(INTERP, box, nextkey);
    }

    INTVAL exists_keyed_int (INTVAL key) {
        void * ret;

	ret = list_get(INTERP, (List *) PMC_data(pmc), key, enum_type_PMC);
	if (ret == 0 || ret == (void *) -1)
	    return 0;
        return *(PMC**) ret != NULL;
    }

    INTVAL exists_keyed (PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL)
	    return SELF.exists_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL)
	    return 0;
        return VTABLE_exists_keyed(INTERP, box, nextkey);
    }

    void delete_keyed_int(INTVAL key) {
	list_splice(INTERP, (List *) PMC_data(pmc), NULL, key, 1);
    }

    void delete_keyed(PMC* key) {
        INTVAL ix = key_integer(INTERP, key);
	list_splice(INTERP, (List *) PMC_data(pmc), NULL, ix, 1);
    }

    /* == operation */
    INTVAL is_equal (PMC* value) {
	/* XXX */
        return 0;
    }

    PMC* nextkey_keyed (PMC* key, INTVAL what) {
	PMC *ret = key;

	INTVAL n = SELF.elements();
	PObj_get_FLAGS(ret) &= ~KEY_type_FLAGS;
	PObj_get_FLAGS(ret) |= KEY_integer_FLAG;
	switch (what) {
	    case ITERATE_FROM_START:	/* reset key */
		ret->cache.int_val = 0;
		if (!n)
		    ret->cache.int_val = -1;
		break;
	    case ITERATE_GET_NEXT:
		if (ret->cache.int_val < n - 1)
		    ++ret->cache.int_val;
		else
		    ret->cache.int_val = -1;
		break;
	    case ITERATE_GET_PREV:
		if (ret->cache.int_val >= 0)
		    --ret->cache.int_val;
		break;
	    case ITERATE_FROM_END:
		ret->cache.int_val = n - 1;
		break;
	}
	return ret;
    }
}

