/* -*- Mode: C; c-file-style: "gnu" -*-
   deflater.c -- native methods for java/util/zip/Deflater
   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 "runtime.h"

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

static z_streamp
get_deflater_strm(JNIEnv *env,
		  jobject obj)
{
  jfieldID strm_field;
#ifdef JDK1_1
  jint strm;
#else
  jlong strm;
#endif
  jclass obj_class = (*env)->GetObjectClass(env, obj);

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

  return (z_streamp)strm;
}

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

  /* XXX This should be rewritten to use a 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);
}

static jint
get_deflater_level(JNIEnv *env,
		   jobject obj)
{
  jfieldID level_field;
  jclass obj_class = (*env)->GetObjectClass(env, obj);

  level_field = (*env)->GetFieldID(env, obj_class, "level", "I");

  return (*env)->GetIntField(env, obj, level_field);
}

static jint
get_deflater_strategy(JNIEnv *env,
		      jobject obj)
{
  jfieldID strategy_field;
  jclass obj_class = (*env)->GetObjectClass(env, obj);

  strategy_field = (*env)->GetFieldID(env, obj_class, "strategy", "I");

  return (*env)->GetIntField(env, obj, strategy_field);
}

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

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

  deflateSetDictionary(strm,
		       dict + off,
		       len);

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

JNIEXPORT jint JNICALL
Java_java_util_zip_Deflater_deflate(JNIEnv *env,
				    jobject obj,
				    jbyteArray b,
				    jint off,
				    jint len)
{
  (*env)->FatalError(env, "java.util.zip.Deflater.deflate() not implemented");
  return 0;
}

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

  return strm->adler;
}

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

  return strm->total_out;
}

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

  return strm->total_in;
}

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

  deflateReset(strm);
}

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

  deflateEnd(strm);
}

#ifdef JDK1_1
JNIEXPORT void JNICALL
Java_java_util_zip_Deflater_init(JNIEnv *env,
				 jobject obj,
				 jboolean nowrap)
{
  z_streamp strm = get_deflater_strm(env, obj);
  jint level = get_deflater_level(env, obj);
  jint strategy = get_deflater_strategy(env, obj);

  deflateInit(strm, level);

  deflateParams(strm,
		level,
		strategy);
}
#else
JNIEXPORT jlong JNICALL
Java_java_util_zip_Deflater_init(JNIEnv *env,
				 jclass cls,
				 jint strategy,
				 jboolean nowrap)
{
  z_streamp strm = (z_streamp)calloc(1, sizeof(z_stream));

  deflateInit(strm, Z_BEST_SPEED);

  deflateParams(strm,
		Z_BEST_SPEED,
		strategy);

  return (jlong)strm;
}
#endif

#ifndef JDK1_1
static jfieldID buf_field;
static jfieldID off_field;
static jfieldID len_field;

JNIEXPORT void JNICALL
Java_java_util_zip_Deflater_initIDs(JNIEnv *env,
				    jclass cls)
{
  buf_field = (*env)->GetFieldID(env, cls, "buf", "[B");
  off_field = (*env)->GetFieldID(env, cls, "off", "I");
  len_field = (*env)->GetFieldID(env, cls, "len", "I");
}

JNIEXPORT jint JNICALL
Java_java_util_zip_Deflater_deflateBytes(JNIEnv *env,
					 jobject obj,
					 jbyteArray b,
					 jint off, jint len)
{
  z_streamp strm = get_deflater_strm(env, obj);
  jint ret_val;
  jbyte *out_bytes;
  jbyte *in_bytes;
  jbyteArray input_buf;
  jint input_off;
  jint input_len;

  input_off = (*env)->GetIntField(env, obj, off_field);
  input_len = (*env)->GetIntField(env, obj, len_field);
  input_buf = (*env)->GetIntField(env, obj, buf_field);

  out_bytes = (*env)->GetByteArrayElements(env, b, NULL);
  in_bytes = (*env)->GetByteArrayElements(env, input_buf, NULL);

  strm->next_in = in_bytes + off;
  strm->avail_in = input_len;

  strm->next_out = out_bytes + off;
  strm->avail_out = len;

  ret_val = deflate(strm, Z_SYNC_FLUSH);

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

  return ret_val;
}
#endif
