/*
 * $Id: jexc_java_util.c,v 1.7 2000/09/27 10:03:56 msato Exp $
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */

#include "jexc.h"
#include "jexc_runtime.h"
#include "jexc_sys_class.h"

#define JEXC_VECTOR_INCR_SIZE  32
#define JEXC_VECTOR_INIT_SIZE  32

/* 
 * Vector
 */
static int vector_indexof(jexc_object *q,jexc_object *p,jexc_word *sp);
static void vector_removeAt(jexc_object *p,int index);
static jexc_class *vector_enum_class = NULL;

DEFINE_C_METHOD(Utl_Vector_INIT)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    p = fp[0];
    VAR0(p) = (jexc_object *)jexc_new_array(JEXC_VECTOR_INIT_SIZE);
    INTVAR(p,Utl_Vector_SIZE) = 0;
    INTVAR(p,Utl_Vector_cap_incr) = JEXC_VECTOR_INCR_SIZE;
}

/* method size ()I				Utl_Vector_size */
DEFINE_C_METHOD(Utl_Vector_size)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    p = fp[0];
    RETURN_C_METHOD_INT(INTVAR(p,Utl_Vector_SIZE));
}

/* method isEmpty ()Z				Utl_Vector_empty */
DEFINE_C_METHOD(Utl_Vector_isEmpty)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    p = fp[0];
    RETURN_C_METHOD_INT(INTVAR(p,Utl_Vector_SIZE)==0);
}

/* method addElement (Ljava/lang/Object;)V	Utl_Vector_addElement */
DEFINE_C_METHOD(Utl_Vector_addElement)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap,*aq;
    int i,size;

    GC_OFF;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE);
    if(size >= ap->length){
	aq = jexc_new_array(size+INTVAR(p,Utl_Vector_cap_incr));
	for(i = 0; i < size; i++) aq->elem[i] = ap->elem[i];
	ap = aq;
	VAR0(p) = (jexc_object *)ap;
    }
    ap->elem[size++] = fp[1];
    INTVAR(p,Utl_Vector_SIZE) = size;
    GC_ON;
}

/* method elementAt (I)Ljava/lang/Object; 	Utl_Vector_elementAt */
DEFINE_C_METHOD(Utl_Vector_elementAt)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    RETURN_C_METHOD(ap->elem[(jexc_int)((_omAddrInt_t)fp[1])]);
}


/* method setElementAt (Ljava/lang/Object;I)V	Utl_Vector_setElementAt */
DEFINE_C_METHOD(Utl_Vector_setElementAt)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    ap->elem[(jexc_int)((_omAddrInt_t)fp[2])] = fp[1];
}

static int vector_indexof(jexc_object *p,jexc_object *q,jexc_word *sp)
{
    jexc_word *sp0;
    int i,size,r;
    jexc_array *ap;
    jexc_c_func f;

    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE);
    r = -1;
    f = q->class->methods[JEXC_EQUALS_METHOD];
    if(f == Lang_Object_equals){
	for(i = 0; i < size; i++){
	    if(q == ap->elem[i]){
		r = i;
		break;
	    }
	}
    } else {
	for(i = 0; i < size; i++){
	    sp[0] = q;
	    sp[1] = ap->elem[i];
	    sp0 = sp;
	    (*f)(&sp0);
	    if(*sp != 0){
		r = i;
		break;
	    }
	}
    }
    return r;
}

static void vector_removeAt(jexc_object *p,int index)
{
    jexc_array *ap;
    int i,size;
    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE) - 1;
    for(i = index; i < size; i++) ap->elem[i] = ap->elem[i+1];
    INTVAR(p,Utl_Vector_SIZE) = size;
}

/* method indexOf (Ljava/lang/Object;)I		Utl_Vector_indexOf */
DEFINE_C_METHOD(Utl_Vector_indexOf)
{
    DECL_C_METHOD_VAR;
    RETURN_C_METHOD_INT(vector_indexof(fp[0],fp[1],fp+2));
}

/* method removeElementAt (I)V			Utl_Vector_removeElementAt */
DEFINE_C_METHOD(Utl_Vector_removeElementAt)
{
    DECL_C_METHOD_VAR;
    vector_removeAt(fp[0],(jexc_int)((_omAddrInt_t)fp[1]));
}

/* method removeElement (Ljava/lang/Object;)Z	Utl_Vector_removeElement */
DEFINE_C_METHOD(Utl_Vector_removeElement)
{
    DECL_C_METHOD_VAR;
    int index,r;
    index = vector_indexof(fp[0],fp[1],fp+2);
    r = 0;
    if(index >= 0){
	r = 1;
	vector_removeAt(fp[0],index);
    }
    RETURN_C_METHOD_INT(r);
}

/* method contains (Ljava/lang/Object;)Z	Utl_Vector_contains */
DEFINE_C_METHOD(Utl_Vector_contains)
{
    DECL_C_METHOD_VAR;
    int index;
    index = vector_indexof(fp[0],fp[1],fp+2);
    RETURN_C_METHOD_INT(index >= 0);
}

/* method elements ()Ljava/util/Enumeration;	Utl_Vector_elements */
DEFINE_C_METHOD(Utl_Vector_elements)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    if(vector_enum_class == NULL)
	vector_enum_class = jexc_find_class("<vectorEnumeration>");
    p = jexc_new_object(vector_enum_class);
    VAR0(p) = fp[0];
    INTVAR(p,0) = 0;
    RETURN_C_METHOD(p);
}

/* method hasMoreElements ()Z		Utl_vectEnum_hasMoreElements */
DEFINE_C_METHOD(Utl_vectEnum_hasMoreElements)
{
    DECL_C_METHOD_VAR;
    jexc_object *p,*q;
    p = fp[0];
    q = VAR0(p);
    RETURN_C_METHOD_INT(INTVAR(p,0) < INTVAR(q,Utl_Vector_SIZE));
}

/* method nextElement ()Ljava/lang/Object; Utl_vectEnum_nextElement */
DEFINE_C_METHOD(Utl_vectEnum_nextElement)
{
    DECL_C_METHOD_VAR;
    jexc_object *p,*q;
    jexc_array *ap;
    int i;
    p = fp[0];
    q = VAR0(p);
    ap = (jexc_array *)VAR0(q);
    i = INTVAR(p,0);
    INTVAR(p,0) = i+1;
    RETURN_C_METHOD(ap->elem[i]);
}

/* 
 * Stack
 */

DEFINE_C_METHOD(Utl_Stack_peek)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap;
    int size;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE);
    RETURN_C_METHOD(ap->elem[size-1]);
}

DEFINE_C_METHOD(Utl_Stack_pop)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap;
    int size;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE)-1;
    INTVAR(p,Utl_Vector_SIZE) = size;
    RETURN_C_METHOD(ap->elem[size]);
}

DEFINE_C_METHOD(Utl_Stack_push)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *ap,*aq;
    int i,size;

    GC_OFF;
    p = fp[0];
    ap = (jexc_array *)VAR0(p);
    size = INTVAR(p,Utl_Vector_SIZE);
    if(size >= ap->length){
	aq = jexc_new_array(size+INTVAR(p,Utl_Vector_cap_incr));
	for(i = 0; i < size; i++) aq->elem[i] = ap->elem[i];
	ap = aq;
	VAR0(p) = (jexc_object *)ap;
    }
    ap->elem[size++] = fp[1];
    INTVAR(p,Utl_Vector_SIZE) = size;
    GC_ON;
    RETURN_C_METHOD(fp[1]);
}

/*
 * Hash table
 */
#define JEXC_HASH_TABLE_SIZE 512
#define JEXC_HASH_TABLE_LOAD_FACTOR 0.5

static int hashtable_index(jexc_word key,jexc_array *keys);

/* method <init> ()V				Utl_Hashtable_INIT*/
DEFINE_C_METHOD(Utl_Hashtable_INIT)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;

    GC_OFF;
    p = fp[0];
    VAR(p,Utl_Hashtable_keys) = 
	(jexc_object *)jexc_new_array(JEXC_HASH_TABLE_SIZE);
    VAR(p,Utl_Hashtable_vals) = 
	(jexc_object *)jexc_new_array(JEXC_HASH_TABLE_SIZE);
    INTVAR(p,Utl_Hashtable_num_keys) = 0;
    IVAR(jexc_float,p,Utl_Hashtable_load_factor) = JEXC_HASH_TABLE_LOAD_FACTOR;
    GC_ON;
}

/* method <init> (IF)V				Utl_Hashtable_INIT1 */
DEFINE_C_METHOD(Utl_Hashtable_INIT1)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    int i,size;

    GC_OFF;
    p = fp[0];
    size = (jexc_int)((_omAddrInt_t)fp[1]);
    for(i = 1; i < size; i = i << 1) /* */;
    VAR(p,Utl_Hashtable_keys) = (jexc_object *)jexc_new_array(i);
    VAR(p,Utl_Hashtable_vals) = (jexc_object *)jexc_new_array(i);
    INTVAR(p,Utl_Hashtable_num_keys) = 0;
    IVAR(jexc_float,p,Utl_Hashtable_load_factor) = *((jexc_float *)(fp+2));
    GC_ON;
}

/* method get (Ljava/lang/Object;)Ljava/lang/Object; Utl_Hashtable_get */
DEFINE_C_METHOD(Utl_Hashtable_get)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *keys,*vals;
    int idx;
    jexc_word r;

    p = fp[0];
    keys = (jexc_array *)VAR(p,Utl_Hashtable_keys);
    vals = (jexc_array *)VAR(p,Utl_Hashtable_vals);
    idx = hashtable_index(fp[1],keys);
    if(keys->elem[idx] == NULL) r = NULL;
    else r = vals->elem[idx];
    RETURN_C_METHOD(r);
}


/*  method put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; Utl_Hashtable_put */
DEFINE_C_METHOD(Utl_Hashtable_put)
{
    DECL_C_METHOD_VAR;
    jexc_object *p;
    jexc_array *keys,*vals,*keys0,*vals0;
    int num_keys;
    float load_factor;
    int i,size,idx;
    jexc_word r;

    GC_OFF;
    p = fp[0];
    keys = (jexc_array *)VAR(p,Utl_Hashtable_keys);
    vals = (jexc_array *)VAR(p,Utl_Hashtable_vals);
    num_keys = INTVAR(p,Utl_Hashtable_num_keys)+1;
    load_factor = IVAR(jexc_float,p,Utl_Hashtable_load_factor);
    size = keys->length;
    if(((float)num_keys) > ((float)size)*load_factor){
	/* rehash */
	keys0 = jexc_new_array(size*2);
	vals0 = jexc_new_array(size*2);
	for(i = 0; i < size; i++){
	    if(keys->elem[i] == NULL) continue;
	    idx = hashtable_index(keys->elem[i],keys0);
	    keys0->elem[idx] = keys->elem[i];
	    vals0->elem[idx] = vals->elem[i];
	}
	keys = keys0;
	vals = vals0;
	VAR(p,Utl_Hashtable_keys) = (jexc_object *)keys;
	VAR(p,Utl_Hashtable_vals) = (jexc_object *)vals;
    }
    INTVAR(p,Utl_Hashtable_num_keys) = num_keys;
    idx = hashtable_index(fp[1],keys);
    if(keys->elem[idx] == NULL) r = NULL;
    else r = vals->elem[idx];
    keys->elem[idx] = fp[1];
    vals->elem[idx] = fp[2];
    GC_ON;
    RETURN_C_METHOD(r);
}

static int hashtable_index(jexc_word key,jexc_array *keys)
{
    _omAddrInt_t hcode;
    int h;
    int i,size;
    jexc_object *p;

    if(key->class == jexc_integer_class)
	hcode = (_omAddrInt_t)(*((jexc_int *)key->i_var_base));
    else 
	hcode = (_omAddrInt_t)key;

    size = keys->length;
    h = (hcode >> 8)&(size-1);

    if(key->class == jexc_integer_class){
	for(i = 0; i < size; i++){
	    p = keys->elem[h];
	    if(p == NULL ||
	       (p->class == jexc_integer_class &&
		*((jexc_int *)(p->i_var_base)) == hcode)) break;
	    h++;
	    if(h >= size) h = 0;
	}
    } else {
	for(i = 0; i < size; i++){
	    p = keys->elem[h];
	    if(p == NULL || (_omAddrInt_t)p == hcode) break;
	    h++;
	    if(h >= size) h = 0;
	}
    }
    if(i == size) return -1;
    else return h;
}

