/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ClazzFile.h"
#include "arrays.h"
#include "array-class.h"
#include "objects.h"
#include "resolve.h"
#include "log.h"
#include "interp.h"
#include "exceptions.h"
#include "primitive-class.h"
#include "alloc.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define ARRAY_LOG "ArrayRef"

#if 0
static void
array_finalize(GC_obj *obj)
{
  japhar_object* array = (japhar_object*)obj;
  jvalue v;
  void *elements;

  SigPrimType element_type = INSTANCE_DATA(array)->fields[ARRAY_TYPE_INDEX].value.i;

  printf ("Finalizing array %p or type %s\n", obj,
	  SIG_formatPrimitiveTypeToC(THREAD_getEnv(), element_type));

  get_instance_field(array->_static + 1,
		     array->_static,
		     INSTANCE_DATA(array)->fields[ARRAY_BODY_INDEX].value.l);

  /* we need to call the finalizer (since the java code may need the elements)
     before we free the body of the array... do we *really* need to do this,
     I mean, can user code put a finalizer method into an array class? */

  /* object_finalize(obj); */

  if (elements)
    free(elements);

  free(array - 1);
}
#endif

/*
 * Called by opcode anewarray and JNI NewObjectArray
 *
 * Return new LENGTH array with ELEMENT_CLASSNAME elements.
 * element_classname might both be base type and another array.
 */
japhar_obj
create_array(JNIEnv *env, jint length, char *element_classname)
{
  japhar_obj array_reference = NULL;
  char *array_classname = (char*)malloc(strlen(element_classname) + 4);

  if (element_classname[0] == '[')
    sprintf (array_classname, "[%s", element_classname);
  else
    sprintf (array_classname, "[L%s;", element_classname);

  array_reference = new_array(env, length,
			      createFakeArrayClass(env, array_classname));
  free(array_classname);

  return array_reference;
}

ClazzFile *
array_element_type(JNIEnv *env, ClazzFile *array_type)
{
  char *array_classname = getClassName(env, array_type);
  char *array_elementname = array_classname+1;
  ClazzFile *retval;

  /* Bogus array class ? */
  if (NULL == array_classname || array_classname[0] != '[')
    {
      throw_Exception(env, "XXX", "Unknown element of not-array class");
      return NULL;
    }

  switch (array_elementname[0]) {
  case '[': /* Array class */
    retval = createFakeArrayClass(env, array_elementname);
    break;
  case 'L': /* Normal class */
    retval = find_class(env, array_elementname);
    break;
  default: /* primary type */
    retval = createFakePrimitiveClass(env, array_elementname);
    break;
  }

  JAVARLOG4(ARRAY_LOG, 1, "array_element_type('%s' - '%s') = %s 0x%x\n",
	    getClassName(env, array_type),
	    array_elementname, getClassName(env, retval), retval);

  return retval;
}

japhar_obj 
new_array(JNIEnv *env, u4 n_elem, ClazzFile *array_type)
{
  japhar_obj new_arr;
  japhar_object *arr_object;
  int el_size = 0;
  char *array_class_name = getClassName(env, array_type);
  FieldStruct *field;
  jvalue value;
  int el_type;

  switch (array_class_name[1])
    {
    case '[':
      el_type = SIG_JOBJECT;
      el_size = sizeof(jobject);
      break;
    case 'L':
      el_type = SIG_JOBJECT;
      el_size = sizeof(jobject);
      break;
    case 'I':
      el_type = SIG_JINT;
      el_size = sizeof(jobject);
      break;
    case 'F':
      el_type = SIG_JFLOAT;
      el_size = sizeof(jfloat);
      break;
    case 'J':
      el_type = SIG_JLONG;
      el_size = sizeof(jlong);
      break;
    case 'D':
      el_type = SIG_JDOUBLE;
      el_size = sizeof(jdouble);
      break;
    case 'C':
      el_type = SIG_JCHAR;
      el_size = sizeof(jchar);
      break;
    case 'S':
      el_type = SIG_JSHORT;
      el_size = sizeof(jshort);
      break;
    case 'B':
      el_type = SIG_JBYTE;
      el_size = sizeof(jbyte);
      break;
    case 'Z':
      el_type = SIG_JBOOLEAN;
      el_size = sizeof(jboolean);
      break;
    default:
      /* XXX punt */
      el_type = SIG_JINT;
      el_size = sizeof(jint);
      break;
    }

  /* allocate the body first, to we don't have to worry about the
     object machinery if it fails. */
  if (n_elem == 0)
    value.l = (void*)jcalloc(env, 1, el_size); /* just so we have *something* */
  else
    value.l = (void*)jcalloc(env, n_elem, el_size);

  if (value.l == NULL)
    return NULL;

  new_arr = new_object(env, array_type);

  if (new_arr == NULL)
    return NULL;

  obj_to_object(new_arr, arr_object);

  /* do the body of the array */
  field = (*new_arr)->fields [ ARRAY_BODY_INDEX ];
  set_instance_field(new_arr, field, value);

  /* do the length of the array */
  field = (*new_arr)->fields [ ARRAY_LENGTH_INDEX ];
  value.i = n_elem;
  set_instance_field(new_arr, field, value);

  /* do the array's type */
  field = (*new_arr)->fields [ ARRAY_TYPE_INDEX ];
  value.i = el_type;
  set_instance_field(new_arr, field, value);

  /* do the elsize */
  field = (*new_arr)->fields [ ARRAY_ELSIZE_INDEX ];
  value.i = el_size;
  set_instance_field(new_arr, field, value);

  JAVARLOG5 (ARRAY_LOG, 1,
	     "new array --> %p (%ld) {body = %p(%ld), length = %d}\n",
	     new_arr, (long)new_arr, value.l, value.j, n_elem);

  return new_arr;
}


japhar_obj 
multi_new_array (JNIEnv *env, jint *n_elems,
		 int dimensions, ClazzFile *array_type)
{
  int i;
  japhar_obj new_arr = new_array (env, n_elems[ 0 ], array_type);
  jvalue body_item;

  get_instance_field(new_arr,
		     (*new_arr)->fields [ ARRAY_BODY_INDEX ],
		     &body_item);

  if (dimensions > 1)
    {
      ClazzFile *subarray_type = createFakeArrayRemoveDimension(env,
								array_type);
      
      for (i=0; i<n_elems[ 0 ]; i++)
	((jobject*)body_item.l)[ i ] =
	  (jobject) multi_new_array (env, n_elems + 1,
				     dimensions - 1, subarray_type);
    }

  return new_arr;
}

/**
 * Creates a jvalue array from a jobjectArray.  Returned pointer
 * is allocated via calloc() and must therefore be freed by caller
 * via free() when no longer needed.
 */
jvalue *
jobjects_to_jvalues(JNIEnv *env, jobjectArray jarr)
{
  jint size = (*env)->GetArrayLength(env, jarr);
  jvalue* arr = (jvalue*) jcalloc(env, size, sizeof(jvalue));
  int i;

  if (NULL == arr)
    return NULL;

  for (i = 0; i < size; ++i) {
    jobject elem = (*env)->GetObjectArrayElement(env, jarr, i);
    arr[i].l = elem;
  }

  return arr;
}

jint
get_array_length(japhar_obj arrayref)
{
  FieldStruct *field;

  /* duplicate some of the code in get_instance_field since we
     know about the structure arrays */

  field = (*arrayref)->fields [ ARRAY_LENGTH_INDEX ];

  return *(jint*)(((char*)arrayref + 2 * sizeof(ClazzFile*)) + field->field_offset);
}

void*
get_array_body(japhar_obj arrayref)
{
  FieldStruct *field;
  
  field = (*arrayref)->fields [ ARRAY_BODY_INDEX ];

  return *(jobject*)(((char*)arrayref + 2 * sizeof(ClazzFile*)) + field->field_offset);
}
