/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
/*
  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 "runtimeint.h"
#include "ClazzFile.h"

#include "prmem.h"

extern PRLogModuleInfo *arrayLm;

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

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

  HVM_FieldGet(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)
    PR_DELETE(elements);

  PR_DELETE(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.
 */
PR_IMPLEMENT(japhar_object*)
HVM_ArrayCreate(HungryEnv *henv, PRUint32 length, char *element_classname)
{
  japhar_object* array_reference = NULL;
  char *array_classname = (char*)malloc(PL_strlen(element_classname) + 4);

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

  array_reference = HVM_ArrayNew(henv, length,
                                 HVM_ClassFind(henv, array_classname));
  PR_DELETE(array_classname);

  return array_reference;
}

PR_IMPLEMENT(ClazzFile*)
HVM_ArrayGetElementType(HungryEnv *henv, ClazzFile *array_type)
{
  char *array_classname = getClassName(env, array_type);
  char *array_elementname = array_classname+1;
  ClazzFile *retval;

  retval = HVM_ClassFind(henv, array_elementname);

  PR_LOG(arrayLm, PR_LOG_DEBUG,
         ("array_element_type('%s' - '%s') = %s 0x%x\n",
          getClassName(henv, array_type),
          array_elementname, getClassName(henv, retval), retval));

  return retval;
}

PR_IMPLEMENT(japhar_object*)
HVM_ArrayClone(HungryEnv *henv, japhar_object* old_arr)
{
  ClazzFile *array_type = old_arr->clazz;
  japhar_object* new_arr;
  int n_elem;
  int el_size = 0;
  void *new_body;
  char *array_class_name = getClassName(env, array_type);
  FieldStruct *field;
  InterpValue value;

  el_size = HVM_SigSizeofStrInBytes(array_class_name + 1);

  /* get the length of the old array */
  field = old_arr->clazz->fields [ ARRAY_LENGTH_INDEX ];
  HVM_FieldGet(old_arr, field, &value);
  n_elem = value.i;

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

  if (new_body == NULL)
    return NULL;

  new_arr = HVM_ObjectNew(henv, array_type);

  if (new_arr == NULL)
    return NULL;

  /* get the body of the old array and copy it. */
  field = new_arr->clazz->fields [ ARRAY_BODY_INDEX ];
  HVM_FieldGet(old_arr, field, &value);
  memcpy(new_body, value.l, n_elem * el_size);

  /* do the body of the array */
  field = new_arr->clazz->fields [ ARRAY_BODY_INDEX ];
  value.l = new_body;
  HVM_FieldSet(new_arr, field, value);

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

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

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

  PR_LOG (arrayLm, PR_LOG_DEBUG,
          ("clone array --> %p (%ld) {body = %p(%ld), length = %d}\n",
           new_arr, (long)new_arr, value.l, value.j, n_elem));

  return new_arr;
}

PR_IMPLEMENT(japhar_object*)
HVM_ArrayNew(HungryEnv *henv, PRUint32 n_elem, ClazzFile *array_type)
{
  japhar_object* new_arr;
  int el_size = 0;
  char *array_class_name = getClassName(env, array_type);
  FieldStruct *field;
  InterpValue value;

  el_size = HVM_SigSizeofStrInBytes(array_class_name + 1);

  /* 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(henv, 1, el_size); /* just so we have *something* */
  else
    value.l = (void*)jcalloc(henv, n_elem, el_size);

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

  new_arr = HVM_ObjectNew(henv, array_type);

  if (new_arr == NULL)
    return NULL;

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

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

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

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

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

  return new_arr;
}

PR_IMPLEMENT(japhar_object*)
HVM_ArrayNewMulti (HungryEnv *henv, PRUint32 *n_elems,
                  int dimensions, ClazzFile *array_type)
{
  PRUint32 i;
  japhar_object* new_arr = HVM_ArrayNew (henv, n_elems[ 0 ], array_type);
  InterpValue body_item;

  HVM_FieldGet(new_arr,
               new_arr->clazz->fields [ ARRAY_BODY_INDEX ],
               &body_item);

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

  return new_arr;
}

/**
 * Creates a InterpValue array from a jobjectArray.  Returned pointer
 * is allocated via PR_CALLOC() and must therefore be freed by caller
 * via free() when no longer needed.
 */
InterpValue *
objects_to_values(HungryEnv *henv, japhar_object* jarr)
{
  PRUint32 size = HVM_ArrayGetLength(jarr);
  InterpValue* arr = (InterpValue*) jcalloc(henv, size, sizeof(InterpValue));
  PRUint32 i;
  japhar_object** objs;

  if (NULL == arr)
    return NULL;

  objs = (japhar_object**)HVM_ArrayGetBody(jarr);
  for (i = 0; i < size; ++i)
    arr[i].l = objs[i];

  return arr;
}

PR_IMPLEMENT(PRUint32)
HVM_ArrayGetLength(japhar_object* arrayref)
{
  FieldStruct *field;

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

  field = arrayref->clazz->fields [ ARRAY_LENGTH_INDEX ];

  return *(PRUint32*)((char*)&arrayref->fields + field->field_offset);
}

PR_IMPLEMENT(void*)
HVM_ArrayGetBody(japhar_object* arrayref)
{
  FieldStruct *field;
  
  field = arrayref->clazz->fields [ ARRAY_BODY_INDEX ];

  return *(void**)((char*)&arrayref->fields + field->field_offset);
}
