/*
 * Handle everything related to display
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <xil/xil.h>
#include "o1k_conf.h"
#include "dither.h"
#include "window_state.h"

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

/* - - - - - - - - - - - - - - - - - - - - - - */
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);

  return TRUE;
}

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

WindowState *
initialize_window(XilSystemState state, int w, int h, char* windowname)
{
  XilLookup yuv_to_rgb;
  XEvent event;
  WindowState *ws;
  XSizeHints hints;
  
  ws = (WindowState *) malloc(sizeof(WindowState));
  if (ws == NULL)
    return NULL;
  
  ws->state = state;

  ws->view = (windowname[0] == 'L') ? LOCAL_VIEW : FAR_VIEW;
  
#if 0 /* TODO */
  xil_install_error_handler(state, error_handler);
#endif

#if 1 /* TODO */
  ws->x = 0;
  ws->y = 0;
#else
  /* resolve window position */
  hints.flags = 0;
  if (vidparams->x >= 0) {
    ws->x = vidparams->x;
    hints.flags |= USPosition;
    hints.x = vidparams->x;
  }
  else {
    ws->x = 0;
  }
  if (vidparams->y >= 0) {
    ws->y = vidparams->y;
    hints.flags |= USPosition;
    hints.y = vidparams->y;
  }
  else {
    ws->y = 0;
  }
#endif
  
  ws->w = w;
  ws->h = h;
  
  /* 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 */
  ws->close_atom = XInternAtom(ws->xdisplay, "WM_DELETE_WINDOW", False);
  if (ws->close_atom)
    XSetWMProtocols(ws->xdisplay, ws->xwindow, &ws->close_atom, 1);

  ws->state_atom = XInternAtom(ws->xdisplay, "WM_STATE", False);
  
  /* 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, 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;
}

/* - - - - - - - - - - - - - - - - - - - - - - */
int check_events(WindowState * ws)
{
  XEvent event;
  Atom actual_type;
  int  actual_format;
  unsigned long nitmes;
  unsigned long bytes_after;
  unsigned char *prop;
  int doview;
  
  if (XPending(ws->xdisplay) > 0) {
    XNextEvent(ws->xdisplay, &event);
    if (event.xany.type == ClientMessage){
      if (((XClientMessageEvent *) & event)->data.l[0] == ws->close_atom){
        if (verbose) printf("check_events close window !!!\n");
      }
    }
  }
    
  XGetWindowProperty(ws->xdisplay, ws->xwindow, ws->state_atom, 0, 2, False,
                     AnyPropertyType, &actual_type, &actual_format,
                     &nitmes, &bytes_after, &prop);

  memcpy((unsigned char*)&ws->win_prop_state, prop, sizeof(WinPropState));
  
  if (ws->win_prop_state.cur_state == STATE_VIEW){
    doview = 1;
  } else if (ws->win_prop_state.cur_state == STATE_ICON){
    doview = 0;
  } else {
    if (verbose) printf("window transition state %d\n",ws->win_prop_state.cur_state);
    doview = 0;
  }
  
  XFree(prop);
  
  return doview;
}

int deiconify_window(WindowState * ws)
{
  if (ws->win_prop_state.cur_state == STATE_ICON){
    XUnmapWindow(ws->xdisplay, ws->win_prop_state.cur_icon);
    XMapWindow(ws->xdisplay, ws->xwindow);
  }
  return 0;
}

int iconify_window(WindowState * ws)
{
  if (ws->win_prop_state.cur_state == STATE_VIEW){
    XIconifyWindow(ws->xdisplay, ws->xwindow, DefaultScreen(ws->xdisplay));
  }
  return 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)
{
  xil_state_set_synchronize(ws->state, TRUE);

  /* must destroy display image before calling XCloseDisplay */
  xil_destroy(ws->imageYCC);
  xil_destroy(ws->display);
  XCloseDisplay(ws->xdisplay);
  free(ws);
}

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