#include "e.h"

typedef struct _E_Maximize_Rect E_Maximize_Rect;

struct _E_Maximize_Rect
{
   int x1, y1, x2, y2;
};

#define OBSTACLE(_x1, _y1, _x2, _y2) \
   { \
      r = E_NEW(E_Maximize_Rect, 1); \
      r->x1 = (_x1); r->y1 = (_y1); r->x2 = (_x2); r->y2 = (_y2); \
      rects = eina_list_append(rects, r); \
   }

static void _e_maximize_border_rects_fill(E_Border *bd, Eina_List *list, int *x1, int *y1, int *x2, int *y2, E_Maximize dir);
static void _e_maximize_border_rects_fill_both(E_Border *bd, Eina_List *rects, int *x1, int *y1, int *x2, int *y2); 
static void _e_maximize_border_rects_fill_horiz(E_Border *bd, Eina_List *rects, int *x1, int *x2, int *bx, int *by, int *bw, int *bh);
static void _e_maximize_border_rects_fill_vert(E_Border *bd, Eina_List *rects, int *y1, int *y2, int *bx, int *by, int *bw, int *bh);

EAPI void
e_maximize_border_shelf_fit(E_Border *bd, int *x1, int *y1, int *x2, int *y2, E_Maximize dir)
{
   e_maximize_border_shelf_fill(bd, x1, y1, x2, y2, dir);
}

EAPI void
e_maximize_border_dock_fit(E_Border *bd, int *x1, int *y1, int *x2, int *y2)
{
   E_Border_List *bl;
   E_Border *bd2;
   int cx1, cx2, cy1, cy2;

   cx1 = bd->zone->x;
   if (x1) cx1 = *x1;
   
   cy1 = bd->zone->y;
   if (y1) cy1 = *y1;
   
   cx2 = bd->zone->x + bd->zone->w;
   if (x2) cx2 = *x2;

   cy2 = bd->zone->y + bd->zone->h;
   if (y2) cy2 = *y2;

   bl = e_container_border_list_first(bd->zone->container);
   while ((bd2 = e_container_border_list_next(bl)))
     {
	enum {
	     NONE,
	     TOP,
	     RIGHT,
	     BOTTOM,
	     LEFT
	} edge = NONE;
	
	if ((bd2->zone != bd->zone) || (bd2 == bd) ||
	    (bd2->client.netwm.type != ECORE_X_WINDOW_TYPE_DOCK))
	  continue;

	if (((bd2->x == bd2->zone->x) || ((bd2->x + bd2->w) == (bd2->zone->x + bd2->zone->w))) &&
	    ((bd2->y == bd2->zone->y) || ((bd2->x + bd2->h) == (bd2->zone->x + bd2->zone->h))))
	  {
	     /* corner */
	     if (bd2->w > bd2->h)
	       {
		  if (bd2->y == bd2->zone->y)
		    edge = TOP;
		  else if ((bd2->y + bd2->h) == (bd2->zone->y + bd2->zone->h))
		    edge = BOTTOM;
	       }
	     else
	       {
		  if ((bd2->x + bd2->w) == (bd2->zone->x + bd2->zone->w))
		    edge = RIGHT;
		  else if (bd2->x == bd2->zone->x)
		    edge = LEFT;
	       }
	  }
	else
	  {
	     if (bd2->y == bd2->zone->y)
	       edge = TOP;
	     else if ((bd2->y + bd2->h) == (bd2->zone->y + bd2->zone->h))
	       edge = BOTTOM;
	     else if (bd2->x == bd2->zone->x)
	       edge = LEFT;
	     else if ((bd2->x + bd2->w) == (bd2->zone->x + bd2->zone->w))
	       edge = RIGHT;
	  }

	switch (edge)
	  {
	   case TOP:
	      if ((bd2->y + bd2->h) > cy1)
		cy1 = (bd2->y + bd2->h);
	      break;
	   case RIGHT:
	      if (bd2->x < cx2)
		cx2 = bd2->x;
	      break;
	   case BOTTOM:
	      if (bd2->y < cy2)
		cy2 = bd2->y;
	      break;
	   case LEFT:
	      if ((bd2->x + bd2->w) > cx1)
		cx1 = (bd2->x + bd2->w);
	      break;
	   case NONE:
	      printf("Crazy people. Dock isn't at the edge.\n");
	      break;
	  }
     }
   e_container_border_list_free(bl);

   if (x1) *x1 = cx1;
   if (y1) *y1 = cy1;
   if (x2) *x2 = cx2;
   if (y2) *y2 = cy2;
}

EAPI void
e_maximize_border_shelf_fill(E_Border *bd, int *x1, int *y1, int *x2, int *y2, E_Maximize dir)
{
   Eina_List *l, *rects = NULL;
   E_Shelf *es;
   E_Maximize_Rect *r;

   EINA_LIST_FOREACH(e_shelf_list(), l, es)
     {
	Eina_List *ll;
	E_Config_Shelf_Desk *sd;

	if (es->cfg->overlap) continue;
	if (es->zone != bd->zone) continue;
	if (es->cfg->desk_show_mode)
	  {
	     EINA_LIST_FOREACH(es->cfg->desk_list, ll, sd)
	       {
		  if (!sd) continue;
		  if ((sd->x == bd->desk->x) && (sd->y == bd->desk->y))
		    {
		       OBSTACLE(es->x + es->zone->x, es->y + es->zone->y, 
				es->x + es->zone->x + es->w, es->y + es->zone->y + es->h);
		       break;
		    }
	       }
	  }
	else
	  {
	     OBSTACLE(es->x + es->zone->x, es->y + es->zone->y, 
		      es->x + es->zone->x + es->w, es->y + es->zone->y + es->h);
	  }
     }
   if (rects)
     {
	_e_maximize_border_rects_fill(bd, rects, x1, y1, x2, y2, dir);
	E_FREE_LIST(rects, free);
     }
}

EAPI void
e_maximize_border_border_fill(E_Border *bd, int *x1, int *y1, int *x2, int *y2, E_Maximize dir)
{
   Eina_List *rects = NULL;
   E_Border_List *bl;
   E_Maximize_Rect *r;
   E_Border *bd2;

   bl = e_container_border_list_first(bd->zone->container);
   while ((bd2 = e_container_border_list_next(bl)))
     {
	if ((bd2->zone != bd->zone) || (bd == bd2) || (bd2->desk != bd->desk && !bd2->sticky) || (bd2->iconic))
	  continue;
	OBSTACLE(bd2->x, bd2->y, bd2->x + bd2->w, bd2->y + bd2->h);
     }
   e_container_border_list_free(bl);
   if (rects)
     {
	_e_maximize_border_rects_fill(bd, rects, x1, y1, x2, y2, dir);
	E_FREE_LIST(rects, free);
     }
}

static void
_e_maximize_border_rects_fill(E_Border *bd, Eina_List *rects, int *x1, int *y1, int *x2, int *y2, E_Maximize dir)
{
   if ((dir & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_BOTH)
     {
        _e_maximize_border_rects_fill_both(bd, rects, x1, y1, x2, y2);
     }
   else
     {
        int bx, by, bw, bh;

        bx = bd->x;
        by = bd->y;
        bw = bd->w;
        bh = bd->h;

	if ((dir & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL)
	  _e_maximize_border_rects_fill_horiz(bd, rects, x1, x2, &bx, &by, &bw, &bh);
	else if ((dir & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL)
	  _e_maximize_border_rects_fill_vert(bd, rects, y1, y2, &bx, &by, &bw, &bh);  
     }
}

static void
_e_maximize_border_rects_fill_both(E_Border *bd, Eina_List *rects, int *x1, int *y1, int *x2, int *y2)
{
   int hx1, hy1, hx2, hy2;
   int vx1, vy1, vx2, vy2;
   int bx, by, bw, bh;

   hx1 = vx1 = bd->zone->x;
   if (x1) hx1 = vx1 = *x1;
   
   hy1 = vy1 = bd->zone->y;
   if (y1) hy1 = vy1 = *y1;
   
   hx2 = vx2 = bd->zone->x + bd->zone->w;
   if (x2) hx2 = vx2 = *x2;

   hy2 = vy2 = bd->zone->y + bd->zone->h;
   if (y2) hy2 = vy2 = *y2;

   /* Init working values, try maximizing horizontally first */
   bx = bd->x;
   by = bd->y;
   bw = bd->w;
   bh = bd->h;
   _e_maximize_border_rects_fill_horiz(bd, rects, &hx1, &hx2, &bx, &by, &bw, &bh);
   _e_maximize_border_rects_fill_vert(bd, rects, &hy1, &hy2, &bx, &by, &bw, &bh);

   /* Reset working values, try maximizing vertically first */
   bx = bd->x;
   by = bd->y;
   bw = bd->w;
   bh = bd->h;
   _e_maximize_border_rects_fill_vert(bd, rects, &vy1, &vy2, &bx, &by, &bw, &bh);
   _e_maximize_border_rects_fill_horiz(bd, rects, &vx1, &vx2, &bx, &by, &bw, &bh);

   /* Use the result set that gives the largest volume */
   if (((hx2 - hx1) * (hy2 - hy1)) > ((vx2 - vx1) * (vy2 - vy1)))
     {
        if (x1) *x1 = hx1;
	if (y1) *y1 = hy1;
	if (x2) *x2 = hx2;
	if (y2) *y2 = hy2;
     }
   else
     {
        if (x1) *x1 = vx1;
	if (y1) *y1 = vy1;
	if (x2) *x2 = vx2;
	if (y2) *y2 = vy2;
     }
}

static void
_e_maximize_border_rects_fill_horiz(E_Border *bd, Eina_List *rects, int *x1, int *x2, int *bx, int *by, int *bw, int *bh)
{
   Eina_List *l;
   E_Maximize_Rect *rect;
   int cx1, cx2;

   cx1 = bd->zone->x;
   if (x1) cx1 = *x1;
   
   cx2 = bd->zone->x + bd->zone->w;
   if (x2) cx2 = *x2;

   /* Expand left */
   EINA_LIST_FOREACH(rects, l, rect)
     {
	if ((rect->x2 > cx1) && (rect->x2 <= *bx) &&
	    E_INTERSECTS(0, rect->y1, bd->zone->w, (rect->y2 - rect->y1), 0, *by, bd->zone->w, *bh))
	  {
	     cx1 = rect->x2;
	  }
     }
   *bw += (*bx - cx1);
   *bx = cx1;

   /* Expand right */
   EINA_LIST_FOREACH(rects, l, rect)
     {
	if ((rect->x1 < cx2) && (rect->x1 >= (*bx + *bw)) &&
	    E_INTERSECTS(0, rect->y1, bd->zone->w, (rect->y2 - rect->y1), 0, *by, bd->zone->w, *bh))
	  {
	     cx2 = rect->x1;
	  }
     }
   *bw = (cx2 - cx1);
 
   if (x1) *x1 = cx1;
   if (x2) *x2 = cx2;
}

static void
_e_maximize_border_rects_fill_vert(E_Border *bd, Eina_List *rects, int *y1, int *y2, int *bx, int *by, int *bw, int *bh)
{
   Eina_List *l;
   E_Maximize_Rect *rect;
   int cy1, cy2;

   cy1 = bd->zone->y;
   if (y1) cy1 = *y1;
   
   cy2 = bd->zone->y + bd->zone->h;
   if (y2) cy2 = *y2;

   /* Expand up */
   EINA_LIST_FOREACH(rects, l, rect)
     {
	if ((rect->y2 > cy1) && (rect->y2 <= *by) &&
	    E_INTERSECTS(rect->x1, 0, (rect->x2 - rect->x1), bd->zone->h, *bx, 0, *bw, bd->zone->h))
	  {
	     cy1 = rect->y2;
	  }
     }
   *bh += (*by - cy1);
   *by = cy1;

   /* Expand down */
   EINA_LIST_FOREACH(rects, l, rect)
     {
	if ((rect->y1 < cy2) && (rect->y1 >= (*by + *bh)) &&
	    E_INTERSECTS(rect->x1, 0, (rect->x2 - rect->x1), bd->zone->h, *bx, 0, *bw, bd->zone->h))
	  {
	     cy2 = rect->y1;
	  }
     }
   *bh = (cy2 - cy1);

   if (y1) *y1 = cy1;
   if (y2) *y2 = cy2;
}

