/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
/*
 * functions to get an X window up.
 * 
 *  In make_truecolor_window there are a lot of printf()'s and even
 *  exit()'s. What should really happen in those cases? 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

/* shared memory */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>

#include "xstuff.h"
#include "mas/mas.h"


void destroy_xshm_image( XInfo *xinfo );
Visual *FindFullColorVisual ( Display *dpy, int *depth );
void CreateFullColorWindow ( XInfo *xinfo );


/* we don't use shared mem at the moment anyway  */
#ifdef SH_MEM
static int ErrorFlag = 0;

static int HandleXError( Display *dpy, XErrorEvent *event )
{
    ErrorFlag = 1;
    return 0;
}
#endif

void make_truecolor_window (int w, int h, XInfo *xinfo, int perform_map )
{
    Display *display;
    XWindowAttributes winattr;
    int screen;

    char *data;
    char *title;
    
    int tmp, tmp2;
    
    char *name = ":0";
    char *hello = (xinfo->name == NULL) ? "MAS Visualization Device" : xinfo->name;
    
    int i = 0;
    int wx, hx;
    long unsigned int *l;
    unsigned short int *r;

        
/*     if (dbl){ */
/*         w *= 2; */
/*         h *= 2; */
/*     } */

    title = masc_construct_title( hello );
    
    xinfo->hints.width = w;
    xinfo->hints.height = h;
        
    xinfo[i].ExistingWindow = 0;
    xinfo[i].ditherType = 7; /*FULL_COLOR_DITHER*/;
    xinfo[i].owncmFlag = 0;
    xinfo[i].display = NULL;
    xinfo[i].hints.x = 200;
    xinfo[i].hints.y = 200;
    xinfo[i].visual = NULL;
    xinfo[i].name = "ugh";
    xinfo[i].cmap = 0;
    xinfo[i].gc = 0;

    xinfo->wi = w;
    xinfo->he = h;
    
    
    display = xinfo->display = XOpenDisplay(name);
    if (xinfo->display == NULL) {
        fprintf(stderr, "Cannot open display\n");
        exit(-2);
    }
    screen = DefaultScreen (display);

    CreateFullColorWindow (xinfo);

#ifdef LISTEN_TO_EXPOSE_EVENTS
    XSelectInput(display, xinfo->window, StructureNotifyMask|ExposureMask);
#endif
#ifndef LISTEN_TO_EXPOSE_EVENTS
    XSelectInput(display, xinfo->window, StructureNotifyMask);
#endif
    
    /* Tell other applications about this window */
    if( title )
    {
        
        XSetStandardProperties (display, xinfo->window, title,
                                title, None, NULL, 0, &xinfo->hints);
        masc_rtfree( title );
    }
    else
    {
        
        XSetStandardProperties (display, xinfo->window, hello,
                                hello, None, NULL, 0, &xinfo->hints);
    }
    
    /* Map window. */


    if( perform_map )
    {
        XMapWindow(display, xinfo->window);
        /* Wait for map. */
        while (1) {
            XEvent	xev;
            
            XNextEvent(display, &xev);
            if (xev.type == MapNotify && xev.xmap.event == xinfo->window) {
                break;
            }
        }
    }
    
    
/*     XSelectInput(display, xinfo->window, NoEventMask); */
    
    
    if (xinfo->gc==0) {
        xinfo->gc = XCreateGC(xinfo->display, xinfo->window, 0, 0);
    }
    xinfo->ximage = NULL;
    
    XGetWindowAttributes(xinfo->display, xinfo->window, &winattr);


#ifdef SH_MEM
    if(check_for_xshm(xinfo->display)==0)
    {
        printf("Can't use X shared memory extension. Maybe recompile without -DSH_MEM ?\n");
        exit(1);
    }

    xinfo->ximage = XShmCreateImage( xinfo->display,
                                     xinfo->visual, xinfo->depth,
                                     ZPixmap, NULL, &xinfo->shminfo,
                                     xinfo->wi, xinfo->he );
    if (xinfo->ximage == NULL) {
        printf("XShmCreateImage failed!\n");
        exit(1);
    }

    xinfo->shminfo.shmid = shmget( IPC_PRIVATE, xinfo->ximage->bytes_per_line
                                   * xinfo->ximage->height, IPC_CREAT|0777 );
    if (xinfo->shminfo.shmid < 0) {
/*         perror("shmget"); */
      XDestroyImage( xinfo->ximage );
      xinfo->ximage = NULL;
/*       error( "alloc_back_buffer: Shared memory error (shmget), disabling." ); */
/*       c->shm = 0; */
/*       return NULL; */
      printf("shmget() failed!\n");
      exit(1);
    }

    xinfo->shminfo.shmaddr = xinfo->rgbdata  = xinfo->ximage->data 
        = (char*)shmat( xinfo->shminfo.shmid, 0, 0 );

    if (xinfo->shminfo.shmaddr == (char *) -1) {
        /* perror("alloc_back_buffer"); */
        XDestroyImage( xinfo->ximage );
        xinfo->ximage = NULL;
        printf("shmat() failed\n");
        exit(1);
    }

    xinfo->shminfo.readOnly = False;
    ErrorFlag = 0;
/*     XSetErrorHandler( HandleXError ); */
    /* This may trigger the X protocol error we're ready to catch: */
    XShmAttach( xinfo->display, &xinfo->shminfo );
    XSync( xinfo->display, False );

    if (ErrorFlag) {
        /* we are on a remote display, this error is normal, don't print it */
        XFlush( xinfo->display );
        ErrorFlag = 0;
        XDestroyImage( xinfo->ximage );
        shmdt( xinfo->shminfo.shmaddr );
        shmctl( xinfo->shminfo.shmid, IPC_RMID, 0 );
        exit(1);
    }
    
    shmctl( xinfo->shminfo.shmid, IPC_RMID, 0 ); /* nobody else needs it */
    
    
#endif
#ifndef SH_MEM    

    /* FIXME: need to free these! */
    if (xinfo->depth == 24)
        xinfo->rgbdata = malloc(4*w*h);

    if (xinfo->depth == 16)
        xinfo->rgbdata = malloc(2*w*h);

    xinfo->ximage = XCreateImage (xinfo->display,
                                  xinfo->visual, xinfo->depth, ZPixmap,
                                  0, xinfo->rgbdata,
                                  xinfo->wi, xinfo->he, 32, 0);
#endif    
    
}



void delete_window (void *xi)
{
    XInfo *xinfo = xi;
    
    XDestroyWindow(xinfo->display, xinfo->window);    
    return;
}

/* I think it would be a really good idea if this function was
   actually called from somewhere! Right now it isn't, since I don't know
   from where I would do that. */
void destroy_xshm_image( XInfo *xinfo )
{
    XShmDetach( xinfo->display, &xinfo->shminfo );
    XDestroyImage( xinfo->ximage );
    shmdt( xinfo->shminfo.shmaddr );
}


Visual *FindFullColorVisual ( Display *dpy, int *depth )
{
  XVisualInfo vinfo;
  XVisualInfo *vinfo_ret;
  int numitems, maxdepth;
  
  vinfo.class = TrueColor;
  
  vinfo_ret = XGetVisualInfo(dpy, VisualClassMask, &vinfo, &numitems);
  
  if (numitems == 0) return NULL;

  maxdepth = 0;
  while(numitems > 0) {
    if (vinfo_ret[numitems-1].depth > maxdepth) {
      maxdepth = vinfo_ret[numitems-1 ].depth;
    }
    numitems--;
  }
  XFree((void *) vinfo_ret);

  if (maxdepth < 16) return NULL;

  if (XMatchVisualInfo(dpy, DefaultScreen(dpy), maxdepth, 
		       TrueColor, &vinfo)) {
    *depth = maxdepth;
    return vinfo.visual;
  }
  
  return NULL;
}


void CreateFullColorWindow ( XInfo *xinfo )
{
  int depth;
  Visual *visual;
  XSetWindowAttributes xswa;
  unsigned long mask;
  unsigned int class;
  int screen;
  Display *dpy=xinfo->display;
  int x = xinfo->hints.x,
      y = xinfo->hints.y;
  unsigned int w = xinfo->hints.width,
               h = xinfo->hints.height;

  screen = XDefaultScreen(dpy);
  class = InputOutput;	/* Could be InputOnly */
  if (xinfo->visual == NULL) {
    xinfo->visual = visual = FindFullColorVisual (dpy, &depth);
    xinfo->depth = depth;
  } else {
     visual=xinfo->visual;
     depth=xinfo->depth;
  }
    
  if (visual == NULL) {
    return;
  }
  mask = CWBackPixel | CWColormap | CWBorderPixel;
  if (xinfo->cmap==0) {
  xswa.colormap = XCreateColormap(dpy,
				  XRootWindow(dpy, screen),
                                  visual, AllocNone);
  } else xswa.colormap = xinfo->cmap;
  xswa.background_pixel = BlackPixel(dpy, DefaultScreen(dpy));
  xswa.border_pixel = WhitePixel(dpy, DefaultScreen(dpy));

  xinfo->window = XCreateWindow(dpy, RootWindow(dpy, screen), x, y, w, h,
    (unsigned int) 1, depth, class, visual, mask, &xswa);
}


/*
 * Check if the X Shared Memory extension is available.
 * Return:  0 = not available
 *          1 = shared XImage support available
 *          2 = shared Pixmap support available also
 */
int check_for_xshm( Display *display )
{
   int major, minor, ignore;
   Bool pixmaps;

   if (XQueryExtension( display, "MIT-SHM", &ignore, &ignore, &ignore )) {
      if (XShmQueryVersion( display, &major, &minor, &pixmaps )==True) {
	 return (pixmaps==True) ? 2 : 1;
      }
      else {
	 return 0;
      }
   }
   else {
      return 0;
   }
}
