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

void draw_outline(Client *c) {
	XDrawRectangle(dpy, c->screen->root, c->screen->invert_gc,
		c->x - c->border, c->y - c->border,
		c->width + 1, c->height + 1);
}

void get_mouse_position(int *x, int *y, Window root) {
	Window dw1, dw2;
	int t1, t2;
	unsigned int t3;

	XQueryPointer(dpy, root, &dw1, &dw2, x, y, &t1, &t2, &t3);
}

void recalculate_sweep(Client *c, int x1, int y1, int x2, int y2,
                      int westwards, int northwards ) {
	int basex, basey;
	int xsnap=0, ysnap=0;

	c->width = abs(x1 - x2);
	c->height = abs(y1 - y2);

	if (c->size->flags & PResizeInc) {
		basex = (c->size->flags & PBaseSize) ? c->size->base_width :
			(c->size->flags & PMinSize) ? c->size->min_width : 0;
		basey = (c->size->flags & PBaseSize) ? c->size->base_height :
			(c->size->flags & PMinSize) ? c->size->min_height : 0;
                xsnap = (c->width - basex) % c->size->width_inc;
                ysnap = (c->height - basey) % c->size->height_inc;
                c->width -= xsnap;
                c->height -= ysnap;
	}

	if (c->size->flags & PMinSize) {
		if (c->width < c->size->min_width) c->width = c->size->min_width;
		if (c->height < c->size->min_height) c->height = c->size->min_height;
	}

	if (c->size->flags & PMaxSize) {
		if (c->width > c->size->max_width) c->width = c->size->max_width;
		if (c->height > c->size->max_height) c->height = c->size->max_height;
	}

	c->x = (x1 <= x2) ? x1 : x1 - c->width;
	c->y = (y1 <= y2) ? y1 : y1 - c->height;
        if (x1>x2)
          xsnap = -xsnap;
        if (y1>y2)
          ysnap = -ysnap;
        if (westwards)
          c->x += xsnap;
        if (northwards)
          c->y += ysnap;
}

void sweep(Client *c) {
	XEvent ev;
        int win_px,win_py;
        double win_px_n,win_py_n;
        int root_px,root_py;
        Window dumw;
        int dumi;
        ResizeDir dir;
        int x1 = c->x;
        int y1 = c->y;
        int x2 = c->x+c->width;
        int y2 = c->y+c->height;
 
        XQueryPointer(dpy,c->window,&dumw,&dumw,&root_px,&root_py,
                      &win_px,&win_py,&dumi);
        win_px_n = (double)win_px/(double)c->width;
        win_py_n = (double)win_py/(double)c->height;
 
        if (win_py_n<0.3)
          {
            if (win_px_n<0.3)
              dir = NorthWest;
            else if (win_px_n>0.7)
              dir = NorthEast;
            else
              dir = North;
          }
        else if (win_py_n>0.7)
          {
            if (win_px_n<0.3)
              dir = SouthWest;
            else if (win_px_n>0.7)
              dir = SouthEast;
            else
              dir = South;
          }
        else
          {
            if (win_px_n<=0.5)
              dir = West;
            else
              dir = East;
          }

	if (!grab_pointer(c->screen->root, MouseMask, resize_cursors[dir])) return;

	XGrabServer(dpy);
	draw_outline(c);

        for (;;) 
          {
            int dx;
            int dy;
 
            XMaskEvent(dpy, MouseMask, &ev);
            switch (ev.type) {
           case MotionNotify:
              draw_outline(c); /* clear */
              XUngrabServer(dpy);
              dx = ev.xmotion.x-root_px;
              dy = ev.xmotion.y-root_py;
              switch (dir)
                {
               case NorthWest:
                  recalculate_sweep(c,x1+dx,y1+dy,x2,y2,1,1);
                  break;
                case North:
                  recalculate_sweep(c,x1,y1+dy,x2,y2,0,1);
                  break;
                case NorthEast:
                  recalculate_sweep(c,x1,y1+dy,x2+dx,y2,0,1);
                  break;
                case East:
                  recalculate_sweep(c,x1,y1,x2+dx,y2,0,0);
                  break;
                case SouthEast:
                  recalculate_sweep(c,x1,y1,x2+dx,y2+dy,0,0);
                  break;
                case South:
                  recalculate_sweep(c,x1,y1,x2,y2+dy,0,0);
                  break;
                case SouthWest:
                  recalculate_sweep(c,x1+dx,y1,x2,y2+dy,1,0);
                  break;
                case West:
                  recalculate_sweep(c,x1+dx,y1,x2,y2,1,0);
                  break;
               }
              XSync(dpy, False);
              XGrabServer(dpy);
              draw_outline(c);
              break;
            case ButtonRelease:
              draw_outline(c); /* clear */
              XUngrabServer(dpy);
              XUngrabPointer(dpy, CurrentTime);
              return;
            }
          }
}

static int absmin(int a, int b) {
       if (abs(a) < abs(b))
               return a;
       return b;
}
static void snap_client(Client *c) {
       int dx, dy;
       Client *ci;

       /* snap to screen border */
       if (abs(c->x - c->border) < opt_snap) c->x = c->border;
       if (abs(c->y - c->border) < opt_snap) c->y = c->border;
       if (abs(c->x + c->width + c->border - DisplayWidth(dpy, c->screen->screen)) < opt_snap)
               c->x = DisplayWidth(dpy, c->screen->screen) - c->width - c->border;
       if (abs(c->y + c->height + c->border - DisplayHeight(dpy, c->screen->screen)) < opt_snap)
               c->y = DisplayHeight(dpy, c->screen->screen) - c->height - c->border;

       /* snap to other windows */
       dx = dy = opt_snap;
      for (ci = head_client; ci; ci = ci->next) {
               if (ci != c && (ci->vdesk == vdesk || ci->vdesk == STICKY)) {
                       if (ci->y - ci->border - c->border - c->height - c->y <= opt_snap && c->y - c->border - ci->border - ci->height - ci->y <= opt_snap) {
                               dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border);
                               dx = absmin(dx, ci->x + ci->width - c->x - c->width);
                               dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border);
                               dx = absmin(dx, ci->x - c->x);
                       }
                       if (ci->x - ci->border - c->border - c->width - c->x <= opt_snap && c->x - c->border - ci->border - ci->width - ci->x <= opt_snap) {
                               dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border);
                               dy = absmin(dy, ci->y + ci->height - c->y - c->height);
                               dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border);
                               dy = absmin(dy, ci->y - c->y);
                       }
               }
       }
       if (abs(dx) < opt_snap)
               c->x += dx;
       if (abs(dy) < opt_snap)
               c->y += dy;
}

void drag(Client *c) {
	int x1, y1;
	int old_cx = c->x;
	int old_cy = c->y;
	XEvent ev;
	if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return;
	get_mouse_position(&x1, &y1, c->screen->root);
	XGrabServer(dpy);
	draw_outline(c);
	for (;;) {
		XMaskEvent(dpy, MouseMask, &ev);
		switch (ev.type) {
			case MotionNotify:
				draw_outline(c);
				XUngrabServer(dpy);
				c->x = old_cx + (ev.xmotion.x - x1);
				c->y = old_cy + (ev.xmotion.y - y1);
				if (opt_snap)
					snap_client(c);
				XSync(dpy, False);
				XGrabServer(dpy);
				draw_outline(c);
				break;
			case ButtonRelease:
				draw_outline(c);
				XUngrabServer(dpy);
				XUngrabPointer(dpy, CurrentTime);
				return;
			default:
				break;
		}
	}
}

void move(Client *c, int set) {
	if (c) {
		XRaiseWindow(dpy, c->parent);
		if (!set)
			drag(c);
		XMoveWindow(dpy, c->parent, c->x - c->border, c->y - c->border);
	}
}

void resize(Client *c, int set) {
	if (c) {
		XRaiseWindow(dpy, c->parent);
		if (!set)
			sweep(c);
		XMoveResizeWindow(dpy, c->parent, c->x - c->border,
				c->y - c->border, c->width + (c->border*2),
				c->height + (c->border*2));
		XMoveResizeWindow(dpy, c->window, c->border, c->border,
				c->width, c->height);
	}
}

void maximise(Client *c) {
	if (c->oldw && c->oldh) {
		c->x = c->oldx;
		c->width = c->oldw;
		c->oldw = 0;
		c->y = c->oldy;
		c->height = c->oldh;
		c->oldh = 0;
	} else {
		c->oldx = c->x;
		c->oldw = c->width;
		recalculate_sweep(c, 0, c->y, DisplayWidth(dpy, c->screen->screen),
                                c->y + c->height,0,0);
		c->oldy = c->y;
		c->oldh = c->height;
		recalculate_sweep(c, c->x, 0, c->x + c->width,
                                DisplayHeight(dpy, c->screen->screen),0,0);
	}
}

void maximise_horiz(Client *c) {
	if (c->oldw) {
		c->x = c->oldx;
		c->width = c->oldw;
		c->oldw = 0;
	} else {
		c->oldx = c->x;
		c->oldw = c->width;
		recalculate_sweep(c, 0, c->y, DisplayWidth(dpy, c->screen->screen),
                                c->y + c->height,0,0);
	}
}

void maximise_vert(Client *c) {
	if (c->oldh) {
		c->y = c->oldy;
		c->height = c->oldh;
		c->oldh = 0;
	} else {
		c->oldy = c->y;
		c->oldh = c->height;
		recalculate_sweep(c, c->x, 0, c->x + c->width,
                                DisplayHeight(dpy, c->screen->screen),0,0);
	}
}

void hide(Client *c) {
	if (c) {
		c->ignore_unmap += 2;
		XUnmapWindow(dpy, c->parent);
		XUnmapWindow(dpy, c->window);
		set_wm_state(c, IconicState);
	}
}

void unhide(Client *c, int raise, int bringtofront) {
	XMapWindow(dpy, c->window);
	raise ? XMapRaised(dpy, c->parent) : XMapWindow(dpy, c->parent);
	set_wm_state(c, NormalState);

	if (bringtofront)
	   client_to_front(c);
}

void next(void) {
	Client *newc;
	Client *stop;

        if (!head_client)
           return;
 
        if (current && current->next)
        {
           stop = current;
           newc = current->next;
        }
        else 
        {
           stop = head_client;
           newc = head_client;
        }
        while (newc && newc->vdesk != vdesk && newc->vdesk != STICKY)
        {
           newc = newc->next;
           if (newc==NULL)
              newc = head_client;
           if (newc==stop)
              newc = NULL;
              if(newc->vdesk != vdesk && newc->vdesk != STICKY) {
                      do {
                              newc = newc->next;
                      } while (newc && newc->vdesk != vdesk && newc->vdesk != STICKY);
              }
	}
	if (newc) {
		if (newc->vdesk == vdesk || newc->vdesk == STICKY) {
                        unhide(newc,RAISE,0);
                        focus_client(newc);
		}
	}
}

void switch_vdesk(int v) {
	Client *c;
	int wdesk;
	if (v == vdesk)
		return;
	if (current && current->vdesk != STICKY) {
		client_update_current(current, NULL);
	}
	for (c = head_client; c; c = c->next) {
		wdesk = c->vdesk;
		if (wdesk == vdesk) {
			hide(c);
		} else if (wdesk == v) {
			unhide(c, NO_RAISE,0);
		}
	}
	vdesk = v;
}

ScreenInfo *find_screen(Window root) {
	int i;
	for (i = 0; i < num_screens; i++) {
		if (screens[i].root == root)
			return &screens[i];
	}
	return NULL;
}
