/*
 * Copyright (c) 1999 Ethan Fischer <allanon@crystaltokyo.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

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

#include "../../include/aftersteplib.h"

static int
pixmap_error_handler (Display * dpy, XErrorEvent * error)
{
  return 0;
}

Pixmap
background_pixmap_property (Display * dpy, Window win)
{
  Pixmap pixmap = None;
  Atom _XROOTPMAP_ID = XInternAtom (dpy, "_XROOTPMAP_ID", False);
  if (_XROOTPMAP_ID != None)
    {
      Atom type;
      int format;
      unsigned long length, after;
      unsigned char *data;
      if (XGetWindowProperty (dpy, win, _XROOTPMAP_ID, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data) == Success)
	{
	  if (type == XA_PIXMAP)
	    pixmap = *((Pixmap *) data);
	  XFree (data);
	}
    }
  /* the _XROOTPMAP_ID may not point to a valid pixmap, so check it */
  if (pixmap != None)
    {
      int (*old) (Display *, XErrorEvent *) = XSetErrorHandler (pixmap_error_handler);
      Window root;
      int junk;
      if (!XGetGeometry (dpy, pixmap, &root, &junk, &junk, &junk, &junk, &junk, &junk))
	pixmap = None;
      XSetErrorHandler (old);
    }
  return pixmap;
}

void
darken_image (XImage * image, XColor color)
{
  color.red = color.red / 256 + 1;
  color.green = color.green / 256 + 1;
  color.blue = color.blue / 256 + 1;
  switch (DefaultDepth (dpy, screen))
    {
    case 15:
    case 16:
      {
	unsigned short *p1 = (unsigned short *) image->data;
	unsigned short *pf = (unsigned short *) (image->data + image->height * image->bytes_per_line);
	unsigned long rk, gk, bk;
	rk = DefaultVisual (dpy, screen)->red_mask;
	gk = DefaultVisual (dpy, screen)->green_mask;
	bk = DefaultVisual (dpy, screen)->blue_mask;
	while (p1 < pf)
	  {
	    unsigned short *p = p1;
	    unsigned short *pl = p1 + image->width;
	    for (; p < pl; p++)
	      {
		unsigned long v = *p;
		v = (v & ~rk) | (((v & rk) * color.red / 256) & rk);
		v = (v & ~gk) | (((v & gk) * color.green / 256) & gk);
		v = (v & ~bk) | (((v & bk) * color.blue / 256) & bk);
		*p = v;
	      }
	    p1 = (unsigned short *) ((char *) p1 + image->bytes_per_line);
	  }
	break;
      }
    case 24:
      {
	unsigned char *p1 = (unsigned char *) image->data;
	unsigned char *pf = (unsigned char *) (image->data + image->height * image->bytes_per_line);
	while (p1 < pf)
	  {
	    unsigned char *p = p1;
	    unsigned char *pl = p1 + image->width * 3;
	    for (; p < pl; p += 3)
	      {
		p[0] = (unsigned long) p[0] * color.red / 256;
		p[1] = (unsigned long) p[1] * color.green / 256;
		p[2] = (unsigned long) p[2] * color.blue / 256;
	      }
	    p1 = (unsigned char *) ((char *) p1 + image->bytes_per_line);
	  }
	break;
      }
    case 32:
      {
	unsigned long *p1 = (unsigned long *) image->data;
	unsigned long *pf = (unsigned long *) (image->data + image->height * image->bytes_per_line);
	unsigned long rk, gk, bk;
	rk = DefaultVisual (dpy, screen)->red_mask;
	gk = DefaultVisual (dpy, screen)->green_mask;
	bk = DefaultVisual (dpy, screen)->blue_mask;
	while (p1 < pf)
	  {
	    unsigned long *p = p1;
	    unsigned long *pl = p1 + image->width;
	    for (; p < pl; p++)
	      {
		unsigned long v = *p;
		v = (v & ~rk) | (((v & rk) * color.red / 256) & rk);
		v = (v & ~gk) | (((v & gk) * color.green / 256) & gk);
		v = (v & ~bk) | (((v & bk) * color.blue / 256) & bk);
		*p = v;
	      }
	    p1 = (unsigned long *) ((char *) p1 + image->bytes_per_line);
	  }
	break;
      }
    }
}

/****************************************************************************
 *
 * darken an area of a pixmap by a color
 *
 ***************************************************************************/
void
darken_pixmap (Pixmap pixmap, XColor color, int x, int y, int width, int height)
{
  XImage *image;
  image = XGetImage (dpy, pixmap, x, y, width, height, ~0, ZPixmap);
  if (image != NULL)
    {
      darken_image (image, color);
      XPutImage (dpy, pixmap, DefaultGC (dpy, screen), image, 0, 0, x, y, width, height);
      XDestroyImage (image);
    }
  else
    {
      fprintf (stderr, "!!\n");
    }
}

/****************************************************************************
 *
 * fill part of a pixmap with the root pixmap, offset properly to look 
 * "transparent"
 *
 ***************************************************************************/
int
fill_with_background (Pixmap pixmap, int x, int y, int width, int height, int root_x, int root_y)
{
  Pixmap rootmap = background_pixmap_property (dpy, RootWindow (dpy, screen));
  if (rootmap != None)
    {
      GC gc;
      XGCValues gcv;
      gcv.tile = rootmap;
      gcv.fill_style = FillTiled;
      gcv.ts_x_origin = -root_x;
      gcv.ts_y_origin = -root_y;
      gc = XCreateGC (dpy, rootmap, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &gcv);
      XFillRectangle (dpy, pixmap, gc, x, y, width, height);
      XFreeGC (dpy, gc);
    }
  return rootmap != None;
}

/****************************************************************************
 *
 * grab a section of the screen and darken it
 *
 ***************************************************************************/
int
fill_with_darkened_background (Pixmap pixmap, XColor color, int x, int y, int width, int height, int root_x, int root_y)
{
  if (fill_with_background (pixmap, x, y, width, height, root_x, root_y))
    {
      darken_pixmap (pixmap, color, x, y, width, height);
      return 1;
    }
  return 0;
}
