/*
 * ButtonBox - a program launcher
 *
 * Copyright (C) 2000 Paul Collins
 *
 * 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 <stdio.h>
#include <stdlib.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>

#include "assertions.h"
#include "button.h"
#include "configuration.h"
#include "colour.h"
#include "dispatcher.h"



Window create_button_box_window (Display* d);

void create_subwindows (Dispatcher* dispatcher, Window parent);

Bool apply_shape_to_window (Display* d, Window w);

Bool get_rectangles_for_children (Display* display, Window window,
				  XRectangle** rectangles_return, int* nrectangles_return);

Bool get_rectangle_of_window (Display* d, Window w, XRectangle* r);



char* config_file_location = "ButtonBox.ad";


Bool
main_window_proc (Dispatcher* dispatcher, XEvent* event, void* data)
{
	L("main_window_proc: called for window 0x%08x\n", event->xany.window);
	switch (event->xany.type) {
	case Expose:
		L("main_window_proc: processing Expose event on window 0x%08x\n",
		  event->xexpose.window);
		XClearArea (dispatcher_get_display (dispatcher),
			    event->xexpose.window,
			    event->xexpose.x, event->xexpose.y,
			    event->xexpose.width, event->xexpose.height,
			    False);
		break;
	case EnterNotify:
		L("main_window_proc: processing EnterNotify event on window 0x%08x\n",
		  event->xcrossing.window);
		L("main_window_proc: raising window 0x%08x\n",
		  event->xcrossing.window);
		/* XXX: There are probably cases where it is
                   unnecessary to call XRaiseWindow, but I dont
                   understand crossing events well enough to detect
                   them yet. */
		XRaiseWindow (dispatcher_get_display (dispatcher),
			      event->xcrossing.window);
		break;
	case LeaveNotify:
		L("main_window_proc: processing LeaveNotify event on window 0x%08x\n",
		  event->xcrossing.window);
		if (event->xcrossing.detail != NotifyInferior) {
			L("main_window_proc: lowering window 0x%08x\n",
			  event->xcrossing.window);
			XLowerWindow (dispatcher_get_display (dispatcher),
				      event->xcrossing.window);
		}
		break;
	default:
		return False;
		break;
	}
	return True;
}



int
main (int argc, char** argv)
{
	Display* display = NULL;
	Dispatcher* dispatcher = NULL;
	Window main_window = None;
	
	display = XOpenDisplay (NULL);
	if (!display) {
		L ("XOpenDisplay: failed.\n");
		exit (EXIT_FAILURE);
	}
	if (argc == 2) {
		config_file_location = argv[1];
		L ("main: setting config_file_location = \"%s\"\n",
		   config_file_location);
	}
	colour_init (display);
	dispatcher = dispatcher_new (display);
	I (dispatcher != NULL);
	dispatcher_reap_zombies (dispatcher, True);
	main_window = create_button_box_window (display);
	if (main_window != None) {
		create_subwindows (dispatcher, main_window);
	}
	apply_shape_to_window (display, main_window);
	XMapWindow (display, main_window);
	
	dispatcher_register (dispatcher, main_window, main_window_proc, NULL);

	dispatcher_run_loop (dispatcher);
	
	exit (EXIT_SUCCESS);
}



Window
create_button_box_window (Display* d)
{
	Window w = None;
	XSetWindowAttributes attributes;
	
	attributes.override_redirect = True;
	attributes.background_pixel = WhitePixel (d, DefaultScreen (d));
	attributes.event_mask = (ExposureMask
				 | ButtonPressMask
				 | ButtonReleaseMask
				 | EnterWindowMask
				 | LeaveWindowMask);
	w = XCreateWindow (d, RootWindowOfScreen (DefaultScreenOfDisplay (d)),
			   0, 0, 100, 100, /* Adjusted later. */
			   0,
			   CopyFromParent, InputOutput,
			   CopyFromParent,
			   (CWOverrideRedirect
			    | CWBackPixel
			    | CWEventMask),
			   &attributes);
	if (w != None) {
		L ("create_button_box_window: XCreateWindow: successful\n");
		XStoreName (d, w, "buttonbox");
		XLowerWindow (d, w);
	} else {
		L ("create_button_box_window: XCreateWindow: returned None\n");
	}
	return w;
}



void
create_subwindows (Dispatcher* dispatcher, Window parent)
{
	Display* d = NULL;
	Button* b = NULL;
	int widest = 0;
	int narrowest = 0;
	int last_y = 1;
	configuration* c = NULL;
	button_info* current = NULL;
	XWindowAttributes attributes;

	d = dispatcher_get_display (dispatcher);
	I (d != NULL);
	c = configuration_load_from_file (config_file_location);
	I (c != NULL);
	current = c->buttons;
	while (current != NULL) {
		Window w = None;
		b = button_new (dispatcher, parent, current);
		w = button_get_window (b);
		XGetWindowAttributes (d, w, &attributes);
		XMoveWindow (d, w, 1, last_y);
		last_y = attributes.height + (attributes.border_width * 3) + last_y;
		if (widest < attributes.width)
			widest = attributes.width;
		if ((narrowest > attributes.width)
		    || (narrowest == 0))
			narrowest = attributes.width;
		current = current->bi_next;
	}
	configuration_free (c);
	XResizeWindow (d, parent, widest + 4, last_y + (2 * attributes.border_width));
}



Bool
apply_shape_to_window (Display* d, Window w)
{
	XRectangle * rectangles = NULL;
	int nrectangles = 0;
	Bool success = False;
	
	success = get_rectangles_for_children (d, w, &rectangles, &nrectangles);
	if (!success) {
		L ("apply_shape_to_window: get_rectangles_for_children: failed\n");
		return False;
	}
	XShapeCombineRectangles (d, w, ShapeBounding, 0, 0,
				 rectangles, nrectangles, ShapeSet, Unsorted);
	free (rectangles);
	return True;
}



Bool
get_rectangles_for_children (Display* d, Window window,
			     XRectangle** rectangles_return, int* nrectangles_return)
{
	XRectangle* rectangles = NULL;
	int nrectangles = 0;
	int i = 0;

	/* Returns for XQueryTree: */
	Status status = 0;
	Window root_return = None;
	Window parent_return = None;
	Window* children_return = NULL;
	int nchildren_return = 0;
	
	I (d != NULL);
	status = XQueryTree (d, window, &root_return, &parent_return,
			     &children_return, &nchildren_return);
	if (status == 0) {
		L ("get_rectangles_for_children: XQueryTree failed, returning False\n");
		return False;
	}
	if (nchildren_return == 0) {
		L ("get_rectangles_for_children: window has no children, returning False\n");
		return False;
	}
	L ("get_rectangles_for_children: XQueryTree: got %d children\n", nchildren_return);
	rectangles = malloc (nchildren_return * sizeof (XRectangle));
	I (rectangles != 0);
	for (i = 0; i < nchildren_return; ++i) {
		if (get_rectangle_of_window (d, children_return[i], &rectangles[i]) == False) {
			L ("get_rectangles_for_children: "
			   "get_rectangle_of_window: returned False\n");
		} else {
			++nrectangles;
		}
	}
	XFree (children_return);
	*rectangles_return = rectangles;
	*nrectangles_return = nrectangles;

	return True;
}



Bool
get_rectangle_of_window (Display* d, Window w, XRectangle* r)
{
	XWindowAttributes attributes;
	Status status = 0;
	
	I (d != NULL);
	I (w != None);
	I (r != NULL);
	
	status = XGetWindowAttributes (d, w, &attributes);
	if (!status) {
		L ("get_rectangle_of_window: XGetWindowAttributes failed for XID 0x%08x\n", w);
		return False;
	}
	r->x = attributes.x;
	r->y = attributes.y;
	r->width = attributes.width;
	r->height = attributes.height;
	
	r->x -= attributes.border_width;
	r->y -= attributes.border_width;
	r->width += attributes.border_width * 4;
	r->height += attributes.border_width * 4;

	return True;
}


