#include "BadWM.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>

void make_new_client(Window w, ScreenInfo *s) {
	Client *c;
	XWindowAttributes attr;
	long dummy;
	XWMHints *hints;
	char *name;

	XGrabServer(dpy);

	c = (Client *)malloc(sizeof(Client));
	if (!c) {
		return;
	}
	/* We do this first of all as a test to see if the window actually
	 * still exists by the time we've got to create a client structure
	 * for it (sometimes they vanish too quickly or something, and lots
	 * of pain ensues). */
	initialising = w;
	XFetchName(dpy, w, &name);
	/* If 'initialising' is now set to None, that means doing the
	 * XFetchName raised BadWindow - the window has been removed before
	 * we got a chance to grab the server. */
	if (initialising == None) {
		free(c);
		XSync(dpy, False);
		XUngrabServer(dpy);
		return;
	}
	initialising = None;
	c->next = head_client;
	head_client = c;

	/* initialise(c, w); */
	c->screen = s;
	c->window = w;
	c->ignore_unmap = 0;

	/* Jon Perkin reported a crash wish an app called 'sunpci' which we
	 * traced to getting divide-by-zeros because it sets PResizeInc
	 * but then has increments as 0.  So we check for 0s here and set them
	 * to sensible defaults. */
	c->size = XAllocSizeHints();
	if (c->size->width_inc == 0)
		c->size->width_inc = 1;
	if (c->size->height_inc == 0)
		c->size->height_inc = 1;
	XGetWMNormalHints(dpy, c->window, c->size, &dummy);

	XGetWindowAttributes(dpy, c->window, &attr);

	c->x = attr.x;
	c->y = attr.y;
	c->width = attr.width;
	c->height = attr.height;
	c->border = 1;
	c->oldw = c->oldh = 0;

	if (name) {
		XFree(name);
	}

	c->vdesk = vdesk;

	/* c->size = XAllocSizeHints();
	XGetWMNormalHints(dpy, c->window, c->size, &dummy); */

	if (attr.map_state == IsViewable) {
		c->ignore_unmap++;
	} else {
           /* Only initialize the position if the application hasn't cared
              about it */
           if (c->x==0 && c->y==0)
              init_position(c);
           if ((hints = XGetWMHints(dpy, w))) {
              if (hints->flags & StateHint)
                 set_wm_state(c, hints->initial_state);
              XFree(hints);
           }
	}

	/* client is initialised */

	gravitate(c);
	reparent(c);

	XMapWindow(dpy, c->window);
	XMapRaised(dpy, c->parent);
	set_wm_state(c, NormalState);

	XSync(dpy, False);
	XUngrabServer(dpy);

        focus_client(c);
}


int intersect_area( Client *c1, Client *c2 )
{
   int c1x1=c1->x, c1y1=c1->y;
   int c1x2=c1->x+c1->width, c1y2=c1->y+c1->height;
   int c2x1=c2->x, c2y1=c2->y;
   int c2x2=c2->x+c2->width, c2y2=c2->y+c2->height;
 
   if (c1x2<c2x1 || c1y2<c2y1 || c1x1>c2x2 || c1y1>c2y2)
     return 0;

   if (c2x2>c1x1 && c2y2>c1y1)
     return (c2x2-c1x1)*(c2y2-c1y1);
   else if (c1x2>c2x1 && c2y2>c1y1)
     return (c1x2-c2x1)*(c2y2-c1y1);
   else if (c2x2>c1x1 && c1y2>c2y1)
     return (c2x2-c1x1)*(c1y2-c2y1);
   else if (c1x2>c2x1 && c1y2>c2y1)
     return (c1x2-c2x1)*(c1y2-c2y1);
   exit(1);
}


int calc_overlap( Client *c )
{
   Client *cl;
   int dw = DisplayWidth(dpy,c->screen->screen);
   int dh = DisplayHeight(dpy,c->screen->screen);
   int val = 0;
  
   for (cl = head_client; cl; cl=cl->next)
     {
       if (cl!=c && cl->vdesk==c->vdesk)
         val += intersect_area(c,cl);
     }
 
   /* penalize outside-of-window positions hard */
   if (c->x<0)
     val += 32*abs(c->x)*c->height;
   if (c->y<0)
     val += 32*abs(c->y)*c->width;
   if (c->x+c->width>dw)
     val += 32*(c->x+c->width-dw)*c->height;
   if (c->y+c->height>dh)
     val += 32*(c->y+c->height-dh)*c->width;
 
   return val;
}


void minoverlap_place_client( Client *c )
{
   int dw = DisplayWidth(dpy,c->screen->screen);
   int dh = DisplayHeight(dpy,c->screen->screen);
   int wi = dw/16;
   int hi = dh/16;
   int mlen = (wi>hi)?wi:hi;
   int failures; 
   int xmin=0,ymin=0;
   int minval = INT_MAX;
   int val;

   /* Global placement strategy - the screen is divided into a 16x16 grid,
      and the window's top left corner is placed where it causes the
      least overlap with existing windows */
   for (c->y=-hi; c->y<dh; c->y+=wi)
      for (c->x=-wi; c->x<dw; c->x+=hi)
      {
        val = calc_overlap(c);
        if (val<minval)
        {
           minval = val;
           xmin = c->x;
           ymin = c->y;
        }
      }

   /* Local placement strategy similar to the one suggested in the 
      specifications. Choose random displacement vector (manhattan 
      length = cellsize) and test, if the result is better than the current
      placement update current and continue. Stop after 5 failures. */

   c->x = xmin;
   c->y = ymin;

   failures = 0;
   while (failures<5)
   {
      int dx,dy;

      dx = random()%mlen - mlen/2;
      dy = mlen-dx - mlen/2;
      c->x += dx;
      c->y += dy;
      val = calc_overlap(c);
      if (val<minval)
      {
        minval = val;
        xmin = c->x;
        ymin = c->y;
      }
      else
        failures++;
   }
   c->x = xmin;
   c->y = ymin;
}

void init_position(Client *c) {
	int xmax = DisplayWidth(dpy, c->screen->screen);
	int ymax = DisplayHeight(dpy, c->screen->screen);

	if (c->width < 15) c->width = 15;
	if (c->height < 15) c->height = 15;
	if (c->width > xmax) c->width = xmax;
	if (c->height > ymax) c->height = ymax;

	if (c->size->flags & (/*PPosition | */USPosition)) {
		c->x = c->size->x;
		c->y = c->size->y;
		if (c->x < 0 || c->y < 0 || c->x > xmax || c->y > ymax)
			c->x = c->y = c->border;
	} else {
          minoverlap_place_client(c);
	}
	/* reposition if maximised horizontally or vertically */
	if (c->x == 0 && c->width == xmax)
		c->x = -c->border;
	if (c->y == 0 && c->height == ymax)
		c->y = -c->border;
}

void reparent(Client *c) {
	XSetWindowAttributes p_attr;

	XSelectInput(dpy, c->window, EnterWindowMask | PropertyChangeMask);

	p_attr.override_redirect = True;
	p_attr.background_pixel = c->screen->bg.pixel;
	p_attr.event_mask = ChildMask | ButtonPressMask | ExposureMask | EnterWindowMask;
	c->parent = XCreateWindow(dpy, c->screen->root, c->x-c->border, c->y-c->border,
		c->width+(c->border*2), c->height + (c->border*2), 0,
		DefaultDepth(dpy, c->screen->screen), CopyFromParent,
		DefaultVisual(dpy, c->screen->screen),
		CWOverrideRedirect | CWBackPixel | CWEventMask, &p_attr);
	XDefineCursor(dpy,c->parent,move_curs);

	XAddToSaveSet(dpy, c->window);
	XSetWindowBorderWidth(dpy, c->window, 0);
	XReparentWindow(dpy, c->window, c->parent, c->border, c->border);
}
