/*
 * Copyright © 2008 Bart Massey <bart@cs.pdx.edu>
 *
 * 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 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.
 *
 * Except as contained in this notice, the names of the authors or
 * their institutions shall not be used in advertising or otherwise to
 * promote the sale, use or other dealings in this Software without
 * prior written authorization from the authors.
 */

/* gcc -g -O2 -Wall `pkg-config --cflags --libs xcb` -o test xcb_image.o test_xcb_image.c */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <xcb/xcb.h>

#include <xcb/xcb_aux.h>
#include "xcb_image.h"

#define W_W 128
#define W_H 128

static void
reflect_window (xcb_connection_t *c,
		xcb_drawable_t win,
		xcb_drawable_t new_win,
		xcb_gcontext_t gc,
		uint16_t      width,
		uint16_t      height)
{
  xcb_image_t *image;
  uint32_t    pixel1;
  uint32_t    pixel2;
  int32_t     left_x;
  int32_t     right_x;
  int32_t     y;
  int         format;

  format = XCB_IMAGE_FORMAT_XY_PIXMAP;

  printf ("get_image %d %d\n", width, height);
  image = xcb_image_get (c, win,
			 0, 0, width, height,
			 0xaaaaaaaa,
			 format);
  if (!image) {
      printf("xcb_image_get failed: exiting\n");
      exit(1);
  }

  printf ("Create image summary:\n");
  printf (" * format................: %d\n", image->format);
  printf (" * byte order............: %d\n", image->byte_order);
  printf (" * bitmap order..........: %d\n", image->bit_order);
  printf (" * bitmap pad............: %d\n", image->scanline_pad);
  printf (" * depth.................: %d\n", image->depth);
  printf (" * bytes/line............: %d\n", image->stride);
  printf (" * bits/pixel (or unit)..: %d\n", image->bpp);
  printf (" * plane_mask............: %#x\n", image->plane_mask);

  printf ("bpl %d %d\n", image->stride, image->height);

  printf("calculating reflection -- this may take awhile...\n");
  
  for (left_x = 0 ; left_x < width/2 ; left_x++)
    {
      for (y = 0 ; y < height ; y++)
	{
	  pixel1 = xcb_image_get_pixel (image, left_x, y);
	  right_x = width - left_x-1;
	  if (left_x != right_x)
	    {
	      pixel2 = xcb_image_get_pixel (image, right_x, y);
	      xcb_image_put_pixel (image, left_x, y, pixel2);
	    }
	  xcb_image_put_pixel (image, right_x, y, pixel1);
	}
    }

  printf("putting image\n");

  xcb_image_put (c, new_win, gc, image, 0, 0, 0);
  image = xcb_image_get (c, new_win,
			 0, 0, width, height,
			 ~0,
			 format);

  printf ("done\n");
}

int
main (int argc, char *argv[])
{
  xcb_connection_t   *c;
  xcb_screen_t       *screen;
  xcb_drawable_t      win;
  xcb_drawable_t      new_win;
  xcb_drawable_t      rect;
  xcb_rectangle_t     rect_coord = { 0, 0, W_W, W_H};
  xcb_gcontext_t      bgcolor, fgcolor;
  xcb_point_t         points[2];
  uint32_t           mask;
  uint32_t           valgc[2];
  uint32_t           valwin[3];
  int              depth;
  int              screen_nbr;
  xcb_generic_event_t *e;
  
  /* Open the connexion to the X server and get the first screen */
  c = xcb_connect (NULL, &screen_nbr);
  screen = xcb_aux_get_screen (c, screen_nbr);
  depth = xcb_aux_get_depth (c, screen);

  /* Create a black graphic context for drawing in the foreground */
  win = screen->root;

  fgcolor = xcb_generate_id(c);
  mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
  valgc[0] = screen->black_pixel;
  valgc[1] = 0; /* no graphics exposures */
  xcb_create_gc(c, fgcolor, win, mask, valgc);

  bgcolor = xcb_generate_id(c);
  mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
  valgc[0] = screen->white_pixel;
  valgc[1] = 0; /* no graphics exposures */
  xcb_create_gc(c, bgcolor, win, mask, valgc);

  /* Ask for our window's Id */
  win = xcb_generate_id(c);

  /* Create the window */
  mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE;
  valwin[0] = screen->white_pixel;
  valwin[1] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_EXPOSURE;
  valwin[2] = XCB_EVENT_MASK_BUTTON_PRESS;
  xcb_create_window (c,                        /* Connection          */
 		   0,                        /* depth               */
		   win,                      /* window Id           */
		   screen->root,             /* parent window       */
		   0, 0,                     /* x, y                */
		   W_W, W_H,                 /* width, height       */
		   10,                       /* border_width        */
		   XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class               */
		   screen->root_visual,      /* visual              */
		   mask, valwin);                 /* masks, not used yet */

  /* From the tutorial: set the window title. */
  xcb_change_property (c, XCB_PROP_MODE_REPLACE, win,
                       XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
                       strlen ("drew"), "drew");

  /* Map the window on the screen */
  xcb_map_window (c, win);

  /* Create a Pixmap that will fill the window */
  rect = xcb_generate_id (c);
  xcb_create_pixmap(c, depth, rect, win, W_W, W_H);
  xcb_poly_fill_rectangle(c, rect, bgcolor, 1, &rect_coord);
  points[0].x = 0;
  points[0].y = 0;
  points[1].x = W_W;
  points[1].y = W_H;
  xcb_poly_line(c, XCB_COORD_MODE_ORIGIN, rect, fgcolor, 2, points);
  points[0].x = W_W / 4;
  points[0].y = 0;
  points[1].x = W_W / 2;
  points[1].y = W_H / 2;
  xcb_poly_line(c, XCB_COORD_MODE_ORIGIN, rect, fgcolor, 2, points);

  /* Ask for our window's Id */
  new_win = xcb_generate_id(c);

  /* Create the window */
  mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_DONT_PROPAGATE;
  valwin[0] = screen->white_pixel;
  valwin[1] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_EXPOSURE;
  valwin[2] = XCB_EVENT_MASK_BUTTON_PRESS;
  xcb_create_window (c,                        /* Connection          */
 		   0,                        /* depth               */
		   new_win,                  /* window Id           */
		   screen->root,             /* parent window       */
		   0, 0,                     /* x, y                */
		   W_W, W_H,                 /* width, height       */
		   10,                       /* border_width        */
		   XCB_WINDOW_CLASS_INPUT_OUTPUT,/* class               */
		   screen->root_visual,      /* visual              */
		   mask, valwin);                 /* masks, not used yet */
  /* From the tutorial: set the window title. */
  xcb_change_property (c, XCB_PROP_MODE_REPLACE, new_win,
                       XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
                       strlen ("got"), "got");


  

  /* Map the window on the screen */
  xcb_map_window (c, new_win);


  xcb_flush (c); 

  while ((e = xcb_wait_for_event(c)))
    {
      switch (e->response_type)
	{ 
	case XCB_EXPOSE:
	  {
	    xcb_copy_area(c, rect, win, bgcolor,
			0, 0, 0, 0, W_W, W_H);
	    reflect_window (c, win, new_win,
			    fgcolor,
			    W_W, W_H);
	    xcb_flush (c);
	    break;
	  }
	}
      free (e);
    }

  return 1;
}
