/*
 * Copyright (c) 1999-2001 Caucho Technology.  All rights reserved.
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Developer Source License ("the License").  The following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 *
 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL Caucho OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.      
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
/* probably system-dependent */
#include <dlfcn.h>
#include <jni.h>

#include "cse.h"
#include "proxy.h"

extern int errno;

static int
Java_com_caucho_server_http_JniStream_readNative(JNIEnv *env,
                                                 jobject obj,
                                                 jint fd,
                                                 jbyteArray buf,
                                                 jint offset,
                                                 jint length)
{
  request_t *req = (request_t *) fd;
  int sublen;
  char *jbuf;
  jboolean is_copy = 0;

  if (req->content_offset >= req->content_length)
    return -1;

  sublen = req->length - req->offset;
  if (sublen <= 0) {
    sublen = request_fill(req);
    
    if (sublen <= 0)
      return -1;
  }

  if (sublen > req->content_length - req->content_offset)
    sublen = req->content_length - req->content_offset;

  if (sublen > length)
    sublen = length;

  jbuf = (*env)->GetPrimitiveArrayCritical(env, buf, &is_copy);

  if (jbuf)
    memcpy(jbuf + offset, req->buffer + req->offset, sublen);
  
  (*env)->ReleasePrimitiveArrayCritical(env, buf, jbuf, 0);

  req->content_offset += sublen;
  req->offset += sublen;

  return sublen;
}

static int
Java_com_caucho_server_http_JniStream_writeNative(JNIEnv *env,
                                                  jobject obj,
                                                  jint fd,
                                                  jbyteArray buf,
                                                  jint offset,
                                                  jint length)
{
  request_t *req = (request_t *) fd;
  response_t *res;
  char *cbuf;
  char buffer[8192];
  jboolean is_copy;
  int sublen;
  int result;
  int buffer_offset;

  if (! req)
    return -1;

  buffer_offset = 0;
  
  res = req->response;

  while (length > 0) {
    if (length < 8192)
      sublen = length;
    else
      sublen = 8192;

    is_copy = 0;
    cbuf = (*env)->GetPrimitiveArrayCritical(env, buf, &is_copy);

    if (cbuf) {
      memcpy(buffer, cbuf + offset, sublen);

      (*env)->ReleasePrimitiveArrayCritical(env, buf, cbuf, 0);
    }

    buffer_offset = 0;
    if (! res->after_headers) {
      res->after_headers = 1;
      buffer_offset = response_send_headers(res, buffer, sublen);
    }

    result = response_write_chunk(res, buffer + buffer_offset,
                                  sublen - buffer_offset);

    if (result < 0) {
      return -1;
    }

    length -= sublen;
    offset += sublen;
  }

  return 0;
}

static int
Java_com_caucho_server_http_JniStream_flushNative(JNIEnv *env,
                                                  jobject obj,
                                                  jint fd)
{
  request_t *req = (request_t *) fd;
  response_t *res;

  if (! req)
    return -1;

  res = req->response;

  return cse_flush_request(res);
}

static void
Java_com_caucho_server_http_JniServer_closeNative(JNIEnv *env,
                                                  jobject obj,
                                                  jint fd)
{
  resin_close_server(fd);
}

void
jni_vfs_init(JNIEnv *env)
{
  JNINativeMethod methods[3];
  jclass jniClass;

  methods[0].name = "writeNative";
  methods[0].signature = "(I[BII)I";
  methods[0].fnPtr = Java_com_caucho_server_http_JniStream_writeNative;

  methods[1].name = "readNative";
  methods[1].signature = "(I[BII)I";
  methods[1].fnPtr = Java_com_caucho_server_http_JniStream_readNative;

  methods[2].name = "flushNative";
  methods[2].signature = "(I)I";
  methods[2].fnPtr = Java_com_caucho_server_http_JniStream_flushNative;
  
  jniClass = (*env)->FindClass(env, "com/caucho/server/http/JniStream");
  if (jniClass == 0) {
    fprintf(stderr, "Class not found: com.caucho.server.http.JniStream\n");
    exit(1);
  }

  (*env)->RegisterNatives(env, jniClass, methods, 3);
  
  methods[0].name = "closeNative";
  methods[0].signature = "()V";
  methods[0].fnPtr = Java_com_caucho_server_http_JniServer_closeNative;
  
  jniClass = (*env)->FindClass(env, "com/caucho/server/http/JniServer");
  if (jniClass == 0) {
    fprintf(stderr, "Class not found: com.caucho.server.http.JniServer\n");
    exit(1);
  }

  (*env)->RegisterNatives(env, jniClass, methods, 1);
}
