/*
 * Guarana.c
 * Implementation of native methods in class BR.unicamp.Guarana.Guarana
 *
 * Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 */

/* guarana.unicamp.br/APIguarana/lib */

#include "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(0, (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)
    return;
  obj->meta_object = newm;
  if (prev)
    do_execute_java_method(0, (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;
  union wrapValue wrap;
  assert (!(nop->kind & op_wrapped));
  switch (nop->kind & op_kind_mask) {
  case op_method_invocation:
  case op_constructor_invocation: {
    va_list args;
    char *sig;
    int argcount;
    jword result;
    args = ARGLIST_TO_VA_LIST(METHOD_SIGNATURE(nop->op_id.method),
			      nop->op_arg.args);
    sig = nop->op_id.method->signature->data;
    argcount = sizeofSig(&sig, false);
    switch(*sig) {
    case 'Z':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.boolean_value = result;
      break;

    case 'B':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.byte_value = result;
      break;

    case 'S':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.short_value = result;
      break;

    case 'I':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.int_value = result;
      break;

    case 'J':
      CALL_KAFFE_LMETHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, wrap.long_value);
      break;
      
    case 'C':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.char_value = result;
      break;

    case 'F':
      CALL_KAFFE_FMETHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, wrap.float_value);
      break;
      
    case 'D':
      CALL_KAFFE_DMETHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, wrap.double_value);
      break;
      
    case 'V':
    case 'L':
    case '[':
      CALL_KAFFE_METHOD_VARARGS(nop->op_id.method, nop->object, argcount, args, result);
      wrap.object_value = (jobject)result;
      break;

    default:
      unimp("Guarana.deliver: unknown return type");
      return 0;
    }
    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);
    memcpy(&wrap, addr, FIELD_SIZE(nop->op_id.field_id.field));
    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.object_value);
      }
    } 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.object_value);
      }
    }
    memcpy(addr, &nop->op_arg, FIELD_SIZE(nop->op_id.field_id.field));
    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;
    memcpy(&wrap, addr, size);
    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.object_value);
    }
    memcpy(addr, &nop->op_arg, size);
    break;
  }

  case op_array_length:
    wrap.int_value = 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);
}

struct Hjava_lang_Object* BR_unicamp_Guarana_Guarana_makeProxy(struct Hjava_lang_Class *clazz, struct HBR_unicamp_Guarana_MetaObject *mobj) {
  struct Hjava_lang_Object *obj;
  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);
  do_execute_java_class_method("BR/unicamp/Guarana/Guarana", "setMetaObjectOf", "(Ljava/lang/Object;LBR/unicamp/Guarana/MetaObject;LBR/unicamp/Guarana/MetaObject;)V", obj, (jobject)0, mobj);
  do_execute_java_class_method("BR/unicamp/Guarana/Guarana", "broadcast", "(LBR/unicamp/Guarana/Message;Ljava/lang/Object;)V", clazz, execute_java_constructor(0, "BR/unicamp/Guarana/NewProxy", 0, "(Ljava/lang/Object;)V", obj));
  return obj;
}
