/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "pier.h"

/* used for dragging the pier with button2 */
static pier_t	*current_pier	= NULL;
static int	drag_x, drag_y;

/* flag if we use double clicks for launching */
static int	pier_singleclick;

/* do we allow pier moving */
static int	pier_nodragging;

/* handle map requests when we launch apps to dock/swallow */
static int map_request(int pcall, screen_t *screen, XMapRequestEvent *e) {
	XClassHint classhint;
	comtab_t *comtab;
	int ret;

	ret = PLUGIN_OK;

	/* we have to check new windows if we are waiting for a dock/swallowed win */
	if (!LIST_EMPTY(&comtab_list)) {
		if (XGetClassHint(display, e->window, &classhint) == 0)
			return PLUGIN_OK;

		/* see if this is a WM_CLASS we a looking for */
		LIST_FOREACH(comtab, &comtab_list, ct_list)
			if (strcmp(comtab->classhint.res_class, classhint.res_class) == 0
					&& strcmp(comtab->classhint.res_name,
					classhint.res_name) == 0) {
				pier_gotcom(comtab, e);
				ret = PLUGIN_USING;
				goto free1;
			}

free1:
		XFree(classhint.res_name);
		XFree(classhint.res_class);
	}

	return ret;
}

/* x event handler for button presses */
static int button_press(XButtonEvent *e) {
	pier_t *pier;
	Window dumwin;

	/* button 2 drags the pier */
	if (pier_nodragging || e->button != Button2)
		return PLUGIN_OK;

	if (!(pier = pier_findpier(e->window)))
		return PLUGIN_OK;

	current_pier = pier;
	if (!XTranslateCoordinates(display, e->root, pier->window, e->x_root,
			e->y_root, &drag_x, &drag_y, &dumwin)) {
		current_pier = NULL;
		return PLUGIN_OK;
	}
	
	return PLUGIN_OK;
}

/* x event handler for button release */
static int button_release(XButtonEvent *e) {
	static long lasttime;
	static int gotfirst = 0;
	pier_t *pier;

	if (current_pier) {
		current_pier = NULL;
		return PLUGIN_OK;
	} else if (e->button != Button1)
		return PLUGIN_OK;
	else if ((pier = pier_findpier(e->window)) == NULL)
		return PLUGIN_OK;

	if (pier_singleclick) {
		if (e->x >= 0 && e->y >= 0
				&& e->x < pier->width && e->y < pier->height)
			pier_click(pier, e);
	} else {
		if (gotfirst && (e->time - lasttime <= 200)
				&& e->x >= 0 && e->y >= 0
				&& e->x < pier->width && e->y < pier->height) {
			pier_click(pier, e);
			gotfirst = 0;
		} else {
			lasttime = e->time;
			gotfirst = 1;
		}
	}

	return PLUGIN_OK;
}

/* pointer motion x event handler */
static int pointer_motion(XMotionEvent *e) {
	int x, y;

	if (!current_pier)
		return PLUGIN_OK;

	x = e->x_root - drag_x;
	y = e->y_root - drag_y;

	/* constrain movement */
	if (x < 0)
		x = 0;
	else if (x + current_pier->width >= DisplayWidth(display, current_pier->screen))
		x = DisplayWidth(display, current_pier->screen) - current_pier->width;
	if (y < 0)
		y = 0;
	else if (y + current_pier->height >= DisplayHeight(display, current_pier->screen))
		y = DisplayHeight(display, current_pier->screen) - current_pier->height;

	/* move the window */
	current_pier->x = x;
	current_pier->y = y;
	XMoveWindow(display, current_pier->window, x, y);

	return PLUGIN_OK;
}

/* entry point for X11 events */
int pier_xevent_handler(XEvent *e) {
	switch (e->type) {
	case ButtonPress:
		button_press(&e->xbutton);
		break;
	case ButtonRelease:
		button_release(&e->xbutton);
		break;
	case MotionNotify:
		pointer_motion(&e->xmotion);
		break;
	}

	return PLUGIN_OK;
}

/* parse params and such */
int pier_init() {
	pixmap_t *tile;
	int size;

	/* get basic parameters */
	OPTIONAL_PARAM(&plugin_this->params, "singleclick", bool, pier_singleclick,  0);
	OPTIONAL_PARAM(&plugin_this->params, "nodragging", bool, pier_nodragging, 0);
	OPTIONAL_PARAM(&plugin_this->params, "tile_pixmap", pixmap, tile, NULL);
	OPTIONAL_PARAM(&plugin_this->params, "tile_size", int, size, 64);
	if (size <= 0 || size > 128)
		size = 64;

	if (pier2_init(size, tile) == -1)
		return PLUGIN_UNLOAD;

	parseparams();
	return PLUGIN_OK;
}

/* shutdown the pier */
void shutdown() {
	pier_shutdown();
}

/* realize the pier; get ready for usage */
int pier_start() {
	/* register callbacks */
	plugin_callback_add(plugin_this, PCALL_MAP_REQUEST, map_request);

	if (pier_realize_all() == -1)
		return PLUGIN_UNLOAD;
	return PLUGIN_OK;
}
