/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include "dat.h"
#include "fns.h"

Client	*clients;
Client	*current;

void
setactive(Client *c, int on)
{
/*	dbg("setactive client %x %d", c->window, c->on); */

	if(c->parent == c->screen->root)
		return;

	if(on){
		XUngrabButton(dpy, AnyButton, AnyModifier, c->parent);
		XSetInputFocus(dpy, c->window, RevertToPointerRoot, timestamp());
		if(c->proto & Ptakefocus)
			sendcmessage(c->window, wm_protocols, wm_take_focus, 0, 1);
		cmapfocus(c);
	}else{
		if(c->proto & Plosefocus)
			sendcmessage(c->window, wm_protocols, wm_lose_focus, 0, 1);
		XGrabButton(dpy, AnyButton, AnyModifier, c->parent, False,
			ButtonMask, GrabModeAsync, GrabModeSync, None, None);
	}
	draw_border(c, on);
}

void
draw_border(Client *c, int active)
{
	unsigned long pixel;

	if(active){
		if(c->hold)
			pixel = c->screen->activeholdborder;
		else
			pixel = c->screen->activeborder;
	}else{
		if(c->hold)
			pixel = c->screen->inactiveholdborder;
		else
			pixel = c->screen->inactiveborder;
	}

	if(debug) fprintf(stderr, "draw_border %p pixel %ld active %d hold %d\n", (void*)c, pixel, active, c->hold);
	XSetWindowBackground(dpy, c->parent, pixel);
	XClearWindow(dpy, c->parent);
}

void
active(Client *c)
{
	Client *cc;

	if(c == 0){
		fprintf(stderr, "rio: active(c==0)\n");
		return;
	}
	if(c == current)
		return;
	if(current){
		setactive(current, 0);
		if(current->screen != c->screen)
			cmapnofocus(current->screen);
	}
	setactive(c, 1);
	for(cc = clients; cc; cc = cc->next)
		if(cc->revert == c)
			cc->revert = c->revert;
	c->revert = current;
	while(c->revert && !normal(c->revert))
		c->revert = c->revert->revert;
	current = c;
#ifdef	DEBUG
	if(debug)
		dump_revert();
#endif
}

void
nofocus(void)
{
	static Window w = 0;
	int mask;
	XSetWindowAttributes attr;
	Client *c;

	if(current){
		setactive(current, 0);
		for(c = current->revert; c; c = c->revert)
			if(normal(c)){
				active(c);
				return;
			}
		cmapnofocus(current->screen);
		/* if no candidates to revert to, fall through */
	}
	current = 0;
	if(w == 0){
		mask = CWOverrideRedirect/*|CWColormap*/;
		attr.override_redirect = 1;
		/* attr.colormap = screens[0].def_cmap;*/
		w = XCreateWindow(dpy, screens[0].root, 0, 0, 1, 1, 0,
			0 /*screens[0].depth*/, InputOnly, 	screens[0].vis, mask, &attr);
		XMapWindow(dpy, w);
	}
	XSetInputFocus(dpy, w, RevertToPointerRoot, timestamp());
}

void
top(Client *c)
{
	Client **l, *cc;

	l = &clients;
	for(cc = *l; cc; cc = *l){
		if(cc == c){
			*l = c->next;
			c->next = clients;
			clients = c;
			return;
		}
		l = &cc->next;
	}
	fprintf(stderr, "rio: %p not on client list in top()\n", (void*)c);
}

Client *
getclient(Window w, int create)
{
	Client *c;

	if(w == 0 || getscreen(w))
		return 0;

	for(c = clients; c; c = c->next)
		if(c->window == w || c->parent == w)
			return c;

	if(!create)
		return 0;

	c = (Client *)malloc(sizeof(Client));
	if (!c){
		fprintf(stderr, "rio: Failed to allocate memory\n");
		exit(1);
	}

	memset(c, 0, sizeof(Client));
	c->window = w;
	/* c->parent will be set by the caller */
	c->parent = None;
	c->reparenting = 0;
	c->state = WithdrawnState;
	c->init = 0;
	c->cmap = None;
	c->label = c->class = 0;
	c->revert = 0;
	c->is9term = 0;
	c->hold = 0;
	c->ncmapwins = 0;
	c->cmapwins = 0;
	c->wmcmaps = 0;
	c->next = clients;
	c->virt = virt;
	clients = c;
	return c;
}

void
rmclient(Client *c)
{
	Client *cc;

	for(cc = current; cc && cc->revert; cc = cc->revert)
		if(cc->revert == c)
			cc->revert = cc->revert->revert;

	if(c == clients)
		clients = c->next;
	for(cc = clients; cc && cc->next; cc = cc->next)
		if(cc->next == c)
			cc->next = cc->next->next;

	if(hidden(c))
		unhidec(c, 0);

	if(c->parent != c->screen->root)
		XDestroyWindow(dpy, c->parent);

	c->parent = c->window = None;		/* paranoia */
	if(current == c){
		current = c->revert;
		if(current == 0)
			nofocus();
		else {
			if(current->screen != c->screen)
				cmapnofocus(c->screen);
			setactive(current, 1);
		}
	}
	if(c->ncmapwins != 0){
		XFree((char *)c->cmapwins);
		free((char *)c->wmcmaps);
	}
	if(c->iconname != 0)
		XFree((char*) c->iconname);
	if(c->name != 0)
		XFree((char*) c->name);
	if(c->instance != 0)
		XFree((char*) c->instance);
	if(c->class != 0)
		XFree((char*) c->class);
	memset(c, 0, sizeof(Client));		/* paranoia */
	free(c);
}

#ifdef	DEBUG
void
dump_revert(void)
{
	Client *c;
	int i;

	i = 0;
	for(c = current; c; c = c->revert){
		fprintf(stderr, "%s(%x:%d)", c->label ? c->label : "?", (int)c->window, c->state);
		if(i++ > 100)
			break;
		if(c->revert)
			fprintf(stderr, " -> ");
	}
	if(current == 0)
		fprintf(stderr, "empty");
	fprintf(stderr, "\n");
}

void
dump_clients(void)
{
	Client *c;

	for(c = clients; c; c = c->next)
		fprintf(stderr, "w 0x%x parent 0x%x @ (%d, %d)\n", (int)c->window, (int)c->parent, c->x, c->y);
}
#endif

void
shuffle(int up)
{
	Client **l, *c;

	if(clients == 0 || clients->next == 0)
		return;
	if(!up){
		c = 0;
		/*for(c=clients; c->next; c=c->next) */
		/*	; */
		for(l=&clients; (*l)->next; l=&(*l)->next)
			if ((*l)->state == 1)
				c = *l;
		if (c == 0)
			return;
		XMapRaised(dpy, c->parent);
		top(c);
		active(c);
	}else{
		c = clients;
		for(l=&clients; *l; l=&(*l)->next)
			;
		clients = c->next;
		*l = c;
		c->next = 0;
		XLowerWindow(dpy, c->window);
	}
/*	XMapRaised(dpy, clients->parent); */
/*	top(clients);	 */
/*	active(clients); */
}
