/* -*- Mode: C; c-file-style: "gnu" -*-
   inflater.c -- native methods for java/util/zip/Inflater
   Created: Chris Toshok <toshok@hungry.com>, 9-Nov-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 "jni.h"
#include "zlib.h"
#include "objects.h"

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

/* wow...
   1.2 has completely raped this file. */

static z_streamp
get_inflater_strm(JNIEnv *env,
		  jobject obj)
{
  jfieldID strm_field;
  jclass obj_class = (*env)->GetObjectClass(env, obj);

#ifdef JDK1_1
  jint strm;
  strm_field = (*env)->GetFieldID(env, obj_class, "strm", "I");
  strm = (*env)->GetIntField(env, obj, strm_field);
#else
  jlong strm;
  strm_field = (*env)->GetFieldID(env, obj_class, "strm", "J");
  strm = (*env)->GetLongField(env, obj, strm_field);
#endif

  return (z_streamp)(jint)strm;
}

static void
set_inflater_strm(JNIEnv *env,
		  jobject obj,
		  z_streamp strm)
{
  jfieldID strm_field;
  jclass obj_class = (*env)->GetObjectClass(env, obj);

  /* XXX Should be rewritten to use lookup table */
  assert(sizeof(z_streamp) <= sizeof(jint));

  strm_field = (*env)->GetFieldID(env, obj_class, "strm", "I");
  (*env)->SetIntField(env, obj, strm_field, (jint)strm);
}

JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_setDictionary(JNIEnv *env,
					  jobject obj,
					  jbyteArray b,
					  jint off,
					  jint len)
{
  z_streamp strm = get_inflater_strm(env, obj);
  jbyte* dict;

  dict = (*env)->GetByteArrayElements(env, b, NULL);

  inflateSetDictionary(strm,
		       dict + off,
		       len);

  (*env)->ReleaseByteArrayElements(env, b, dict, 0);
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Inflater_inflate(JNIEnv *env,
				    jobject obj,
				    jbyteArray b,
				    jint off,
				    jint len)
{
  jclass obj_class = (*env)->GetObjectClass(env, obj);
  z_streamp strm = get_inflater_strm(env, obj);
  jfieldID buf_field;
  jfieldID len_field;
  jfieldID off_field;
  jbyteArray buf;
  jint in_length;
  jbyte* in_bytes;
  jint in_off;
  jbyte* out_bytes;
  int inflate_ret_val;
  jint ret_val = 0;
  int prev_total_out = strm->total_out;

  buf_field = (*env)->GetFieldID(env, obj_class, "buf", "[B");
  len_field = (*env)->GetFieldID(env, obj_class, "len", "I");
  off_field = (*env)->GetFieldID(env, obj_class, "off", "I");

  buf = (*env)->GetObjectField(env, obj, buf_field);
  in_length = (*env)->GetIntField(env, obj, len_field);
  in_off = (*env)->GetIntField(env, obj, off_field);

  in_bytes = (*env)->GetByteArrayElements(env, buf, NULL);
  out_bytes = (*env)->GetByteArrayElements(env, b, NULL);
  
  strm->next_in = (char*)in_bytes + in_off;
  strm->avail_in = in_length;
  strm->next_out = (char*)out_bytes + off;
  strm->avail_out = len;

  inflate_ret_val = inflate(strm, Z_PARTIAL_FLUSH);

  if (inflate_ret_val == Z_OK)
    ret_val = len - strm->avail_out;
  else
    {
      if (inflate_ret_val == Z_NEED_DICT
	  || inflate_ret_val == Z_BUF_ERROR)
	ret_val = 0;
      else if (inflate_ret_val == Z_DATA_ERROR)
	{
	  inflateSync(strm);
	  ret_val = 0;
	}
      else if (inflate_ret_val == Z_STREAM_END) /* we reached the end of the stream */
	{
	  ret_val = strm->total_out - prev_total_out;
	}
      else if (inflate_ret_val == Z_MEM_ERROR
	       || inflate_ret_val == Z_STREAM_ERROR)
	{
	  jclass cls = (*env)->FindClass(env, "java/util/zip/DataFormatException");

	  (*env)->ThrowNew(env, cls, "in inflateBytes");
	}
      else
	assert(0);
    }

  (*env)->ReleaseByteArrayElements(env, b, out_bytes, 0);
  (*env)->ReleaseByteArrayElements(env, b, in_bytes, 0);

  return ret_val;
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Inflater_inflateBytes(JNIEnv *env,
					 jobject obj,
					 jbyteArray b,
					 jint off,
					 jint len)
{
  return Java_java_util_zip_Inflater_inflate(env, obj, b, off, len);
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Inflater_getAdler(JNIEnv *env,
				     jobject obj)
{
  z_streamp strm = get_inflater_strm(env, obj);

  return strm->adler;
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Inflater_getTotalIn(JNIEnv *env,
				       jobject obj)
{
  z_streamp strm = get_inflater_strm(env, obj);

  return strm->total_in;
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Inflater_getTotalOut(JNIEnv *env,
					jobject obj)
{
  z_streamp strm = get_inflater_strm(env, obj);

  return strm->total_out;
}

#ifdef JDK1_1
JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_reset(JNIEnv *env,
				  jobject obj)
{
  z_streamp strm = get_inflater_strm(env, obj);

  inflateReset(strm);
}
#else /* JDK1.2 */
JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_reset(JNIEnv *env,
				  jclass cls,
				  jlong strm)
{
  z_streamp zstrm = (z_streamp)(jint)strm;

  inflateReset(zstrm);
}
#endif

JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_end(JNIEnv *env,
				jobject obj)
{
  z_streamp strm = get_inflater_strm(env, obj);

  inflateEnd(strm);
}

#if 0
/* well, it had to happen sooner or later... in 1.1, this function is defined as:

   private native void init(boolean);

   but in 1.2, it's defined as:

   private static native long init(boolean);

   *sigh*....
*/

JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_init(JNIEnv *env,
				 jobject obj,
				 jboolean nowrap)
{
  z_streamp strm = (z_streamp)calloc(1, sizeof(z_stream));

  set_inflater_strm(env, obj, strm);

  inflateInit(strm);
}
#else
/* JDK1.2 */
JNIEXPORT jlong JNICALL
Java_java_util_zip_Inflater_init(JNIEnv *env,
				 jobject cls,
				 jboolean nowrap)
{
  z_streamp strm = (z_streamp)calloc(1, sizeof(z_stream));
#define WSIZE 0x8000
  int i, windowBits;
  /* windowBits = log2(wsize) */
  for (i = WSIZE, windowBits = 0;  !(i & 1);  i >>= 1, ++windowBits);

  inflateInit2(strm, -windowBits);

  return (jlong)(jint)strm;
}
#endif

/* JDK1.2 */
JNIEXPORT void JNICALL
Java_java_util_zip_Inflater_initIDs(JNIEnv *env,
				    jclass cls)
{
}
