/*****************************************************************************
   xdwapi.c - implementation of the xdesktopwaves API

   Copyright (C) 2004 Oliver Hamann (olha@users.sourceforge.net)

   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
*****************************************************************************/

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include "xdwapi.h"


typedef struct {
	Display * display;
	Window root;
	Atom XDESKTOPWAVES_WINDOW_ID;
	Atom XDESKTOPWAVES_COMMAND_MESSAGE;
	Atom XDESKTOPWAVES_WINDOW_INTERACTION;
} xdwApiContextImp;


xdwApiContext xdwApiOpen(Display * display)
{
	xdwApiContextImp * imp;

	imp=(xdwApiContextImp*)malloc(sizeof(xdwApiContextImp));
	imp->display=display;
	imp->root=DefaultRootWindow(display);
	imp->XDESKTOPWAVES_WINDOW_ID=XInternAtom(
		display,"XDESKTOPWAVES_WINDOW_ID",False
	);
	imp->XDESKTOPWAVES_COMMAND_MESSAGE=XInternAtom(
		display,"XDESKTOPWAVES_COMMAND_MESSAGE",False
	);
	imp->XDESKTOPWAVES_WINDOW_INTERACTION=XInternAtom(
		display,"XDESKTOPWAVES_WINDOW_INTERACTION",False
	);
	return (xdwApiContext)imp;
}


void xdwApiClose(xdwApiContext context)
{
	free((xdwApiContextImp*)context);
}


volatile Bool xdwApiGotXError;


static int xdwApiTmpXErrHandler(Display * display, XErrorEvent * errorEvent)
{
	xdwApiGotXError=True;
	return 0;
}


static Bool xdwApiSendCommand(xdwApiContext context, int argCount, ...)
{
	xdwApiContextImp * imp;
	int (*originalXErrorHandler)(Display *, XErrorEvent *);
	Atom type;
	Status st;
	Window win;
	XEvent event;
	va_list ap;
	unsigned char * data;
	char * name;
	unsigned long len, rem;
	int i, frmt, ret;

	/* Get window id of xdesktopwaves. */
	imp=(xdwApiContextImp*)context;
	data=NULL;
	ret=XGetWindowProperty(
		imp->display,imp->root,imp->XDESKTOPWAVES_WINDOW_ID,0,1,False,
		XA_WINDOW,&type,&frmt,&len,&rem,&data
	);
	if (ret!=Success || type!=XA_WINDOW || frmt!=32 || len!=1 || rem!=0) {
		if (data) XFree(data);
		return False;
	}
	win=*(Window*)data;
	if (data) XFree(data);
	if (win==None) return False;

	/* Prepare client message to be sent. */
	memset(&event,0,sizeof(event));
	event.xclient.type=ClientMessage;
	event.xclient.display=imp->display;
	event.xclient.window=win;
	event.xclient.message_type=imp->XDESKTOPWAVES_COMMAND_MESSAGE;
	event.xclient.format=16;
	va_start(ap,argCount);
	for (i=0; i<argCount && i<10; i++) {
		event.xclient.data.s[i]=(short)va_arg(ap,int);
	}
	va_end(ap);

	/* Send the message, but check for the window name first. */
	XSync(imp->display,False);
	xdwApiGotXError=False;
	originalXErrorHandler=XSetErrorHandler(xdwApiTmpXErrHandler);
	name=NULL;
	st=XFetchName(imp->display,win,&name);
	if (st && (!name || strcmp(name,"xdesktopwaves")!=0)) st=0;
	if (name) XFree(name);
	if (st) st=XSendEvent(imp->display,win,False,0,&event);
	XSync(imp->display,False);
	XSetErrorHandler(originalXErrorHandler);
	if (xdwApiGotXError) st=0;

	return st ? True : False;
}


Bool xdwApiPing(xdwApiContext context)
{
	return xdwApiSendCommand(context,1,0);
}


Bool xdwApiClear(xdwApiContext context)
{
	return xdwApiSendCommand(context,1,1);
}


Bool xdwApiSetRain(xdwApiContext context, int intensity)
{
	return xdwApiSendCommand(context,2,2,intensity);
}


Bool xdwApiSetStorm(xdwApiContext context, int intensity)
{
	return xdwApiSendCommand(context,2,3,intensity);
}


Bool xdwApiPutRaindrop(xdwApiContext context, int x, int y, int volume)
{
	return xdwApiSendCommand(context,4,4,x,y,volume);
}


static int xdwApiGenGhostId()
{
	static int seed=0;
	int id;

	seed=seed*1375117+3370481;
	id=(getpid()*2873451)^(time(NULL)*11876973)^seed;
	if (!id) id=1;
	return id;
}


int xdwApiPutGhostRect(xdwApiContext context, int milliSecs, int removeId,
                       int x, int y, int width, int height)
{
	int ghostId;
	Bool success;

	ghostId=xdwApiGenGhostId();
	success=xdwApiSendCommand(
		context,
		10,
		5,
		ghostId>>16,
		ghostId&0xffff,
		removeId>>16,
		removeId&0xffff,
		milliSecs,
		x,
		y,
		width,
		height
	);
	return success ? ghostId : 0;
}


int xdwApiPutGhostCircle(xdwApiContext context, int milliSecs, int removeId,
                         int x, int y, int radius)
{
	int ghostId;
	Bool success;

	ghostId=xdwApiGenGhostId();
	success=xdwApiSendCommand(
		context,
		9,
		6,
		ghostId>>16,
		ghostId&0xffff,
		removeId>>16,
		removeId&0xffff,
		milliSecs,
		x,
		y,
		radius
	);
	return success ? ghostId : 0;
}


void xdwApiSetWavesByWindow(xdwApiContext context, Window window,
                            Bool wavesByWindow)
{
	xdwApiContextImp * imp;
	unsigned card32;

	imp=(xdwApiContextImp*)context;
	card32=wavesByWindow ? 1 : 0;
	XChangeProperty(
		imp->display,
		window,
		imp->XDESKTOPWAVES_WINDOW_INTERACTION,
		XA_CARDINAL,
		32,
		PropModeReplace,
		(unsigned char*)&card32,
		1
	);
}


void xdwApiDefaultWavesByWindow(xdwApiContext context, Window window)
{
	xdwApiContextImp * imp;

	imp=(xdwApiContextImp*)context;
	XDeleteProperty(
		imp->display,
		window,
		imp->XDESKTOPWAVES_WINDOW_INTERACTION
	);
}
