/*
 * Result.c
 * Implementation of native methods in class BR.unicamp.Guarana.Result
 *
 * 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"

#define checkNullOp(op) do { \
  if (!(op)) \
    SignalError("java/lang/NullPointerException", "null operation"); \
} while (0)

#define checkRetType(op, type) do { \
  Hjava_lang_Class *cls = BR_unicamp_Guarana_Operation_getType((op)); \
  checkNullOp((op)); \
  if (!CLASS_IS_PRIMITIVE(cls) || CLASS_PRIM_SIG(cls) != (type)) \
    SignalError("java/lang/IllegalArgument", "incorrect return type"); \
} while (0)

#define checkRet(res) do { \
  if (((res)->kind & res_returned) == 0) \
    SignalError("java/lang/IllegalArgument", "not a return valud"); \
} while (0)

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_operation(struct HBR_unicamp_Guarana_Operation *op, jint mode) {
  enum result_type nmode;
  jvalue wrap;
  switch (mode & res_reqMask) {
  case res_noResult:
    nmode = res_noResult;
    break;
  case res_inspect:
    nmode = res_inspect;
    break;
  case res_modify:
  default:
    nmode = res_modify;
    break;
  }
  return guarana_new_result_object(nmode, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnObject(struct Hjava_lang_Object *obj, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkNullOp(op);
  guarana_unwrap(BR_unicamp_Guarana_Operation_getType(op), &wrap, obj);
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnBoolean(jbool res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'Z');
  wrap.i = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnByte(jbyte res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'B');
  wrap.i = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnShort(jshort res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'S');
  wrap.i = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnInt(jint res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'I');
  wrap.i = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnLong(jlong res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'J');
  wrap.j = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnFloat(jdouble res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'F');
  wrap.d = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnDouble(jdouble res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'D');
  wrap.d = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnChar(jchar res, struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'C');
  wrap.i = res;
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_returnVoid(struct HBR_unicamp_Guarana_Operation *op) {
  jvalue wrap;
  checkRetType(op, 'V');
  return guarana_new_result_object(res_returned, op, wrap);
}

struct HBR_unicamp_Guarana_Result* BR_unicamp_Guarana_Result_throwObject(struct Hjava_lang_Throwable *obj, struct HBR_unicamp_Guarana_Operation *op_) {
  jvalue wrap;
  checkNullOp(op_);
  if (obj) do {
      static Hjava_lang_Class *javaLangError = 0;
      static Hjava_lang_Class *javaLangRuntimeException = 0;
      Hjava_lang_Class *objclass = OBJECT_CLASS((Hjava_lang_Object*)obj);
      NativeOperation *op;
      if (javaLangError == 0)
	javaLangError = lookupClass("java/lang/Error");
      if (instanceof(javaLangError, objclass))
	break;
      if (javaLangRuntimeException == 0)
	javaLangRuntimeException = lookupClass("java/lang/RuntimeException");
      if (instanceof(javaLangRuntimeException, objclass))
	break;
      op = (NativeOperation*)op_;
      switch (op->kind & op_kind_mask) {
      case op_nop:
	break;

      case op_method_invocation:
      case op_constructor_invocation:
	{
	  methods *meth = op->op_id.method;
	  Hjava_lang_Class *decclass = meth->class;
	  constIndex *excepts = meth->declared_exceptions;
	  int i, nr = meth->ndeclared_exceptions;
	  for (i = 0; i < nr; ++i) {
	    if (instanceof(getClass(excepts[i], decclass), objclass))
	      break;
	  }
	  if (i < nr)
	    break;
	}
	/* do not break */
	
      default:
	SignalError("java/lang/IllegalArgumentException", "invalid exception type");
      }
  } while (0);
  wrap.l = obj;
  return guarana_new_result_object(res_thrown, op_, wrap);
}

jint BR_unicamp_Guarana_Result_getMode(struct HBR_unicamp_Guarana_Result *this) {
  return ((NativeResult*)this)->kind;
}

struct HBR_unicamp_Guarana_Operation* BR_unicamp_Guarana_Result_getOperation(struct HBR_unicamp_Guarana_Result *this) {
  return ((NativeResult*)this)->op;
}

jbool BR_unicamp_Guarana_Result_isException(struct HBR_unicamp_Guarana_Result *this) {
  return (((NativeResult*)this)->kind & res_thrown) != 0;
}

struct Hjava_lang_Object* BR_unicamp_Guarana_Result_getObjectValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  if (this->kind & res_thrown)
    return this->res.l;
  else if (this->kind & res_returned)
    return guarana_wrap(BR_unicamp_Guarana_Operation_getType(this->op), this->res, 0);
  else
    return 0;
}

jbool BR_unicamp_Guarana_Result_getBooleanValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'Z');
  return this->res.i;
}

jbyte BR_unicamp_Guarana_Result_getByteValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'B');
  return this->res.i;
}

jshort BR_unicamp_Guarana_Result_getShortValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'S');
  return this->res.i;
}

jint BR_unicamp_Guarana_Result_getIntValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'I');
  return this->res.i;
}

jlong BR_unicamp_Guarana_Result_getLongValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'J');
  return this->res.j;
}

jchar BR_unicamp_Guarana_Result_getCharValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'C');
  return this->res.i;
}

jdouble BR_unicamp_Guarana_Result_getFloatValueAsDouble(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'F');
  return this->res.d;
}

jdouble BR_unicamp_Guarana_Result_getDoubleValue(struct HBR_unicamp_Guarana_Result *this_) {
  NativeResult *this = (NativeResult*)this_;

  checkRet(this);
  checkRetType(this->op, 'D');
  return this->res.d;
}
