/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. 
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Contributor(s): Ettore Pasquini
 *
 * ***** END GPL LICENSE BLOCK *****
 */
 
/*
compile with: 
gcc intern/ghost/intern/3dcnx/linux/3dcnxplug-lin.c -Iintern/ghost -Wall -lc -shared -fvisibility=hidden -o ../build/linux2/spaceplug.plug
*/

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include "GHOST_Types.h"

#define DEBUG 	1

#ifndef TRUE
#define TRUE	1
#endif
#ifndef FALSE
#define FALSE	0
#endif
#define noErr	0
#define XHigh32( Value )        (((Value)>>16)&0x0000FFFF)
#define XLow32( Value )         ((Value)&0x0000FFFF)

// copied from intern/GHOST_SystemX11.h
typedef struct NDOFPlatformInfo {
	Display *display;
	Window window;
	volatile GHOST_TEventNDOFData *currValues;
} NDOFPlatformInfo;

// pointer to the static struct in ndof manager
static NDOFPlatformInfo *ndof_stat = NULL;

Atom g_magellan_motion;         // MotionEvent
Atom g_magellan_cmd;            // CommandEvent
//Atom g_magellan_btn_press;  	// ButtonPressEvent: disabled for now
//Atom g_magellan_btn_release;	// ButtonReleaseEvent: disabled for now
Window g_magellan_wnd = InputFocus;	// Magellan Driver Window

enum _CommandMessages_ { 
	NoCommandMessage, 
	CommandMessageApplicationWindow = 27695, 
	CommandMessageApplicationSensitivity
};

// ----------------------------------------------------------------------------
// Utility functions
// ----------------------------------------------------------------------------

static int magellanErrorHandler(Display *display, XErrorEvent *err)
{
	char buf[128];
	
	switch (err->error_code) {
		case BadWindow : 
			break;

		default : 
			XGetErrorText(display, err->error_code, buf, sizeof(buf));
			fprintf(stderr, "3Dconnexion blender plugin error: %s\n", buf);
			break;
	};
	return noErr;
}

static int magellanSetWindow(Display *display, Window window)
{
	XTextProperty magellan_wndname;
	XEvent cmd_msg;
	Atom actual_type;
	int actual_format;
	unsigned long n_items, rem_bytes;
	unsigned char *prop_ret;
	Window root;
	int (*app_error_handler)(Display *, XErrorEvent *);
	int err = 1;

	app_error_handler = XSetErrorHandler(magellanErrorHandler);
 
	// Read the window of the Magellan Driver
	root = RootWindow(display, DefaultScreen(display));

	#ifdef DEBUG
	fprintf(stderr, "Magellan Root Window: %#x\n", (unsigned)root);
	#endif

	prop_ret = NULL;
	XGetWindowProperty(display, root, g_magellan_cmd, 0, 1, FALSE,
		AnyPropertyType, &actual_type, &actual_format, &n_items,
		&rem_bytes, &prop_ret);

	g_magellan_wnd = InputFocus;
	if (prop_ret != NULL) {
		g_magellan_wnd = *(Window *) prop_ret;
		XFree(prop_ret);

		#ifdef DEBUG
		fprintf(stderr, "Magellan Driver Window: %#x\n", 
		        (unsigned)g_magellan_wnd);
		#endif

		if (XGetWMName(display, g_magellan_wnd, &magellan_wndname) != 0) {
			#ifdef DEBUG
			fprintf(stderr, "Magellan Driver Window Name: %s\n", 
			        magellan_wndname.value);
			#endif

			if (strcmp((char *)"Magellan Window", 
			           (char *)magellan_wndname.value) == 0) 
			{
				//Send the application window to the Magellan X Window Driver
				cmd_msg.type = ClientMessage;
				cmd_msg.xclient.format = 16;
				cmd_msg.xclient.send_event = FALSE;
				cmd_msg.xclient.display = display;
				cmd_msg.xclient.window = g_magellan_wnd;
				cmd_msg.xclient.message_type = g_magellan_cmd;
				cmd_msg.xclient.data.s[0] = (short) XHigh32(window);
				cmd_msg.xclient.data.s[1] = (short) XLow32(window);
				cmd_msg.xclient.data.s[2] = CommandMessageApplicationWindow;
				
				// XSendEvent returns 0 if there's an error
				err = (XSendEvent(display,g_magellan_wnd,0,0,&cmd_msg) == 0);
				XFlush(display);
			}
		}
	}

	XSetErrorHandler(app_error_handler);
	return err;
}


// ---------------------------------------------------------------------------
// Exported functions called by GHOST
// ----------------------------------------------------------------------------

__attribute__ ((visibility("default"))) 
int ndofInit()
{
	return 1;
}

__attribute__ ((visibility("default"))) 
void* ndofOpen(void *platform_data)
{
	int err;
	Display *display;
	
	ndof_stat = (NDOFPlatformInfo *)platform_data; 
	display = ndof_stat->display;
	
	// define our event types
	g_magellan_motion      = XInternAtom(display, "MotionEvent", TRUE);
	//g_magellan_btn_press   = XInternAtom(display, "ButtonPressEvent", TRUE);
	//g_magellan_btn_release = XInternAtom(display, "ButtonReleaseEvent",TRUE);
	g_magellan_cmd         = XInternAtom(display, "CommandEvent", TRUE);

#ifdef DEBUG
	fprintf(stderr, "X Display: %s\n", XDisplayName((char *)NULL));
	fprintf(stderr, "Magellan App Window: %#x\n", (unsigned)ndof_stat->window);
	fprintf(stderr, "Magellan Motion Event ID=%ld\n", g_magellan_motion);
	fprintf(stderr, "Magellan Command Event ID=%ld\n", g_magellan_cmd);
	//fprintf(stderr, "Magellan Btn Press Event ID=%d\n", g_magellan_btn_press);
	//fprintf(stderr, "g_magellan_btn_release = %d \n", g_magellan_btn_release);
#endif

	// determine if the driver is alive
	err = (g_magellan_motion == 0 || g_magellan_cmd == 0);
		  //|| g_magellan_btn_press == 0 && g_magellan_btn_release == 0);

	if (err || magellanSetWindow(display, ndof_stat->window) != noErr) {
	   fprintf(stderr, "No 3Dconnexion driver is running.\n");
	   return NULL;
	};

	return ndof_stat;
}

__attribute__ ((visibility("default"))) 
void ndofShutdown(void* deviceHandle)
{
	fprintf(stderr, "3Dconnexion blender plugin: shutdown\n");
}
