/****************************************************************************
 * 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! HashMap.new(Int init) */

METHOD knh__HashMap_new(Ctx *ctx, knh_sfp_t *sfp)
{
	HashMap *o = (HashMap*)sfp[0].o;
	int init = IS_NULL(sfp[1].o) ? KNH_HASHMAP_INITSIZE: (sfp[1].i)->value;
	if(init > 0) {
		knh_HashMap_struct_init(ctx, DP(o), init, NULL);
	}
	METHOD_RETURN(ctx, sfp, o);
}
/* ------------------------------------------------------------------------ */
/* ======================================================================== */
/* [method] */

/* ------------------------------------------------------------------------ */
/* @method void HashMap.get(Any1! key) */

METHOD knh__HashMap_get(Ctx *ctx, knh_sfp_t *sfp)
{
	HashMap *o = (HashMap*)sfp[0].o;
	Object *key = sfp[1].o;

	knh_hcode_t hcode = knh_Object_hashCode(key);
	knh_uint_t h =  hcode % DP(o)->capacity;
	knh_hashmapentry_t *cur = DP(o)->array[h];

	while(cur != NULL) {
		if(cur->hcode == hcode) {
			if(DP(o)->fcompareTo(cur->key, key) == 0 || IS_NULL(cur->key)) {
				METHOD_RETURN(ctx, sfp, cur->value);
			}
		}
		cur = cur->next;
	}
	METHOD_RETURN(ctx, sfp, KNH_NULL);
}

/* ------------------------------------------------------------------------ */
/* @method Boolean! HashMap.opIn(Any1! key) */

METHOD knh__HashMap_opIn(Ctx *ctx, knh_sfp_t *sfp)
{
	knh__HashMap_get(ctx, sfp);
	if(IS_NULL(sfp[-1].o)) {
		KNH_SETv(ctx, sfp[-1].o, KNH_FALSE);
	}
	else {
		KNH_SETv(ctx, sfp[-1].o, KNH_TRUE);
	}
}

/* ------------------------------------------------------------------------ */
/* @method void HashMap.remove(Any1! key) */

METHOD knh__HashMap_remove(Ctx *ctx, knh_sfp_t *sfp)
{
	HashMap *o = (HashMap*)sfp[0].o;
	if(!knh_Object_isImmutable(o)) {
		Object *key = sfp[1].o;

		knh_hcode_t hcode = knh_Object_hashCode(key);
		knh_uint_t h =  hcode % DP(o)->capacity;
		knh_hashmapentry_t *cur = DP(o)->array[h];
		knh_hashmapentry_t **prev_next = &(DP(o)->array[h]);

		while(cur != NULL) {
			if(cur->hcode == hcode) {
				if(DP(o)->fcompareTo(cur->key, key) == 0 || IS_NULL(cur->key)) {
					prev_next[0] = cur->next;
					cur->next = DP(o)->unused;
					DP(o)->unused = cur;
					cur->hcode = 0;
					cur->key = NULL;
					cur->value = NULL;
					DP(o)->size--;
					break;
				}
			}
			prev_next = &(cur->next);
			cur = cur->next;
		}
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

/* ------------------------------------------------------------------------ */
/* @method void HashMap.set(Any1! key, Any2 value) */

METHOD knh__HashMap_set(Ctx *ctx, knh_sfp_t *sfp)
{
	if(IS_NULL(sfp[2].o)) {
		knh__HashMap_remove(ctx, sfp);
	}else if(!knh_Object_isImmutable(sfp[0].o)) {
		HashMap *o = (HashMap*)sfp[0].o;
		Object *key = sfp[1].o;
		Object *value  = sfp[2].o;

		knh_hcode_t hcode = knh_Object_hashCode(key);
		knh_uint_t h =  hcode % DP(o)->capacity;
		knh_hashmapentry_t *cur = DP(o)->array[h];
		while(cur != NULL) {
			if(cur->hcode == hcode) {
				if(DP(o)->fcompareTo(cur->key, key) == 0 || IS_NULL(cur->key)) {
					KNH_SETv(ctx, cur->value, value);
					METHOD_RETURN(ctx, sfp, KNH_VOID);
				}
			}
			cur = cur->next;
		}
		cur = new_hashmapentry(ctx, o);
		cur->hcode = hcode;
		KNH_INITv(cur->key, key);
		KNH_INITv(cur->value, value);
		cur->next = DP(o)->array[h];
		DP(o)->array[h] = cur;
		DP(o)->size++;
	}
	METHOD_RETURN(ctx, sfp, KNH_VOID);
}

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

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

void knh_HashMap__k(Ctx *ctx, HashMap *o, OutputStream *w, Any *m)
{
	knh_putc(ctx, w, '{');
	size_t pos = 0, capacity = knh_hashmapentry_capacity(ctx, o), c = 0;
	while(pos < capacity) {
		knh_hashmapentry_t *e = knh_hashmapentry_at(o, pos);
		if(e != NULL) {
			if(c > 0) {
				knh_write_delim(ctx,w);
			}
			knh_format(ctx, w, METHODN__k, e->key, KNH_NULL);
			knh_putc(ctx, w, ':');
			knh_putc(ctx, w, ' ');
			knh_format(ctx, w, METHODN__k, e->value, KNH_NULL);
			c++;
		}
		pos++;
	}
	knh_putc(ctx, w, '}');
}

/* ------------------------------------------------------------------------ */
/* @method void HashMap.%dump(OutputStream w, Any m) */

void knh_HashMap__dump(Ctx *ctx, HashMap *o, OutputStream *w, Any *m)
{
	knh_HashMap__k(ctx, o, w, m);
}

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

static
Object* knh_HashMap_var_next(Ctx *ctx, Iterator *it)
{
	HashMap *o = (HashMap*)knh_Iterator_source(it);
	KNH_ASSERT(IS_bHashMap(o));
	size_t pos = knh_Iterator_pos(it), capacity = knh_hashmapentry_capacity(ctx, o);
	while(pos < capacity) {
		knh_hashmapentry_t *e = knh_hashmapentry_at(o, pos);
		if(e != NULL) {
			knh_Iterator_setpos(it, pos+1);
			return e->key;
		}
		pos++;
	}
	return KNH_VOID;
}

/* ------------------------------------------------------------------------ */
/* @map HashMap Iterator! */

Iterator* knh_HashMap_Iterator(Ctx *ctx, HashMap *o, Mapper *mpr)
{
	return new_Iterator(ctx, knh_tClass[o->h.cid].p1, UP(o), knh_HashMap_var_next);
}

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

METHOD knh__HashMap_opItr(Ctx *ctx, knh_sfp_t *sfp)
{
	HashMap *o = (HashMap*)sfp[0].o;
	METHOD_RETURN(ctx, sfp, new_Iterator(ctx, knh_tClass[o->h.cid].p1, UP(o), knh_HashMap_var_next));
}

///* ------------------------------------------------------------------------ */
//
//static
//Object* knh_HashMap_tuple2_next(Ctx *ctx, Iterator *it)
//{
//	HashMap *o = (HashMap*)knh_Iterator_source(it);
//	KNH_ASSERT(IS_bHashMap(o));
//	size_t pos = knh_Iterator_pos(it), capacity = knh_hashmapentry_capacity(ctx, o);
//	while(pos < capacity) {
//		knh_hashmapentry_t *e = knh_hashmapentry_at(o, pos);
//		if(e != NULL) {
//			knh_Iterator_setpos(it, pos+1);
//			return new_Tuple2(ctx, e->key, e->value);
//		}
//		pos++;
//	}
//	return KNH_VOID;
//}
//
///* ------------------------------------------------------------------------ */
///* @map HashMap Tuple2..! */
//
//Iterator* knh_HashMap_Tuple2__(Ctx *ctx, HashMap *o, Mapper *mpr)
//{
//	return new_Iterator(ctx, CLASS_Tuple2, o, knh_HashMap_tuple2_next);
//}
//
///* ------------------------------------------------------------------------ */

#ifdef __cplusplus
}
#endif
