/* @(#)window.c	1.24 94/05/13  */

/*
 * Handle everything related to display
 */

#include <stdio.h>
#include <stdlib.h>
#include <xil/xil.h>
#include "params.h"
#include "dither.h"
#include "window_state.h"

void init_cmap(XilLookup cmap, Display * display, Window window, int offset);

Atom closedownAtom;

/* - - - - - - - - - - - - - - - - - - - - - - */
static void 
create_xil_display(WindowState * ws)
{
    ws->imageYCC = xil_create(ws->state, ws->w, ws->h, 3, XIL_BYTE);
    ws->display = xil_create_from_window(ws->state,
					 ws->xdisplay, ws->xwindow);


    if (ws->display_depth == 24) {

	XilColorspace cspace_ycc601 = xil_colorspace_get_by_name(
						       ws->state, "ycc601");
	XilColorspace cspace_rgb709 = xil_colorspace_get_by_name(
						       ws->state, "rgb709");

	xil_set_colorspace(ws->imageYCC, cspace_ycc601);

	xil_set_colorspace(ws->display, cspace_rgb709);
    }

}

/* - - - - - - - - - - - - - - - - - - - - - - */

/*
 * Error handler installed iff -E specified
 */
Xil_boolean 
error_handler(XilError error)
{
    xil_call_next_error_handler(error);

    fprintf(stderr, "\n***ERROR received; example exiting\n");
    exit(1);
}

/* - - - - - - - - - - - - - - - - - - - - - - */

WindowState *
initialize_window(Parameters * param, XilSystemState state)
{
    XilLookup yuv_to_rgb;
    XilDataType datatype;
    XEvent event;
    WindowState *ws;
    XSizeHints hints;

    ws = (WindowState *) malloc(sizeof(WindowState));
    if (ws == NULL)
	return NULL;

    ws->state = state;

    /* install the xil error handler */
    if (param->error_out)
	xil_install_error_handler(state, error_handler);

    /* resolve window position */
    hints.flags = 0;
    if (param->x >= 0) {
	ws->x = param->x;
	hints.flags |= USPosition;
	hints.x = param->x;
    }
    else {
	ws->x = 0;
    }
    if (param->y >= 0) {
	ws->y = param->y;
	hints.flags |= USPosition;
	hints.y = param->y;
    }
    else {
	ws->y = 0;
    }

    /* ensure decompress window dimensions match those of the CIS */
    ws->w = (param->scalex ?
	     ((float) param->width / (float) param->scalex + 0.5) :
	     param->width);
    ws->h = (param->scaley ?
	     ((float) param->height / (float) param->scaley + 0.5) :
	     param->height);

    /*
     * H261 CIF is defined as 352x288 not 320x240
     */
    
    if (param->do_cis == H261) {
      if (param->scalex == 2 || param->scalex == 1){
        ws->w = 352;
        ws->h = 288;
      } else {
        ws->w = 352/2;
        ws->h = 288/2;
      }
    }
      
    /* xlib window creation */
    ws->xdisplay = XOpenDisplay(NULL);
    if (!ws->xdisplay) {
	fprintf(stderr, "Unable to connect to server\n");
	exit(1);
    }
    ws->display_depth = DefaultDepth(ws->xdisplay,
				     DefaultScreen(ws->xdisplay));

    if (ws->display_depth != 8 && ws->display_depth != 24) {
	fprintf(stderr,
	      "\nError: This example does not support visual depth of %d\n",
		ws->display_depth);
	xil_close(state);
	exit(1);
    }

    ws->xwindow = XCreateSimpleWindow(ws->xdisplay,
				      DefaultRootWindow(ws->xdisplay),
				      ws->x, ws->y, ws->w, ws->h, 0, 0, 0);

    if (!ws->xwindow) {
	fprintf(stderr, "Unable to create window\n");
	exit(1);
    }

    /* if the user tries to shut down the window using the menu */
    if (closedownAtom = XInternAtom(ws->xdisplay, "WM_DELETE_WINDOW", False))
	XSetWMProtocols(ws->xdisplay, ws->xwindow, &closedownAtom, 1);

    /* list window events used. */
    XSelectInput(ws->xdisplay, ws->xwindow,
		 ExposureMask | StructureNotifyMask);

    /* give the window manager a hint about the desired window location */
    XSetWMNormalHints(ws->xdisplay, ws->xwindow, &hints);

    /* set the name on the titlebar */
    XStoreName(ws->xdisplay, ws->xwindow, param->windowname);

    /* make the window visible */
    XMapWindow(ws->xdisplay, ws->xwindow);

    if (ws->display_depth == 8) {

	get_dither_info(state, ws->scale, ws->offset,
			&ws->mask, &yuv_to_rgb, &ws->cmap);

	init_cmap(yuv_to_rgb, ws->xdisplay, ws->xwindow, -1);

	xil_lookup_set_offset(ws->cmap, xil_lookup_get_offset(yuv_to_rgb));
    }

    /* wait for the window to be mapped (an Expose event) */
    do
	XNextEvent(ws->xdisplay, &event);
    while (event.xany.type != Expose);

    create_xil_display(ws);
    return ws;
}

/* - - - - - - - - - - - - - - - - - - - - - - */
void 
check_events(WindowState * ws)
{
    XEvent event;

    if (XPending(ws->xdisplay) > 0) {
	XNextEvent(ws->xdisplay, &event);
	if (event.xany.type == ClientMessage &&
	    ((XClientMessageEvent *) & event)->data.l[0] == closedownAtom) {
	    close_window(ws);
	    exit(0);
	}
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - */

void 
resize(WindowState * ws, int w, int h)
{
    XEvent event;

    xil_destroy(ws->imageYCC);
    xil_destroy(ws->display);

    ws->w = w;
    ws->h = h;
    XResizeWindow(ws->xdisplay, ws->xwindow, w, h);

    /*
     * Wait for a ConfigureNotify event to ensure the XResizeWindow() is
     * complete before calling xil_create_from_window()
     */
    do
	XNextEvent(ws->xdisplay, &event);
    while (event.xany.type != ConfigureNotify);

    create_xil_display(ws);
}

/* - - - - - - - - - - - - - - - - - - - - - - */

void 
display(WindowState * ws, XilCis cis)
{
    xil_decompress(cis, ws->imageYCC);
    if (ws->display_depth == 24) {
	xil_color_convert(ws->imageYCC, ws->display);
    }
    else if (ws->display_depth == 8) {
	xil_rescale(ws->imageYCC, ws->imageYCC, ws->scale, ws->offset);
	xil_ordered_dither(ws->imageYCC, ws->display, ws->cmap, ws->mask);
    }
    xil_toss(ws->imageYCC);
}

/* - - - - - - - - - - - - - - - - - - - - - - */
void 
close_window(WindowState * ws)
{
    /* must destroy display image before calling XCloseDisplay */
    xil_destroy(ws->display);

    XCloseDisplay(ws->xdisplay);

    free(ws);
}

/* - - - - - - - - - - - - - - - - - - - - - - */
