#include "pipe/p_compiler.h"
#include "pipe/p_context.h"
#include "pipe/p_screen.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "target-helpers/inline_sw_helper.h"
#include "target-helpers/inline_debug_helper.h"
#include "state_tracker/xlib_sw_winsys.h"
#include "state_tracker/graw.h"

#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xutil.h>
#include <stdio.h>

static struct {
   Display *display;
   void (*draw)(void);
} graw;


static struct pipe_screen *
graw_create_screen( void )
{
   struct pipe_screen *screen = NULL;
   struct sw_winsys *winsys = NULL;

   /* Create the underlying winsys, which performs presents to Xlib
    * drawables:
    */
   winsys = xlib_create_sw_winsys( graw.display );
   if (winsys == NULL)
      return NULL;

   screen = sw_screen_create( winsys );

   /* Inject any wrapping layers we want to here:
    */
   return debug_screen_wrap( screen );
}


struct pipe_screen *
graw_create_window_and_screen( int x,
                               int y,
                               unsigned width,
                               unsigned height,
                               enum pipe_format format,
                               void **handle)
{
   struct pipe_screen *screen = NULL;
   struct xlib_drawable *xlib_handle = NULL;
   XSetWindowAttributes attr;
   Window root;
   Window win = 0;
   XVisualInfo templat, *visinfo = NULL;
   unsigned mask;
   int n;
   int scrnum;

   graw.display = XOpenDisplay(NULL);
   if (graw.display == NULL)
      return NULL;

   scrnum = DefaultScreen( graw.display );
   root = RootWindow( graw.display, scrnum );


   if (graw.display == NULL)
      goto fail;

   xlib_handle = CALLOC_STRUCT(xlib_drawable);
   if (xlib_handle == NULL)
      goto fail;


   mask = VisualScreenMask | VisualDepthMask | VisualClassMask;
   templat.screen = DefaultScreen(graw.display);
   templat.depth = 32;
   templat.class = TrueColor;

   visinfo = XGetVisualInfo(graw.display, mask, &templat, &n);
   if (!visinfo) {
      printf("Error: couldn't get an RGB, Double-buffered visual\n");
      exit(1);
   }

   /* See if the requirested pixel format matches the visual */
   if (visinfo->red_mask == 0xff0000 &&
       visinfo->green_mask == 0xff00 &&
       visinfo->blue_mask == 0xff) {
      if (format != PIPE_FORMAT_B8G8R8A8_UNORM)
         goto fail;
   }
   else if (visinfo->red_mask == 0xff &&
            visinfo->green_mask == 0xff00 &&
            visinfo->blue_mask == 0xff0000) {
      if (format != PIPE_FORMAT_R8G8B8A8_UNORM)
         goto fail;
   }
   else {
      goto fail;
   }

   /* window attributes */
   attr.background_pixel = 0;
   attr.border_pixel = 0;
   attr.colormap = XCreateColormap( graw.display, root, visinfo->visual, AllocNone);
   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
   /* XXX this is a bad way to get a borderless window! */
   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

   win = XCreateWindow( graw.display, root, x, y, width, height,
		        0, visinfo->depth, InputOutput,
		        visinfo->visual, mask, &attr );


   /* set hints and properties */
   {
      char *name = NULL;
      XSizeHints sizehints;
      sizehints.x = x;
      sizehints.y = y;
      sizehints.width  = width;
      sizehints.height = height;
      sizehints.flags = USSize | USPosition;
      XSetNormalHints(graw.display, win, &sizehints);
      XSetStandardProperties(graw.display, win, name, name,
                              None, (char **)NULL, 0, &sizehints);
   }

   XMapWindow(graw.display, win);
   while (1) {
      XEvent e;
      XNextEvent( graw.display, &e );
      if (e.type == MapNotify && e.xmap.window == win) {
	 break;
      }
   }
   
   xlib_handle->visual = visinfo->visual;
   xlib_handle->drawable = (Drawable)win;
   xlib_handle->depth = visinfo->depth;
   *handle = (void *)xlib_handle;

   screen = graw_create_screen();
   if (screen == NULL)
      goto fail;

   XFree(visinfo);
   return screen;

fail:
   if (screen)
      screen->destroy(screen);

   if (xlib_handle)
      FREE(xlib_handle);

   if (visinfo)
      XFree(visinfo);

   if (win)
      XDestroyWindow(graw.display, win);

   return NULL;
}


void 
graw_set_display_func( void (*draw)( void ) )
{
   graw.draw = draw;
}

void
graw_main_loop( void )
{
   int i;
   for (i = 0; i < 10; i++) {
      graw.draw();
      sleep(1);
   }
}