/*
 * Guarana.c
 * Implementation of native methods in class BR.unicamp.Guarana.Guarana
 *
 * Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 *
 * This file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "guarana/GuaraNative.h"

struct HBR_unicamp_Guarana_MetaObject *BR_unicamp_Guarana_Guarana_getMetaObjectOf(struct Hjava_lang_Object *o) {
  return o->meta_object;
}

void BR_unicamp_Guarana_Guarana_setMetaObjectOf(struct Hjava_lang_Object *obj, struct HBR_unicamp_Guarana_MetaObject *prev, struct HBR_unicamp_Guarana_MetaObject *newm) {
  if (obj->meta_object != prev)
    return;
  if (newm) {
    do_execute_java_method((jobject)newm, "initialize", "(LBR/unicamp/Guarana/OperationFactory;Ljava/lang/Object;)V", 0, 0, guarana_new_operation_factory(obj, newm), obj);
    SOFT_ADDREFERENCE(obj, newmeta);
  }
  if (obj->meta_object != prev) {
    if (newm)
      do_execute_java_method((jobject)newm, "release", "(Ljava/lang/Object;)V", 0, 0, obj);
    return;
  }
  obj->meta_object = newm;
  if (prev)
    do_execute_java_method((jobject)prev, "release", "(Ljava/lang/Object;)V", 0, 0, obj);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Guarana_deliver(struct HBR_unicamp_Guarana_Operation *op) {
  NativeOperation *nop = (NativeOperation*)op;
  jvalue wrap;
  assert (!(nop->kind & op_wrapped));
  switch (nop->kind & op_kind_mask) {
  case op_nop:
    break;

  case op_method_invocation:
  case op_constructor_invocation: {
    jvalue *args = (jvalue*)nop->op_arg.l;
    callMethodA(nop->op_id.method, METHOD_INDIRECTMETHOD(nop->op_id.method), nop->object, args, &wrap);
    break;
  }

  case op_monitor_enter:
    _lockMutex(nop->object);
    break;

  case op_monitor_exit:
    _unlockMutex(nop->object);
    break;

  case op_field_read: {
    void *addr;
    if (nop->op_id.field_id.field->accflags & ACC_STATIC)
      addr = FIELD_ADDRESS(nop->op_id.field_id.field);
    else
      addr = ((char *)nop->object) + FIELD_OFFSET(nop->op_id.field_id.field);
    if (FIELD_ISREF(nop->op_id.field_id.field)) {
	wrap.l = *(jref*)addr;
	break;
    } else switch(CLASS_PRIM_SIG(FIELD_TYPE(nop->op_id.field_id.field))) {
    case 'Z':
	wrap.i = *(jboolean*)addr;
	break;
    case 'B':
	wrap.i = *(jbyte*)addr;
	break;
    case 'S':
	wrap.i = *(jshort*)addr;
	break;
    case 'C':
	wrap.i = *(jchar*)addr;
	break;
    case 'I':
	wrap.i = *(jint*)addr;
	break;
    case 'J':
	wrap.j = *(jlong*)addr;
	break;
    case 'F':
	wrap.f = *(jfloat*)addr;
	break;
    case 'D':
	wrap.d = *(jdouble*)addr;
	break;
    default:
	ABORT();
    }
    break;
  }

  case op_field_write: {
    void *addr;
    if (nop->op_id.field_id.field->accflags & ACC_STATIC) {
      addr = FIELD_ADDRESS(nop->op_id.field_id.field);
      if (FIELD_ISREF(nop->op_id.field_id.field)) {
	SOFT_ADDREFERENCE_STATIC(addr, nop->op_arg.l);
      }
    } else {
      addr = ((char *)nop->object) + FIELD_OFFSET(nop->op_id.field_id.field);
      if (FIELD_ISREF(nop->op_id.field_id.field)) {
	SOFT_ADDREFERENCE(nop->object, nop->op_arg.l);
      }
    }
    if (FIELD_ISREF(nop->op_id.field_id.field)) {
	*(jref*)addr = nop->op_arg.l;
	break;
    } else switch(CLASS_PRIM_SIG(FIELD_TYPE(nop->op_id.field_id.field))) {
    case 'Z':
	*(jboolean*)addr = nop->op_arg.i;
	break;
    case 'B':
	*(jbyte*)addr = nop->op_arg.i;
	break;
    case 'S':
	*(jshort*)addr = nop->op_arg.i;
	break;
    case 'C':
	*(jchar*)addr = nop->op_arg.i;
	break;
    case 'I':
	*(jint*)addr = nop->op_arg.i;
	break;
    case 'J':
	*(jlong*)addr = nop->op_arg.j;
	break;
    case 'F':
	*(jfloat*)addr = nop->op_arg.f;
	break;
    case 'D':
	*(jdouble*)addr = nop->op_arg.d;
	break;
    default:
	ABORT();
    }
    break;
  }

  case op_array_read: {
    void *addr;
    int size;
    size = TYPE_SIZE(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)));
    addr = (char *)(((struct Array *)nop->object)->align)
      + nop->op_id.index * size;
    if (!CLASS_IS_PRIMITIVE(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)))) {
	wrap.l = *(jref*)addr;
	break;
    } else switch(CLASS_PRIM_SIG(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)))) {
    case 'Z':
	wrap.i = *(jboolean*)addr;
	break;
    case 'B':
	wrap.i = *(jbyte*)addr;
	break;
    case 'S':
	wrap.i = *(jshort*)addr;
	break;
    case 'C':
	wrap.i = *(jchar*)addr;
	break;
    case 'I':
	wrap.i = *(jint*)addr;
	break;
    case 'J':
	wrap.j = *(jlong*)addr;
	break;
    case 'F':
	wrap.f = *(jfloat*)addr;
	break;
    case 'D':
	wrap.d = *(jdouble*)addr;
	break;
    default:
	ABORT();
    }
    break;
  }

  case op_array_write: {
    void *addr;
    int size;
    size = TYPE_SIZE(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)));
    addr = (char *)(((struct Array *)nop->object)->align)
      + nop->op_id.index * size;
    if (!CLASS_IS_PRIMITIVE(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)))) {
	SOFT_ADDREFERENCE(nop->object, nop->op_arg.l);
	*(jref*)addr = nop->op_arg.l;
	break;
    } else switch(CLASS_PRIM_SIG(CLASS_ELEMENT_TYPE(OBJECT_CLASS(nop->object)))) {
    case 'Z':
	*(jboolean*)addr = nop->op_arg.i;
	break;
    case 'B':
	*(jbyte*)addr = nop->op_arg.i;
	break;
    case 'S':
	*(jshort*)addr = nop->op_arg.i;
	break;
    case 'C':
	*(jchar*)addr = nop->op_arg.i;
	break;
    case 'I':
	*(jint*)addr = nop->op_arg.i;
	break;
    case 'J':
	*(jlong*)addr = nop->op_arg.j;
	break;
    case 'F':
	*(jfloat*)addr = nop->op_arg.f;
	break;
    case 'D':
	*(jdouble*)addr = nop->op_arg.d;
	break;
    default:
	ABORT();
    }
    break;
  }

  case op_array_length:
    wrap.i = obj_length((struct Array*)nop->object);
    break;

  default:
    unimp("guarana_deliver_operation: unknown operation type");
    return 0;
  }

  return guarana_new_result_object(res_returned, op, wrap);
}

void BR_unicamp_Guarana_Guarana_markAsPerformed(struct HBR_unicamp_Guarana_Operation* op) {
    NativeOperation *nop = (NativeOperation*)op;
    if (nop->kind & op_performed)
	SignalError("java/lang/IllegalArgumentException", "already performed");
    nop->kind |= op_performed;
}

struct Hjava_lang_Object* BR_unicamp_Guarana_Guarana_makeProxy(struct Hjava_lang_Class *clazz, struct HBR_unicamp_Guarana_MetaObject *mobj) {
  static Hjava_lang_Class *guarana = 0;
  static Method *broadcast = 0, *reconfigure = 0;
  static Hjava_lang_Class *newProxy = 0;
  struct Hjava_lang_Object *obj;
  jref msg;
  if (CLASS_IS_PRIMITIVE(clazz) ||
      strcmp(CLASS_CNAME(clazz), "BR/unicamp/Guarana/Guarana") == 0 ||
      strcmp(CLASS_CNAME(clazz), "BR/unicamp/Guarana/Operation") == 0 ||
      strcmp(CLASS_CNAME(clazz), "BR/unicamp/Guarana/Result") == 0)
      return 0;
  obj = newObjectNoMessage(clazz);
  if (!newProxy)
      newProxy = lookupClass("BR/unicamp/Guarana/NewProxy");
  msg = execute_java_constructor(0, newProxy, "(Ljava/lang/Object;)V", obj);
  if (!guarana)
      guarana = lookupClass("BR/unicamp/Guarana/Guarana");
  if (!broadcast)
      broadcast = lookupClassMethod(guarana, "broadcast", "(LBR/unicamp/Guarana/Message;Ljava/lang/Object;)V");
  do_execute_java_method(0, 0, 0, broadcast, 1, msg, clazz);
  if (!reconfigure)
      reconfigure = lookupClassMethod(guarana, "reconfigure", "(Ljava/lang/Object;LBR/unicamp/Guarana/MetaObject;LBR/unicamp/Guarana/MetaObject;)V");
  if (mobj)
      do_execute_java_method(0, 0, 0, reconfigure, 1, obj, (jobject)0, mobj);
  return obj;
}

jint BR_unicamp_Guarana_Guarana_hashCode(struct Hjava_lang_Object *obj) {
  return (jint)obj;
}

struct Hjava_lang_Class* BR_unicamp_Guarana_Guarana_getClass(struct Hjava_lang_Object *obj) {
  return OBJECT_CLASS(obj);
}

struct Hjava_lang_String* BR_unicamp_Guarana_Guarana_getClassName(struct Hjava_lang_Class *clazz) {
  return p_getClassName(clazz);
}
