#include "config.h"
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>

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

#include <ft2build.h>
#include <X11/Xft/Xft.h>

#include "xerror.h"
#include "keyboard.h"
#include "pixmap.h"
#include "main.h"
#include "hints.h"
#include "client.h"
#include "mouse.h"
#include "settings.h"
#include "workspaces.h"
#include "stacking.h"
#include "focus.h"

#define DBUG_VERBOSE(x)			//DBUG(x)

/* remember the focused client of each workspace */
/* (always check if the stored client still exists!)*/
Window last_focus[9] = { None, None, None, None, None, None, None, None, None };

Bool wants_focus(Client *c)
{

  //g_return_val_if_fail(client_exists(c), False); 
	
  /* Modal dialogs *always* accept focus */
	if (c->state & STATE_MODAL)
		return True;
	
	if (c->non_focusing)
		return False;

	/* then try ICCCM */
	if (c->wm_protocols & PROTOCOL_WM_TAKE_FOCUS)
		return True;
 
  XWMHints *hints = c->wmhints;
  if (hints && (hints->flags & InputHint))
  	return hints->input;
  
  /* in dubio pro reo */
  return True;
}

Window get_input_focus(void)
{
	Window focus_return;
	int revert_to_return;
	XGetInputFocus(dpy, &focus_return, &revert_to_return);
	return focus_return;
}

void focus_revert_to_root(void)
{

}

#define FOCUS_SET(w) XSetInputFocus(dpy, w, RevertToParent, CurrentTime)
#define FOCUS_RESET() \
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime)
		
void set_input_focus(Window in)
{
	Window out = last_focus[workspace];

	if (in == root)
	{
		XSetInputFocus(dpy, in, RevertToNone, CurrentTime);
		XSync(dpy, False);
	}
	
	set_window_hint(intern_atoms[NET_ACTIVE_WINDOW], in);
	last_focus[workspace] = in;
}




static Bool try_focus(Client * c)
{
	dbg("%s\n", __func__);

	Bool ret = wants_focus(c) && client_is_viewable(c);	
	if(ret)	
		set_input_focus(c->window);
	return ret;		
}



inline static void focus_topmost(void)
{
	int i = client_list_stacking->len;
	dbg("%s\n", __func__);
	while (--i >= 0) 
	{
		if (try_focus(client_of_window(g_array_index(client_list_stacking, Window, i))))
			return;
	}
	dbg("%s: revert to root\n", __func__);
	set_input_focus(root);
}

inline static Bool focus_follows_mouse(void)
{
	Window w = get_mouse_window(dpy, root);	 

	if (w != root)
	{	
		Client *c = client_of_window(w);
		if (c)
			return try_focus(c);
	}

	return False;		
}


void apply_focus_policy(Client * c)
{
	dbg("%s\n", __func__);

	if (c && try_focus(c))
		return;
	
	if ((focus_policy != FOCUS_UNIX) || !focus_follows_mouse())
			focus_topmost();
	else
		set_input_focus(root);
	
}

void focus_workspace(void)
{
	apply_focus_policy(client_of_window(last_focus[workspace]));		
}

void focus_new_client(Client * c)
{
	Client *c2;

	if (focus_new || 
			((c->wmhints && (c->wmhints->flags & XUrgencyHint))) ||
			((c->transientFor != None) && ((c2 = client_of_window(get_input_focus())) && (c2->window == c->transientFor))))
		try_focus(c);
}

void update_focus(void)
{
	Window focus_window = get_input_focus();
	
	GList *l;

	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;
		if (c->decor_state == ACTIVE)
		{
			if (c->window != focus_window)
			  if (c->has_decor) decorate(c, INACTIVE);
		}
		else if (c->window == focus_window)
		{
				if (c->has_decor) decorate(c, ACTIVE);
				apply_raise_policy(c);
		}
	}
}

