/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   jnistr.c -- Java Native Interface string operations.
   Created: Chris Toshok <toshok@hungry.com>, 26-Jul-1997
 */
/*
  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 "jniint.h"

extern PRLogModuleInfo* jniLm;

/*
  UTF encoding description.
  It is way to encode unicode characters (16-bit) into 8-bit character stream.
  Idea is to leave most often used characters (1-127) the same, at cost of 
  increased length of high end chars.
  01-7f              0[0-6bit]
  0 and 80-7ff       110[6-10bit]   10[0-5bit]
  800-ffff           1110[12-15bit] 10[6-11bit] 10[0-5bit]
*/

/*
  JDK String internal variable description.

  - value (Char[]) The content of the string
  - count (int)    The length of this string
  - offset (int)   How far into the content array this string starts

  The content of a JDK string runs from value[offset] to value[offset+count].
*/

#define UTF8_1BYTE_TRESHOLD     0x0080
#define UTF8_2BYTE_TRESHOLD     0x07ff

static jclass string_class = NULL;
static jmethodID string_ctor;
static jfieldID value_field;
static jfieldID offset_field;
static jfieldID length_field;

static inline void
init_ids(JNIEnv *env)
{
  if (!string_class)
    {
      string_class = (*env)->FindClass(env, java_lang_String);
      string_ctor = (*env)->GetMethodID(env, string_class, "<init>", "([C)V");
      value_field = (*env)->GetFieldID(env, string_class, "value", "[C");
      offset_field = (*env)->GetFieldID(env, string_class, "offset", "I");
      length_field = (*env)->GetFieldID(env, string_class, "count", "I");  
    }
}

jstring
JNIFUNC(NewString)(JNIEnv *env,
                   const jchar *unicodeChars,
                   jsize len)
{
  jstring new_string;
  jcharArray char_array;
  jchar* array_elements;
  int i;

  init_ids(env);

  char_array = (*env)->NewCharArray(env, len);
  if (NULL == char_array)
    return NULL;

  array_elements = (*env)->GetCharArrayElements(env, char_array, NULL);
  
  for (i = 0; i < len; i ++)
    array_elements[i] = unicodeChars[i];

  (*env)->ReleaseCharArrayElements(env, char_array, array_elements, 0);

  new_string = (*env)->NewObject(env, string_class, string_ctor, char_array, 0, len);
  
  return new_string;
}

jsize
JNIFUNC(GetStringLength)(JNIEnv *env,
                         jstring string)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  return HVM_StringGetLength(henv, string);
}

jchar*
JNIFUNC(GetStringChars)(JNIEnv *env,
                        jstring string,
                        jboolean *isCopy)
{
  jint offset = 0;
  jcharArray char_array = (*env)->GetObjectField(env, string, value_field);
  jboolean char_copy;
  jchar* chars = (*env)->GetCharArrayElements(env, char_array, &char_copy);
  int i;
  int length = (*env)->GetIntField(env, string, length_field);
  jchar* copyChars = PR_Calloc(length, sizeof(*copyChars));
  
  if (NULL != offset_field) /* Classpath has no offset field */
    offset = (*env)->GetIntField(env, string, offset_field);

  PR_LOG(jniLm, PR_LOG_DEBUG,
         ("GetStringChars() value=%s offset=%d length=%d\n",
          char_array ? "(non-null)" : "(null)", offset, length));

  if (isCopy)
    *isCopy = JNI_TRUE;
    

  for ( i =0; i < length; i++ )
    {
      copyChars[i] = chars[i+offset];
    }


  (*env)->ReleaseCharArrayElements(env, char_array, chars, 0);

  return copyChars;
}

void
JNIFUNC(ReleaseStringChars)(JNIEnv *env,
                            jstring string,
                            const jchar *chars)
{
  /* do we need to copy it back */
  PR_DELETE((jchar*)chars);
}

/*
  This one can also be speed up by using alloc instead of malloc/free
 */
 
jstring
JNIFUNC(NewStringUTF)(JNIEnv *env,
                      const char *bytes)
{
  HungryEnv *henv = JNIEnvToHEnv(env);

  return HVM_StringFromCString(henv, bytes);
}

jsize
JNIFUNC(GetStringUTFLength)(JNIEnv *env,
                            jstring string)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  return HVM_StringGetUTFLength(henv,
                                string);
}

const char*
JNIFUNC(GetStringUTFChars)(JNIEnv *env,
                           jstring string,
                           jboolean *isCopy)
{
  HungryEnv *henv = JNIEnvToHEnv(env);

  /* we always copy */
  if (isCopy)
    *isCopy = JNI_TRUE;
  return HVM_StringToCString(henv,
                             string);
}

void
JNIFUNC(ReleaseStringUTFChars)(JNIEnv *env,
                               jstring string,
                               const char *utf)
{
  PR_DELETE((char*)utf);
}

/* New JDK1.2 JNI functions */

void
JNIFUNC(GetStringRegion)(JNIEnv* env, jstring string, jsize start,
                         jsize len, jchar * buf )
{
  jint offset = 0;
  jcharArray char_array;
  jboolean char_copy;
  jchar* chars;
  int i;
  int length;

  init_ids(env);

  char_array = (*env)->GetObjectField(env, string, value_field);
  chars = (*env)->GetCharArrayElements(env, char_array, &char_copy);
  length = (*env)->GetIntField(env, string, length_field);

  if (NULL != offset_field) /* Classpath has no offset field */
    offset = (*env)->GetIntField(env, string, offset_field);

  if ( length < start + len )
    {
      /*
        JNI was not supposed to catch errors !!!! Sun is inconsistent
        (*env)->ThrowNew(env, 
        (*env)->FindClass(env, java_lang_StringIndexOutOfBoundsException), "");
      */
    }    

  for ( i =start; i < len+start; i++ )
    {
      buf[i] = chars[i+offset];
    }


  (*env)->ReleaseCharArrayElements(env, char_array, chars, 0);

}

void 
JNIFUNC(GetStringUTFRegion)( JNIEnv *env, jstring string, jsize start,
                             jsize len, char * buf )
{

  jint offset = 0;
  jcharArray char_array;
  jboolean char_copy;
  jchar* chars;
  int java_at, utf_at;
  int length;

  init_ids(env);

  char_array = (*env)->GetObjectField(env, string, value_field);
  chars = (*env)->GetCharArrayElements(env, char_array, &char_copy);
  length = (*env)->GetIntField(env, string, length_field);

  if (NULL != offset_field) /* Classpath has no offset field */
    offset = (*env)->GetIntField(env, string, offset_field);

  utf_at =0;
  
  if ( length < start + len )
    {
      /*
        JNI was not supposed to catch errors !!!! Sun is inconsistent
        (*env)->ThrowNew(env, 
        (*env)->FindClass(env, java_lang_StringIndexOutOfBoundsException), "");
      */
    }

  for ( java_at=offset+start; java_at < (len+start+offset); java_at++ )
    {
      jchar atchar = chars[java_at];
      if ( atchar == 0 )
        {
          /* Zero is encoded by 2 bytes in java utf8 */
          buf[utf_at++] = (jbyte)0xC0;
          buf[utf_at++] = (jbyte)0x80;
        }
      else if ( atchar <= UTF8_1BYTE_TRESHOLD )
        {
          buf[utf_at++] = (jbyte) atchar;
        }
      else if ( atchar <= UTF8_2BYTE_TRESHOLD )
        {
          buf[utf_at++] = 0xC0 | ((atchar & 0x07C0)>>6);
          buf[utf_at++] = 0x80 | ((atchar & 0x003f));
        }
      else
        {
          buf[utf_at++] = 0xE0| ((atchar & 0xF000)>>12);
          buf[utf_at++] = 0x80| ((atchar & 0x0FC0)>>6);
          buf[utf_at++] = 0x80| ((atchar & 0x003f));
        }       
    }


  (*env)->ReleaseCharArrayElements(env, char_array, chars, 0);

}

/* 
  for now just map to normal functions - later we may want to give direct
  access to string array
*/
const jchar * 
JNIFUNC(GetStringCritical)( JNIEnv *env, jstring string, 
                            jboolean *isCopy )
{
  return (*env)->GetStringChars(env, string, isCopy);
}

void
JNIFUNC(ReleaseStringCritical)(JNIEnv *env, jstring string,
                               const jchar *carray )
{
  PR_DELETE((jchar*)carray);
}
