/* -*- Mode: C; c-file-style: "gnu" -*-
   init.c -- Startup functions
   Created: Petter Reinholdtsen <pere@td.org.uit.no>, 1998-12-04
 */
/*
  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 "init.h"
#include "dynamic_loading.h"
#include "resolve.h"
#include "objects.h"
#include "method.h"
#include "op_stack.h"
#include "interp.h"
#include "exceptions.h"
#include "alloc.h"

#include <string.h>

/*
 * Return true (1) if everything went OK, and false (0) otherwise.
 */
int
initialize_system_libraries(void)
{
#ifndef _WINDOWS
  DLL_LibHandle native_handle = DLL_find("lang");
  if (!native_handle)
    return 0;

  native_handle = DLL_find("io");
  if (!native_handle)
    return 0;

  native_handle = DLL_find("util");
  if (!native_handle)
    return 0;

  native_handle = DLL_find("reflect");
  if (!native_handle)
    return 0;

  native_handle = DLL_find("security");
  if (!native_handle)
    return 0;
#endif

  return 1;
}

void
lowlevel_thread_init(JNIEnv *env,
		     jboolean is_primordial,
		     char *name,
		     jobject threadgroup,
		     int priority /* if -1, assign the NORM_PRIORITY */)
{
  JThreadInfo* native_thread_info = (JThreadInfo*)calloc(1, sizeof(JThreadInfo));
  jobject thread;
  jfieldID field;
  jmethodID method;
  jstring thread_name;
  StackFrame *initial_native_frame;

  static jclass System = NULL;
  static jclass Thread = NULL;
  static jclass ThreadGroup = NULL;

  if ((*env)->ExceptionOccurred(env))
    {
      (*env)->ExceptionDescribe(env);
      return;
    }

  if (!System)
    System = clazzfile_to_jclass(env, find_class(env, "java/lang/System"));
  if (!Thread)
    Thread = clazzfile_to_jclass(env, find_class(env, "java/lang/Thread"));
  if (!ThreadGroup)
    ThreadGroup = clazzfile_to_jclass(env, find_class(env, "java/lang/ThreadGroup"));

  if ((!Thread) || (!ThreadGroup))
    {
      fprintf(stderr,
	      "Unable to initialize threads: cannot find class java/lang/%s\n",
	      ((!Thread) ? "Thread" : "ThreadGroup"));
      exit(1);
    }
  /* just to make sure. */
  if (!System)
    {
      fprintf(stderr,
	      "Unable to initialize vm:  cannot find class java/lang/System\n");
      exit(1);
    }

  if (NULL == native_thread_info)
    {
      throw_Exception(env, "java/lang/VirtualMachineError",
		      "Cannot allocate thread-local info");
      return;
    }
  native_thread_info->name = strdup(name);
  THREAD_setName(THREAD_getCurrent(), native_thread_info->name);

  native_thread_info->op_stack = op_stack_allocate(OPSTACK_SIZE);
  if (NULL == native_thread_info->op_stack)
    {
      free(native_thread_info->name);
      free(native_thread_info);
      throw_Exception(env, "java/lang/VirtualMachineError",
		      "Cannot allocate thread-local op_stack");
      return;
    }

  if ( JNI_FALSE == setup_stackframes(native_thread_info) )
    {
      free(native_thread_info->name);
      free(native_thread_info);
      op_stack_deallocate(native_thread_info->op_stack);
      throw_Exception(env, "java/lang/VirtualMachineError",
		      "Cannot allocate thread-local stack");
      return;
    }
  native_thread_info->thread_id = THREAD_getCurrent();

  THREAD_setJavaInfo(native_thread_info);

  thread = new_object(env, jclass_to_clazzfile(env, Thread));

  /* have to put this off until here. */
  native_thread_info->java_thread = thread;

  STATE(native_thread_info) = STATE_RUNNING;

  if (priority == -1)
    {
      field = (*env)->GetStaticFieldID(env, Thread, "NORM_PRIORITY", "I");
      priority = (*env)->GetStaticIntField(env, Thread, field);
    }

  field = (*env)->GetFieldID(env, Thread, "priority", "I");
  (*env)->SetIntField(env, thread, field, priority);

  /* don't use Thread.PrivateInfo, even if we're using JDK1.1 */
  NSA_SetNativeState(thread, native_thread_info);

  initial_native_frame = push_frame(native_thread_info, 0);

  OPSTACK_TOP(initial_native_frame) = OPSTACK(initial_native_frame)->stack_top;
  THISPTR(initial_native_frame) = NULL;
#ifdef DEBUG
  METHODNAME(initial_native_frame) = strdup("<initial native frame>");
#endif

  initial_native_frame->flags |= FRAME_NATIVE;

  thread_name = (*env)->NewStringUTF(env, name);
  (*env)->NewGlobalRef(env, thread_name);

  if (is_primordial)
    {
      threadgroup = new_object(env, jclass_to_clazzfile(env, ThreadGroup));
      method = find_method(env, ThreadGroup, "<init>", "()V");
      initial_native_frame->method = method;
      (*env)->CallVoidMethod(env, threadgroup, method);
      
      if ((*env)->ExceptionOccurred(env))
	{
	  printf ("ThreadGroup.<init> failed.\n");
	  return;
	}
    }

  if (threadgroup)
    method = find_method(env, Thread, "<init>",
			 "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V");
  else
    method = find_method(env, Thread, "<init>",
			 "(Ljava/lang/String;)V");

  if ((*env)->ExceptionOccurred(env))
    {
      printf ("Thread.<init> lookup failed.\n");
      return;
    }

  initial_native_frame->method = method;
  if (threadgroup)
    (*env)->CallVoidMethod(env, thread, method, threadgroup, thread_name);
  else
    (*env)->CallVoidMethod(env, thread, method, thread_name);

  if ((*env)->ExceptionOccurred(env))
    {
      printf ("Thread.<init> failed.\n");
      return;
    }

  if (is_primordial)
    {
      /* we need to initialize the system class first (if it's necessary) */
      method = find_static_method(env, System,
				  "initializeSystemClass",
				  "()V");
      if (method)
	{
	  initial_native_frame->method = method;
	  (*env)->CallStaticVoidMethod(env, System, method);

	  if ((*env)->ExceptionOccurred(env))
	    {
	      pop_frame(native_thread_info);
	      return;
	    }
	}

    }

  pop_frame(native_thread_info);
}

/* we have to do this one by hand, since all the class initialization
   stuff relies on java.lang.Class already being initialized. */
void
initialize_class_class(JNIEnv *env)
{
  ClazzFile *class_cf = find_class(env, "java/lang/Class");
  class_cf->nesting_level = 1;

  calculate_instance_field_offsets(env, class_cf);
}
