/*
  wind.c - A simple strategy for handling windows.
*/
  
#include <stdio.h>
#include <stdlib.h>

#include "err.h"
#include "fc.h"

#include "area.h"

#define BORDER_WIDTH 1
#define INIT_NUM_AREAS 5

/* ---------- Function declarations ---------- */
static struct area *wind_findarea(struct wind *, int, int);

/* ------------------------------------------------------------------------ */
struct wind *
wind_create(struct wind *parent,
	    struct wind_params *wop)
{
    struct wind *ret;
    XGCValues xgcv;

    ret = (struct wind *)calloc(sizeof(struct wind), 1);
    if (!ret) {
	do_error(ERR_NOMEM, "Can't create window.");
    }
    
    ret->window = XCreateSimpleWindow(dpy, 
				      RootWindow(dpy, DefaultScreen(dpy)),   
				      wop->x, wop->y,
				      wop->width, wop->height,
				      BORDER_WIDTH, 
				      /* Want this pixel value to be */
				      /* CopyFromParent or setable somehow. */
				      /* XXX */ 
				      CopyFromParent,
				      ((wop->bck_color == 0) ?
				       WhitePixel(dpy, DefaultScreen(dpy)) :
				       wop->bck_color));
    if (!ret->window) {
	do_error(ERR_NOWIND, "Can't get window.");
    }

    XSetWMName(dpy, ret->window, &gameTextProp);
    XSetWMIconName(dpy, ret->window, &gameIconProp);
    XMapWindow(dpy, ret->window);

    XSelectInput(dpy, ret->window,
		 ButtonPressMask |
		 ButtonReleaseMask |
		 KeyPressMask |
		 KeyReleaseMask |
		 StructureNotifyMask |
		 ExposureMask);

    ret->bounds.x = wop->x;
    ret->bounds.y = wop->y;
    ret->bounds.width = wop->width;
    ret->bounds.height = wop->height;
    ret->parent = wop->parent;
    ret->gc = wop->gc;
    /* Gee, this is lame.  Fix it, please. */
    xgcv.foreground = WhitePixel(dpy, DefaultScreen(dpy));
    xgcv.background = BlackPixel(dpy, DefaultScreen(dpy));
    ret->invertgc = XCreateGC(dpy, ret->window, 
			      GCForeground | GCBackground, &xgcv);
    
    ret->areas = (struct area **)calloc(sizeof(struct area *), INIT_NUM_AREAS);
    if (!ret->areas) {
	do_error(ERR_NOMEM, "Can't create areas.");
    }
    ret->num_areas = 0;
    ret->max_areas = INIT_NUM_AREAS;

    switch (wop->win_type) {
      case FREE_WINDOW:
	do_error(ERR_NOOPENWINDOW, "Can't open free window");
	break;
	
      case FC_WINDOW:
	fcwind_open(ret);
	break;
    }

    return(ret);
}

/* ------------------------------------------------------------------------ */
void 
wind_destroy(struct wind *wind)
{
     wind->ops->term(wind);
     XDestroyWindow(dpy, wind->window);
     free(wind);
}

/* ------------------------------------------------------------------------ */
void wind_mouse(struct wind *wind, XButtonEvent *bev)
{
    struct area *area;

    area = wind_findarea(wind, bev->x, bev->y);
    
    if (area) {
	if (area_mouse(area, bev)) {
	    return;
	}
    }
    wind->ops->mouse(wind, bev);
}

/* ------------------------------------------------------------------------ */
void wind_draw(struct wind *wind, XExposeEvent *eev)
{
     Region rgn;
     XRectangle trect;

     struct area *area = wind_findarea(wind, eev->x, eev->y); 

     trect.x = eev->x;
     trect.y = eev->y;
     trect.height = eev->height;
     trect.width = eev->width;

     rgn = XCreateRegion();
     XUnionRectWithRegion(&trect, rgn, rgn);

     /* Fix this to work with all areas, not just one. */
     /* Cause this is just wrong. !!! */
     /* Needs to check region in the eev, as well as caching the regions to */
     /* build a larger region. */
     if (area) {
	  if (area_draw(area, rgn)) {
	       goto fini;
	  }
     }
     wind->ops->draw(wind, rgn);
     
fini:
     XDestroyRegion(rgn);
}

/* ------------------------------------------------------------------------ */
void wind_resize(struct wind *wind, XConfigureEvent *cev)
{
    struct area *area = wind_findarea(wind, cev->x, cev->y);
    if (area) {
	if (area_resize(area, cev)) {
	    return;
	}
    }
    wind->ops->resize(wind, cev);
}

/* ------------------------------------------------------------------------ */
void wind_key(struct wind *wind, XKeyEvent *kev)
{
    struct area *area = wind_findarea(wind, kev->x, kev->y);
    if (area) {
	if (area_key(area, kev)) {
	    return;
	}
    }
    wind->ops->key(wind, kev);
}

/* ------------------------------------------------------------------------ */
void wind_activate(struct wind *wind, XMapEvent *mev)
{
    int i;
    for (i = 0; i < wind->num_areas; i++) {
	if (wind->areas[i]) {
	    area_activate(wind->areas[i], mev);
	}
    }
}

/* ------------------------------------------------------------------------ */
void wind_deactivate(struct wind *wind, XUnmapEvent *uev)
{
    int i;
    for (i = 0; i < wind->num_areas; i++) {
	if (wind->areas[i]) {
	    area_deactivate(wind->areas[i], uev);
	}
    }
}

/* ------------------------------------------------------------------------ */
static struct area *wind_findarea(struct wind *wind, int x, int y)
{
    int i, dx, dy;
    struct area *area;

    for (i = 0; i < wind->num_areas; i++) {
	area = wind->areas[i];
	dx = x - area->bounds.x;
	if (dx >= 0 && dx < area->bounds.width) {
	    dy = y - area->bounds.y;
	    if (dy >= 0 && dy < area->bounds.height) {
		return(area);
	    }
	}
    }
    return(NULL);
}
