#include "nawm.h"
#include "cache.h"
#include <stdlib.h>


typedef struct _cache_entry
{
  cache_key key;
  struct _cache_entry *next;
  cache_data data;
} cache_entry;

static cache_entry *cache=NULL;

/* returns NULL on failure */
static 
cache_entry *cache_lookup(key)
     cache_key key;
{
  cache_entry *ptr;
  for(ptr=cache; ptr && (ptr->key != key); ptr = ptr->next);
  return ptr;
}

static 
cache_insert(key, data)
     cache_key  key;
     cache_data data;
{
  cache_entry *entry = malloc(sizeof(*entry));
  if (!entry) return; /* cache is non-critical */
  entry->key  = key;
  entry->data = data;
  entry->next = cache;
  cache = entry;
}

static 
cache_delete(key)
     cache_key key;
{
  cache_entry **ptr,*tmp;
  for(ptr=&cache;  *ptr && (*ptr)->key != key;  ptr=&(*ptr)->next  );
  tmp = *ptr; /* the linked list free shuffle */
  *ptr=(*ptr)->next;
  free(tmp);
}


static 
cache_update(key,data)
     cache_key  key;
     cache_data data;
{
  cache_entry *entry;
  entry = cache_lookup(key);
  if (!entry)
    cache_insert(key,data);
  else
    entry->data = data;
}

/* interface stuff */
cache_data 
cached_get_data(key)
     cache_key key;
{
  cache_entry *entry = cache_lookup(key);
  if (entry) 
    return entry->data;
  else
    {
      cache_data data = { 0,0 };
      XWindowAttributes attr;
      /* XX have no good way to report errors, consider changing interface */
      if (!XGetWindowAttributes(dpy, key, &attr)) return data ;
      data.x = attr.x;
      data.y = attr.y;
      cache_insert(key,data);
      return data;
    }
}

/* XXX 
   does reparenting mean we have to create/destroy cache entries?
   what do we do with unmapped windows?
   */

update_cache(ev)
     XEvent *ev;
{
  switch (ev->type)
    {
    case MapNotify: 
    case UnmapNotify:
    case CirculateNotify: 
      break;
    case ConfigureNotify: 
      {
	cache_data data;
	data.x = ev->xconfigure.x;
	data.y = ev->xconfigure.y;
	cache_update(ev->xconfigure.window,data);
      }
      break;
    case CreateNotify: 
      {
	cache_data data;
	data.x = ev->xconfigure.x;
	data.y = ev->xconfigure.y;
	cache_update(ev->xconfigure.window,data);
      }
      break;
    case DestroyNotify: 
      cache_delete(ev->xdestroywindow.window);
      break;
    case ReparentNotify: 
      if (ev->xreparent.parent == root) 
	printf("Got reparent on %d to root\n", ev->xreparent.window);
      else
	printf("Got reparent on %d to %d\n", ev->xreparent.window, ev->xreparent.parent);
      break;
    }
}



start_cache()
{
  Window *children;
  unsigned int numchildren, n;
  Window rootret;
  Window parent;
  XWindowAttributes attr;
  cache_data data;

  XSelectInput(dpy, root, SubstructureNotifyMask);
  /* Xsync(dpy,False); */ /* race condition possible i think ? */

  XQueryTree (dpy, root, &rootret, &parent, &children, &numchildren);
  for (n = 0; n < numchildren; n++)
    {
      XGetWindowAttributes(dpy, children[n], &attr);  
      data.x = attr.x;
      data.y = attr.y;
      cache_insert(children[n],data);
    }
  XFree ((char *) children);
}

dump_cache()
{
  cache_entry *ptr;
  printf("\ncache dump\n");
  for (ptr=cache; ptr ; ptr = ptr->next)
    printf("window %10d at x,y %d,%d\n", ptr->key, ptr->data.x, ptr->data.y);
  printf("\n");
}

dump_query()
{
  Window *children;
  unsigned int numchildren, n;
  Window rootret;
  Window parent;
  XWindowAttributes attr;
  cache_data data;

  printf("\nquery dump\n");
  XQueryTree (dpy, root, &rootret, &parent, &children, &numchildren);
  for (n = 0; n < numchildren; n++)
    {
      XGetWindowAttributes(dpy, children[n], &attr);  
      printf("window %10d at x,y %d,%d\n", children[n], attr.x, attr.y);
    }
  XFree ((char *) children);
  printf("\n");
}

cache_iter(func,stuff)
  void (*func)();
  void *stuff;
{
  cache_entry *ptr;
  for (ptr=cache; ptr ; ptr=ptr->next)
    func(ptr->key,&ptr->data,stuff);
}
