/****************************************************************************
 * KONOHA COPYRIGHT, LICENSE NOTICE, AND DISCRIMER  
 * 
 * Copyright (c) 2005-2008, Kimio Kuramitsu <kimio at ynu.ac.jp>
 *           (c) 2008-      Konoha Software Foundation  
 * All rights reserved.
 * 
 * You may choose one of the following two licenses when you use konoha. 
 * See www.konohaware.org/license.html for further information.
 * 
 * (1) GNU General Public License 2.0      (with    KONOHA_UNDER_GPL2)
 * (2) Konoha Software Foundation License 1.0
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  
 ****************************************************************************/

/* ************************************************************************ */

#include"commons.h"

/* ************************************************************************ */

#ifdef __cplusplus 
extern "C" {
#endif

/* ======================================================================== */
/* [constructor] */

/* ------------------------------------------------------------------------ */
/* @method This! Array.new(Int init) */

METHOD knh__Array_new(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	size_t init = IS_NULL(sfp[1].o) ? : (sfp[1].i)->value;
	if(init > 0) {
		o->list = knh_oarray_malloc(ctx, init, KNH_NULL);
		o->size = 0;
	}
	METHOD_RETURN(ctx, sfp, o);
}

/* ------------------------------------------------------------------------ */
/* @method This! Array.new:array(Int init, Any1 value) */

METHOD knh__Array_new__array(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	size_t init = IS_NULL(sfp[1].o) ? : (sfp[1].i)->value;
	if(init > 0) {
		o->list = knh_oarray_malloc(ctx, init, sfp[2].o);
		o->size = init;
	}
	METHOD_RETURN(ctx, sfp, o);
}

/* ------------------------------------------------------------------------ */
/* @method[VARARGS] This! Array.new:init(Any1 value) @VARARGS */

METHOD knh__Array_new__init(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	knh_sfp_t *v = sfp + 1;
	knh_vargc_t ac = knh_sfp_argc(ctx, v);
	int i;
	for(i = 0; i < ac; i++) {
		knh_Array_add(ctx, o, v[i].o);
	}
	METHOD_RETURN(ctx, sfp, o);
}

/* ------------------------------------------------------------------------ */

/* ======================================================================== */
/* [method] */

/* ------------------------------------------------------------------------ */
/* @method void Array.add(Any1 value) */

METHOD knh__Array_add(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		if(o->size == knh_oarray_capacity(o->list)) {
			o->list = knh_oarray_grow(ctx, o->list);
		}
		KNH_ASSERT(o->size < knh_oarray_capacity(o->list));
		KNH_SETv(ctx, o->list[o->size], sfp[1].o);
		o->size++;
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method[VARARGS] void Array.opLShift(Any1 v) */

METHOD knh__Array_opLShift(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		knh_sfp_t *v = sfp + 1;
		knh_vargc_t ac = knh_sfp_argc(ctx, v);
		int i;
		for(i = 0; i < ac; i++) {
			knh_Array_add(ctx, o, v[i].o);
		}
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method Int! Array.getSize() */

METHOD knh__Array_getSize(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	METHOD_RETURN(ctx, sfp, new_Int(ctx, o->size));
}

/* ------------------------------------------------------------------------ */
/* @method Any1 Array.get(Int! index) */

METHOD knh__Array_get(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	size_t n2 = knh_array_index(ctx, (sfp[1].i)->value, o->size);
	METHOD_RETURN(ctx, sfp, o->list[n2]);
}

/* ------------------------------------------------------------------------ */
/* @method void Array.set(Int! index, Any1 v) */

METHOD knh__Array_set(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		size_t n2 = knh_array_index(ctx, (sfp[1].i)->value, o->size);
		KNH_SETv(ctx, o->list[n2], sfp[2].o);
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method Any1 Array.pop() */

METHOD knh__Array_pop(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o) && o->size > 0) {
		o->size--;
		METHOD_RETURN(ctx, sfp, o->list[o->size]);
	}else {
		METHOD_RETURN(ctx,sfp, KNH_NULL);
	}
}

/* ------------------------------------------------------------------------ */
/* @method void Array.insert(Int! n, Any1 v) */

METHOD knh__Array_insert(Ctx *ctx, knh_sfp_t *sfp) 
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		TODO_THROW(ctx);
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method void Array.remove(Int! n) */

METHOD knh__Array_remove(Ctx *ctx, knh_sfp_t *sfp) 
{
	Array *o = (Array*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		TODO_THROW(ctx);
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method void Array.clear() */

void knh_Array_clear(Ctx *ctx, Array *o) 
{
	if(!knh_Object_isImmutable(o)) {
		size_t i;
		for(i = 0; i < o->size; i++) {
			KNH_SETv(ctx, o->list[i], KNH_NULL);
		}
		o->size = 0;
	}
}

/* ------------------------------------------------------------------------ */
/* @method This! Array.opSubset(Int s, Int e) */

METHOD knh__Array_set__opSubset(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(o->size > 0) {
		size_t s = IS_NULL(sfp[1].o) ? 0 : knh_array_index(ctx, (sfp[1].i)->value, o->size);
		size_t e = IS_NULL(sfp[2].o) ? (o->size) : knh_array_index(ctx, (sfp[2].i)->value, o->size);
		Array *newo = (Array*)new_Object__init(ctx, o->h.flag, o->h.cid);
		newo->list = knh_oarray_copy(ctx, o->list, s, e - s);
		newo->size = e - s;
		METHOD_RETURN(ctx, sfp, newo);
	}
	else {
		METHOD_RETURN(ctx, sfp, new_Object__init(ctx, o->h.flag, o->h.cid));
	}
}

/* ------------------------------------------------------------------------ */
/* @method This! Array.opSlice(Int s, Int e) */

METHOD knh__Array_set__opSlice(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(o->size > 0) {
		size_t s = IS_NULL(sfp[1].o) ? 0 : knh_array_index(ctx, (sfp[1].i)->value, o->size);
		size_t e = IS_NULL(sfp[2].o) ? (o->size) : knh_array_index(ctx, (sfp[2].i)->value, o->size) + 1;
		Array *newo = (Array*)new_Object__init(ctx, o->h.flag, o->h.cid);
		newo->list = knh_oarray_copy(ctx, o->list, s, e - s);
		newo->size = e - s;
		METHOD_RETURN(ctx, sfp, newo);
	}
	else {
		METHOD_RETURN(ctx, sfp, new_Object__init(ctx, o->h.flag, o->h.cid));
	}
}

/* ------------------------------------------------------------------------ */
/* @method This! Array.opOffset(Int s, Int offset) */

METHOD knh__Array_set__opOffset(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	if(o->size > 0) {
		size_t s = IS_NULL(sfp[1].o) ? 0 : knh_array_index(ctx, (sfp[1].i)->value, o->size);
		knh_int_t offset = IS_NULL(sfp[2].o) ? 0 : (sfp[2].i)->value;
		Array *newo = (Array*)new_Object__init(ctx, o->h.flag, o->h.cid);
		if(offset == 0) {
			
		}
		else if(offset > 0){
			newo->list = knh_oarray_copy(ctx, o->list, s, offset);
			newo->size = offset;
		}
		else {
			knh_int_t ss = s + offset;
			if(ss < 0) ss = 0;
			newo->list = knh_oarray_copy(ctx, o->list, ss, s - ss);
			newo->size = s - ss;
		}
		METHOD_RETURN(ctx, sfp, newo);
	}
	else {
		METHOD_RETURN(ctx, sfp, new_Object__init(ctx, o->h.flag, o->h.cid));
	}
}

/* ======================================================================== */
/* [movabletext] */

INLINE
knh_bool_t knh_array_isdump(size_t c)
{
	return (c < 100);
}

/* ------------------------------------------------------------------------ */
/* @method void Array.%k(OutputStream w, Any m) */

void knh_Array__k(Ctx *ctx, Array *o, OutputStream *w, Any *m)
{
	knh_putc(ctx, w, '[');
	size_t c;
	for(c = 0; c < o->size; c++) {
		if(c > 0) {
			knh_write_delim(ctx,w);
		}
		if(!knh_array_isdump(c)) {
			knh_write_dots(ctx, w);
			break;
		}
		knh_format(ctx, w, METHODN__k, o->list[c], KNH_NULL);
	}
	knh_putc(ctx, w, ']');
}

/* ======================================================================== */
/* [mapping] */

static 
Object* knh_Array_var_next(Ctx *ctx, Iterator *it)
{
	Array *o = (Array*)knh_Iterator_source(it);
	KNH_ASSERT(IS_bArray(o));
	size_t pos = knh_Iterator_pos(it);
	while(pos < o->size) {
		if(IS_NOTNULL(o->list[pos])) {
			knh_Iterator_setpos(it, pos+1);
			return o->list[pos]; 
		}
	}
	return KNH_VOID;
}

/* ------------------------------------------------------------------------ */
/* @map Array Iterator! */

Iterator* knh_Array_Iterator(Ctx *ctx, Array *o, Mapper *mpr)
{
	return new_Iterator(ctx, knh_tClass[o->h.cid].p1, o, knh_Array_var_next);
}

/* ------------------------------------------------------------------------ */
/* @method Any1.. Array.opItr() */

METHOD knh__Array_opItr(Ctx *ctx, knh_sfp_t *sfp)
{
	Array *o = (Array*)sfp[0].o;
	METHOD_RETURN(ctx, sfp, new_Iterator(ctx, knh_tClass[o->h.cid].p1, o, knh_Array_var_next));
}

/* ------------------------------------------------------------------------ */
/* @map Iterator Array! */

Array* knh_Iterator_Array(Ctx *ctx, Iterator *o, Mapper *mpr)
{
	Array *a = new_Array(ctx, knh_tClass[o->h.cid].p1, 0);
	Object *v = o->fnext_1(ctx, o);
	while(IS_NOTNULL(v)) {
		knh_Array_add(ctx, a, v);
		v = o->fnext_1(ctx, o);
	}
	return a;
}

/* ------------------------------------------------------------------------ */

Mapper *knh_tMapper_newIteratorArray(Ctx *ctx, knh_class_t icid, knh_class_t acid)
{
	Mapper *mpr = new_Mapper(ctx, KNH_FLAG_MMF_TOTAL, acid, icid, (f_mapper)knh_Array_Iterator, KNH_NULL);
	knh_ClassMap_add(ctx, knh_tClass[acid].cmap, mpr);
	mpr = new_Mapper(ctx, KNH_FLAG_MMF_TOTAL, icid, acid, (f_mapper)knh_Iterator_Array, KNH_NULL);
	knh_ClassMap_add(ctx, knh_tClass[icid].cmap, mpr);
	return mpr;
}

/* ------------------------------------------------------------------------ */

Mapper *knh_tMapper_newArrayIterator(Ctx *ctx, knh_class_t acid, knh_class_t icid)
{
	Mapper *mpr = new_Mapper(ctx, KNH_FLAG_MMF_TOTAL, icid, acid, (f_mapper)knh_Iterator_Array, KNH_NULL);
	knh_ClassMap_add(ctx, knh_tClass[icid].cmap, mpr);
	mpr = new_Mapper(ctx, KNH_FLAG_MMF_TOTAL, acid, icid, (f_mapper)knh_Array_Iterator, KNH_NULL);
	knh_ClassMap_add(ctx, knh_tClass[acid].cmap, mpr);
	return mpr;
}

/* ------------------------------------------------------------------------ */

#ifdef __cplusplus
}
#endif
