/* -*- Mode: C; c-file-style: "gnu" -*-
   common.c -- common methods for the awt cruft.
   Created: Chris Toshok <toshok@hungry.com>, 9-Mar-1998.
 */
/*
  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) 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
*/
#include "config.h"

#include "jni.h"
#include "interp.h"
#include "native-threads.h"
#include "ClazzFile.h"
#include "log.h"
#include <Xm/Xm.h>
#include <assert.h>
#include <stdlib.h>

#include "common.h"

#ifdef FOR_MOZILLA
#include "prmem.h"
#endif

XtAppContext _awt_appContext;
Widget _awt_toplevelWidget;
Display *_awt_Display;
Screen *_awt_Screen;

jboolean awt_initialized = JNI_FALSE;

HMonitor _awt_Monitor = NULL;

void
initialize_awt_with_stuff(Widget toplevel_widget,
			  XtAppContext app_context)
{
  if (_awt_Monitor == NULL)
    _awt_Monitor = MONITOR_create();

  if (!awt_initialized)
    {
      awt_initialized = JNI_TRUE;
      
      LOCK_AWT();

      _awt_appContext = app_context;
      _awt_toplevelWidget = toplevel_widget;

      _awt_Display = XtDisplay(_awt_toplevelWidget);
      _awt_Screen = XtScreen(_awt_toplevelWidget);
      
      UNLOCK_AWT();
    }
}

void
initialize_awt(void)
{
  int fake_argc = 1;
  char **fake_argv;

  fake_argv = malloc(sizeof(char*) * 1);
  fake_argv[0] = "japhar";

  JAVARLOG0(AWT_LOG, 1, "sun.awt.motif/initialize_awt() start\n");

#ifndef THREAD_SAFE_MOTIF
  /* race condition here... hope everything's going to be ok. */
  if (_awt_Monitor == NULL)
    _awt_Monitor = MONITOR_create();
#endif

  if (!awt_initialized)
    {
      LOCK_AWT();
      awt_initialized = JNI_TRUE;
      
      _awt_toplevelWidget  = XtOpenApplication(&_awt_appContext,
					       "AWTapp",
					       NULL, 0, &fake_argc,
					       fake_argv,
					       NULL,
					       applicationShellWidgetClass,
					       NULL, 0);

      _awt_Display = XtDisplay(_awt_toplevelWidget);
      _awt_Screen = XtScreen(_awt_toplevelWidget);

      UNLOCK_AWT();
    }
}

Widget
get_component_widget(JNIEnv *env,
		     jobject component_peer)
{
  return (Widget)NSA_GetNativeState(component_peer);
}

void
set_component_widget(JNIEnv *env,
		     jobject component_peer,
		     Widget widget)
{
  NSA_SetNativeState(component_peer, widget);
}

GraphicsData*
get_graphics_data(JNIEnv *env,
		  jobject graphics)
{
  return (GraphicsData*)NSA_GetNativeState(graphics);
}

void
set_graphics_data(JNIEnv *env,
		  jobject graphics,
		  GraphicsData* data)
{
  NSA_SetNativeState(graphics, data);
}

Widget
get_menubar_widget(JNIEnv *env,
		   jobject menubar_peer)
{
  return (Widget)NSA_GetNativeState(menubar_peer);
}

void
set_menubar_widget(JNIEnv *env,
		   jobject menubar_peer,
		   Widget widget)
{
  NSA_SetNativeState(menubar_peer, widget);
}

Widget
get_menuitem_widget(JNIEnv *env,
		    jobject menuitem_peer)
{
  return (Widget)NSA_GetNativeState(menuitem_peer);
}

void
set_menuitem_widget(JNIEnv *env,
		    jobject menuitem_peer,
		    Widget widget)
{
  NSA_SetNativeState(menuitem_peer, widget);
}

char *
get_menuitem_label(JNIEnv *env,
		   jobject menuitem_peer)
{
  jclass peer_class, menuitem_class;
  jfieldID target_field;
  jobject target;
  jfieldID label_field;
  jstring label;
  const jbyte *label_bytes;
  char *ret_val;

  peer_class = (*env)->FindClass(env, "sun/awt/motif/MMenuItemPeer");
  menuitem_class = (*env)->FindClass(env, "java/awt/MenuItem");

  target_field = (*env)->GetFieldID(env, peer_class, "target", "Ljava/awt/MenuItem;");
  label_field = (*env)->GetFieldID(env, menuitem_class, "label", "Ljava/lang/String;");

  target = (*env)->GetObjectField(env, menuitem_peer, target_field);
  label = (*env)->GetObjectField(env, target, label_field);

  label_bytes = (*env)->GetStringUTFChars(env, label, NULL);

  ret_val = strdup((const char*)label_bytes);

  (*env)->ReleaseStringUTFChars(env, label, label_bytes);

  return ret_val;
}

char *
get_checkbox_label(JNIEnv *env,
		   jobject checkbox_peer)
{
  jclass peer_class, checkbox_class;
  jfieldID target_field;
  jobject target;
  jfieldID label_field;
  jstring label;
  const jbyte *label_bytes;
  char *ret_val;

  peer_class = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  checkbox_class = (*env)->FindClass(env, "java/awt/Checkbox");

  target_field = (*env)->GetFieldID(env, peer_class, "target", "Ljava/awt/Component;");
  label_field = (*env)->GetFieldID(env, checkbox_class, "label", "Ljava/lang/String;");

  target = (*env)->GetObjectField(env, checkbox_peer, target_field);
  label = (*env)->GetObjectField(env, target, label_field);

  label_bytes = (*env)->GetStringUTFChars(env, label, NULL);

  ret_val = strdup((const char*)label_bytes);

  (*env)->ReleaseStringUTFChars(env, label, label_bytes);

  return ret_val;
}


static void
send_mouse_event(JNIEnv *env,
		 Widget w,
		 XEvent *event,
		 jobject peer,
		 jint id)
{
  jclass mouseEventClass = (*env)->FindClass(env, "java/awt/event/MouseEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls,
					     "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, mouseEventClass,
			     "<init>", "(Ljava/awt/Component;IJIIIIZ)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, mouseEventClass,
				ctor, 
				/* 1 */ target,
				/* 2 */ id, /* event_id */
				/* 3 */ (jlong)event->xmotion.time, /* when */
				/* 4 */ 0, /* XXX ??? */
				/* 5 */ 0, /* XXX modifiers */
				/* 6 */ event->xmotion.x,
				/* 7 */ event->xmotion.y,
				/* 8 */ JNI_FALSE /* popupTrigger */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
					"postEvent",
					"(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_button_press_event(JNIEnv *env,
			Widget w,
			XEvent *event,
			jobject peer)
{
  send_mouse_event(env, w, event, peer, 501);
}

static void
send_button_release_event(JNIEnv *env,
			  Widget w,
			  XEvent *event,
			  jobject peer)
{
  send_mouse_event(env, w, event, peer, 502);
}

static void
send_mouse_dragged_event(JNIEnv *env,
			 Widget w,
			 XEvent *event,
			 jobject peer)
{
  send_mouse_event(env, w, event, peer, 506);
}

static void
send_mouse_moved_event(JNIEnv *env,
		       Widget w,
		       XEvent *event,
		       jobject peer)
{
  send_mouse_event(env, w, event, peer, 503);
}

static void
send_component_event(JNIEnv *env,
		     jobject peer,
		     int id)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jclass componentEventClass = (*env)->FindClass(env, "java/awt/event/ComponentEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, componentEventClass,
			     "<init>", "(Ljava/awt/Component;I)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, componentEventClass,
				ctor, 
				/* 1 */ target,
				/* 2 */ id /* event_id */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
					"postEvent",
					"(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_component_resized_event(JNIEnv *env,
			     Widget w,
			     XEvent *event,
			     jobject peer)
{
  send_component_event(env, peer, 101);
}

static void
send_component_moved_event(JNIEnv *env,
			   Widget w,
			   XEvent *event,
			   jobject peer)
{
  send_component_event(env, peer, 100);
}

static void
send_focus_event(JNIEnv *env,
		 jobject peer,
		 int id)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;
  jobject event_obj;
  jmethodID ctor;
  jclass focusEventClass = (*env)->FindClass(env, "java/awt/event/FocusEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  ctor = (*env)->GetMethodID(env, focusEventClass,
			     "<init>", "(Ljava/awt/Component;I)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, focusEventClass,
				ctor, 
				/* 1 */ target,
				/* 2 */ id /* event_id */);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
					"postEvent",
					"(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

static void
send_focus_in_event(JNIEnv *env,
		    Widget w,
		    XEvent *event,
		    jobject peer)
{
  send_focus_event(env, peer, 1004);
}

static void
send_focus_out_event(JNIEnv *env,
		     Widget w,
		     XEvent *event,
		     jobject peer)
{
  send_focus_event(env, peer, 1005);
}

void
send_paint_event(JNIEnv *env,
		 Widget w,
		 XEvent *event,
		 jobject peer)
{
  jclass peer_cls = (*env)->FindClass(env, "sun/awt/motif/MComponentPeer");
  jfieldID target_field = (*env)->GetFieldID(env, peer_cls, "target", "Ljava/awt/Component;");
  jobject target;

  jclass rect_cls;
  jmethodID rect_ctor;
  jobject rect;

  jobject event_obj;
  jmethodID ctor;
  jint id = 801; /* XXX we should get it from the static final field. */
  jclass paintEventClass = (*env)->FindClass(env, "java/awt/event/PaintEvent");
  jclass toolkit_class = (*env)->FindClass(env, "sun/awt/motif/MToolkit");
  jmethodID postEvent;

  rect_cls = (*env)->FindClass(env, "java/awt/Rectangle");
  rect_ctor = (*env)->GetMethodID(env, rect_cls, "<init>", "(IIII)V");
  rect = (*env)->NewObject(env, rect_cls, rect_ctor,
			   event->xexpose.x, event->xexpose.y,
			   event->xexpose.width, event->xexpose.height);

  ctor = (*env)->GetMethodID(env, paintEventClass, "<init>",
			     "(Ljava/awt/Component;ILjava/awt/Rectangle;)V");

  target = (*env)->GetObjectField(env, peer, target_field);

  event_obj = (*env)->NewObject(env, paintEventClass,
				ctor, 
				/* 1 */ target,
				/* 2 */ id, /* event_id */
				/* 3 */ rect);

  postEvent = (*env)->GetStaticMethodID(env, toolkit_class,
					"postEvent",
					"(Ljava/awt/AWTEvent;)V");

  (*env)->CallStaticVoidMethod(env, toolkit_class, postEvent, event_obj);
}

#ifdef FOR_MOZILLA
typedef struct JapharEventType {
  PLEvent e;
  /* japhar specific stuff */
  Widget w;
  XEvent *xevent;
  jobject peer;
} JapharEventType;

static void
japhar_event_handler(JapharEventType *event)
{
  JNIEnv *env = THREAD_getEnv();
  jobject peer = event->peer;
  XEvent *xevent = event->xevent;
  Widget w = event->w;

  printf ("in japhar_event_handler\n");

  switch (xevent->type)
    {
    case ButtonPress:
      send_button_press_event(env, w, xevent, peer);
      break;
    case ButtonRelease:
      send_button_release_event(env, w, xevent, peer);
      break;
    case MotionNotify:
      if (xevent->xmotion.state
	  & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))
	send_mouse_dragged_event(env, w, xevent, peer);
      else
	send_mouse_moved_event(env, w, xevent, peer);
      break;
    case FocusIn:
      JAVARLOG0 (AWT_LOG, 2, "FocusIn\n");
      break;
    case FocusOut:
      JAVARLOG0 (AWT_LOG, 2, "FocusOut\n");
      break;
    case EnterNotify:
      JAVARLOG0 (AWT_LOG, 2, "EnterNotify\n");
      break;
    case LeaveNotify:
      JAVARLOG0 (AWT_LOG, 2, "LeaveNotify\n");
      break;
    case Expose:
      send_paint_event(env, w, xevent, peer);
      break;
    }
}
static void
japhar_event_destructor(JapharEventType *event)
{
  free(event->xevent);
  free(event);
}

static void
component_event_handler(Widget w,
			XtPointer closure,
			XEvent *xevent,
			Boolean *cont)
{
  XEvent *event_copy = (XEvent*)malloc(sizeof(XEvent));
  JapharEventType *event;
  jobject peer = (jobject)closure;

  printf ("in component_event_handler\n");

  memcpy(event_copy, xevent, sizeof(XEvent));

  PL_ENTER_EVENT_QUEUE_MONITOR(_awt_eventQueue);

  event = PR_NEW(JapharEventType);
  if (event == NULL) goto done;

  PL_InitEvent((PLEvent*)event, NULL,
	       (PLHandleEventProc)japhar_event_handler,
	       (PLDestroyEventProc)japhar_event_destructor);

  event->xevent = event_copy;
  event->peer = peer;
  event->w = w;

  PL_PostEvent(_awt_eventQueue, &event->e);

 done:
  PL_EXIT_EVENT_QUEUE_MONITOR(_awt_eventQueue);

  *cont = False;
}
#else
static void
component_event_handler(Widget w,
			XtPointer closure,
			XEvent *event,
			Boolean *cont)
{
  JNIEnv *env = THREAD_getEnv();
  jobject peer = (jobject)closure;

  switch (event->type)
    {
    case ButtonPress:
      send_button_press_event(env, w, event, peer);
      *cont = False;
      break;
    case ButtonRelease:
      send_button_release_event(env, w, event, peer);
      *cont = False;
      break;
    case MotionNotify:
      if (event->xmotion.state
	  & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))
	send_mouse_dragged_event(env, w, event, peer);
      else
	send_mouse_moved_event(env, w, event, peer);
      *cont = False;
      break;
    case ConfigureNotify:
      {
	printf ("sending component moved event\n");
	send_component_moved_event(env, w, event, peer);
	printf ("sending component resized event\n");
	send_component_resized_event(env, w, event, peer);
	break;
      }
    case FocusIn:
      JAVARLOG0 (AWT_LOG, 2, "FocusIn\n");
      send_focus_in_event(env, w, event, peer);
      break;
    case FocusOut:
      JAVARLOG0 (AWT_LOG, 2, "FocusOut\n");
      send_focus_out_event(env, w, event, peer);
      break;
    case EnterNotify:
      JAVARLOG0 (AWT_LOG, 2, "EnterNotify\n");
      break;
    case LeaveNotify:
      JAVARLOG0 (AWT_LOG, 2, "LeaveNotify\n");
      break;
    case Expose:
    case GraphicsExpose:
      printf ("expose event\n");
      send_paint_event(env, w, event, peer);
      *cont = False;
      break;
    }
}
#endif

void
add_component_event_handler(Widget w, jobject peer)
{
  XtInsertEventHandler(w,
		       StructureNotifyMask                |
		       ButtonPressMask     		  |
		       ButtonReleaseMask   		  |
		       ButtonMotionMask    		  |
		       PointerMotionMask   		  |
		       FocusChangeMask     		  |
		       EnterWindowMask     		  |
		       LeaveWindowMask,
		       False, component_event_handler, peer, 0);
}

void
add_canvas_exposure_handler(Widget w, jobject peer)
{
  XtInsertEventHandler(w,
		       ExposureMask,
		       False, component_event_handler, peer, 0);
}

void
inherit_background_from(Widget parent,
			Widget child)
{
  Pixel bg_color;

  XtVaGetValues(parent,
		XmNbackground, &bg_color,
		NULL);

  XmChangeColor(child,
		bg_color);
}

Pixel
jcolor_to_pixel(JNIEnv *env, jobject color)
{
  jclass color_cls = (*env)->GetObjectClass(env, color);
  jmethodID getRed = (*env)->GetMethodID(env, color_cls, "getRed", "()I");
  jmethodID getGreen = (*env)->GetMethodID(env, color_cls, "getGreen", "()I");
  jmethodID getBlue = (*env)->GetMethodID(env, color_cls, "getBlue", "()I");
  jint red, green, blue;
  XColor col;

  red = (*env)->CallIntMethod(env, color, getRed);
  green = (*env)->CallIntMethod(env, color, getGreen);
  blue = (*env)->CallIntMethod(env, color, getBlue);

  col.red = (red << 8) | red;
  col.green = (green << 8) | red;
  col.blue = (blue << 8) | red;
  col.flags = DoRed | DoGreen | DoBlue;

  LOCK_AWT();
  XAllocColor(_awt_Display, DefaultColormapOfScreen(_awt_Screen),
	      &col);
  UNLOCK_AWT();

  return col.pixel;
}
