/* -*-c-*- */
/* Copyright (C) 2002  Olivier Chapuis */
/* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* ---------------------------- included header files ---------------------- */

#include "config.h"

#include <stdio.h>

#include <X11/Xlib.h>

#include <fvwmlib.h>
#include "PictureBase.h"
#include "Graphics.h"
#include "PictureGraphics.h"
#include "FRenderInit.h"
#include "FRender.h"
#include "FRenderInterface.h"

/* ---------------------------- local definitions -------------------------- */

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- imports ------------------------------------ */

/* ---------------------------- included code files ------------------------ */

/* ---------------------------- local types -------------------------------- */

/* ---------------------------- forward declarations ----------------------- */

/* ---------------------------- local variables ---------------------------- */

static FRenderPictFormat *PFrenderVisualFormat = NULL;
static FRenderPictFormat *PFrenderAlphaFormat = NULL;
static FRenderPictFormat *PFrenderMaskFormat = NULL;
static FRenderPictFormat *PFrenderDirectFormat = NULL;
static FRenderPictFormat *PFrenderAbsoluteFormat = NULL;
Bool FRenderVisualInitialized = False;

/* #define USE_ABSOLUTE_FORMAT 1*/

/* ---------------------------- exported variables (globals) --------------- */

/* ---------------------------- local functions ---------------------------- */

static
void FRenderVisualInit(Display *dpy)
{
	FRenderPictFormat pf;

	if (!XRenderSupport || !FRenderGetExtensionSupported())
	{
		return;
	}

	PFrenderVisualFormat = FRenderFindVisualFormat (dpy, Pvisual);
	if (!PFrenderVisualFormat)
	{
		fprintf(stderr,"[fvwmlibs][FRenderInit] -- ERROR: "
			"fail to create XRender Visual Format\n");
		return;
	}
	pf.depth = 8;
	pf.type = FRenderPictTypeDirect;
	pf.direct.alpha = 0;
	pf.direct.alphaMask = 0xff;
	PFrenderAlphaFormat = FRenderFindFormat(
		dpy, FRenderPictFormatType| FRenderPictFormatDepth|
		FRenderPictFormatAlpha| FRenderPictFormatAlphaMask, &pf, 0);
	if (!PFrenderAlphaFormat)
	{
		fprintf(stderr,"[fvwmlibs][FRenderInit] -- ERROR: "
			"fail to create XRender Alpha Format\n");
		return;
	}
	pf.depth = 1;
	pf.type = FRenderPictTypeDirect;
	pf.direct.alpha = 0;
	pf.direct.alphaMask = 1;
	PFrenderMaskFormat = FRenderFindFormat(
		dpy, FRenderPictFormatType| FRenderPictFormatDepth|
		FRenderPictFormatAlpha| FRenderPictFormatAlphaMask, &pf, 0);
	if (!PFrenderMaskFormat)
	{
		fprintf(stderr,"[fvwmlibs][FRenderInit] -- ERROR: "
			"fail to create XRender Mask Format\n");
		return;
	}
	pf.depth = 24;
	pf.type = FRenderPictTypeDirect;
	pf.direct.alpha = 0;
	pf.direct.alphaMask = 0;
	pf.direct.red = 16;
	pf.direct.redMask = 0xff;
	pf.direct.green = 8;
	pf.direct.greenMask = 0xff;
	pf.direct.blue = 0;
	pf.direct.blueMask = 0xff;
	PFrenderDirectFormat = FRenderFindFormat(
		dpy, FRenderPictFormatType| FRenderPictFormatDepth|
		FRenderPictFormatRed| FRenderPictFormatRedMask|
		FRenderPictFormatGreen| FRenderPictFormatGreenMask|
		FRenderPictFormatBlue| FRenderPictFormatBlueMask|
		FRenderPictFormatAlpha| FRenderPictFormatAlphaMask, &pf, 0);
	if (!PFrenderDirectFormat)
	{
		fprintf(stderr,"[fvwmlibs][FRenderInit] -- ERROR: "
			"fail to create XRender Direct Format\n");
		return;
	}
	pf.depth = 32;
	pf.type = FRenderPictTypeDirect;
	pf.direct.alpha = 24;
	pf.direct.alphaMask = 0xff;
	pf.direct.red = 16;
	pf.direct.redMask = 0xff;
	pf.direct.green = 8;
	pf.direct.greenMask = 0xff;
	pf.direct.blue = 0;
	pf.direct.blueMask = 0xff;
	PFrenderAbsoluteFormat = FRenderFindFormat(
		dpy, FRenderPictFormatType| FRenderPictFormatDepth|
		FRenderPictFormatRed| FRenderPictFormatRedMask|
		FRenderPictFormatGreen| FRenderPictFormatGreenMask|
		FRenderPictFormatBlue| FRenderPictFormatBlueMask|
		FRenderPictFormatAlpha| FRenderPictFormatAlphaMask, &pf, 0);
	if (!PFrenderAbsoluteFormat)
	{
		fprintf(stderr,"[fvwmlibs][FRenderInit] -- ERROR: "
			"fail to create XRender Absolute Format\n");
		return;
	}
}

static
Bool FRenderCompositeAndCheck(
	Display *dpy, int op, FRenderPicture src, FRenderPicture alpha,
	FRenderPicture dest, int x, int y, int a_x, int a_y,
	int d_x, int d_y, int d_w, int d_h)
{
	FRenderComposite(
		dpy, op, src, alpha, dest, x, y, a_x, a_y, d_x, d_y, d_w, d_h);

	return True;

}

static
Bool FRenderCreateShadePicture(
	Display *dpy, Window win, int alpha_percent)
{
	static Pixmap shade_pixmap = None;
	static FRenderPicture shade_picture = None;
	static int saved_alpha_percent = 0;
	Bool force_update = False;
	FRenderColor frc;

	if (!XRenderSupport || !FRenderGetExtensionSupported())
	{
		return 0;
	}

	/* FRender Visuals should be already initialized */

	if (!shade_pixmap || !shade_picture)
	{
		FRenderPictureAttributes  pa;

		if (!shade_pixmap)
		{
			shade_pixmap = XCreatePixmap(dpy, win, 1, 1, 8);
		}
		pa.repeat = True;
		if (shade_pixmap)
		{
			shade_picture = FRenderCreatePicture(
				dpy, shade_pixmap, PFrenderAlphaFormat,
				FRenderCPRepeat, &pa);
		}
		force_update = True;
	}
	if (shade_picture &&
	    (alpha_percent != saved_alpha_percent || force_update))
	{
		frc.red = frc.green = frc.blue = 0;
		frc.alpha = 0xffff * (alpha_percent)/100;
		FRenderFillRectangle(
			dpy, FRenderPictOpSrc, shade_picture, &frc,
			0, 0, 1, 1);
		saved_alpha_percent = alpha_percent;
	}

	return shade_picture;
}

static
Bool FRenderTintPicture(
	Display *dpy, Window win, Pixel tint, int tint_percent,
	FRenderPicture dest_picture,
	int dest_x, int dest_y, int dest_w, int dest_h)
{
	static Pixel saved_tint = 0;
	static int saved_tint_percent = 0;
	static Pixmap tint_pixmap = None;
	static FRenderPicture tint_picture = None;
	FRenderPicture shade_picture = None;
	FRenderColor frc_tint;
	Bool force_update = False;
	FRenderPictureAttributes  pa;
	int rv = 0;

	if (!XRenderSupport)
	{
		return 0;
	}

	if (!tint_pixmap || !tint_picture)
	{
		pa.repeat = True;
		if (!tint_pixmap)
		{
			tint_pixmap = XCreatePixmap(dpy, win, 1, 1, 32);
		}
		if (tint_pixmap)
		{
			tint_picture = FRenderCreatePicture(
				dpy, tint_pixmap,
				PFrenderAbsoluteFormat,
				FRenderCPRepeat, &pa);
			if (!tint_picture)
			{
				goto bail;
			}
		}
		else
		{
			goto bail;
		}
		force_update = True;
	}
	if (tint_picture &&
	    (tint != saved_tint || tint_percent != saved_tint_percent ||
	     force_update))
	{
		XColor color;
		float alpha_factor = (float)tint_percent/100;

		force_update = False;
		color.pixel = tint;
		XQueryColor(dpy, Pcmap, &color);
		frc_tint.red = color.red * alpha_factor;
		frc_tint.green = color.green * alpha_factor;
		frc_tint.blue = color.blue * alpha_factor;
		frc_tint.alpha = 0xffff * alpha_factor;
		FRenderFillRectangle(
			dpy, FRenderPictOpSrc, tint_picture, &frc_tint,
			0, 0, 1, 1);
		saved_tint = tint;
		saved_tint_percent = tint_percent;
	}
	if (!shade_picture)
	{
		shade_picture = FRenderCreateShadePicture(dpy, win, 100);
	}

	rv = FRenderCompositeAndCheck(
		dpy, FRenderPictOpOver, tint_picture, shade_picture,
		dest_picture, 0, 0, 0, 0, dest_x, dest_y, dest_w, dest_h);

 bail:
	return rv;
}

/* ---------------------------- interface functions ------------------------ */


Bool FRenderTintRectangle(
	Display *dpy, Window win, Pixmap mask, Pixel tint, int tint_percent,
	Drawable d, int dest_x, int dest_y, int dest_w, int dest_h)
{
	FRenderPicture dest_picture = None;
	FRenderPictureAttributes  pa;
	unsigned int val = 0;
	Bool rv = True;


	if (!XRenderSupport || !FRenderGetExtensionSupported())
	{
		return 0;
	}

	if (!FRenderVisualInitialized)
	{
		FRenderVisualInitialized = True;
		FRenderVisualInit(dpy);
	}

	if (!PFrenderVisualFormat || !PFrenderAlphaFormat ||
	    !PFrenderAbsoluteFormat || !PFrenderMaskFormat)
	{
		return 0;
	}

	pa.clip_mask = mask;
	val = FRenderCPClipMask;

	if (!(dest_picture = FRenderCreatePicture(
		dpy, d, PFrenderVisualFormat, val, &pa)))
	{
		return 0;
	}

	rv = FRenderTintPicture(
		dpy, win, tint_percent, tint, dest_picture, dest_x, dest_y,
		dest_w, dest_h);

	FRenderFreePicture(dpy, dest_picture);
	return rv;
}

int FRenderRender(
	Display *dpy, Window win, Pixmap pixmap, Pixmap mask, Pixmap alpha,
	int depth, int added_alpha_percent, Pixel tint, int tint_percent,
	Drawable d, GC gc, GC alpha_gc,
	int src_x, int src_y, int src_w, int src_h,
	int dest_x, int dest_y, int dest_w, int dest_h,
	Bool do_repeat)
{
	FRenderColor frc;
	Pixmap pixmap_copy = None;
	Pixmap alpha_copy = None;
	FRenderPicture shade_picture = None;
	FRenderPicture alpha_picture = None;
	FRenderPicture mask_picture = None;
	FRenderPicture src_picture = None;
	FRenderPicture dest_picture = None;
	FRenderPicture root_picture = None;
	FRenderPictureAttributes  pa;
	unsigned long pam = 0;
	int alpha_x = src_x;
	int alpha_y = src_y;
	Bool rv = False;
	Bool free_alpha_gc = False;

	if (!XRenderSupport || !FRenderGetExtensionSupported())
	{
		return 0;
	}

	if (!FRenderVisualInitialized)
	{
		FRenderVisualInitialized = True;
		FRenderVisualInit(dpy);
	}

	if (!PFrenderVisualFormat || !PFrenderAlphaFormat ||
	    !PFrenderAbsoluteFormat || !PFrenderMaskFormat)
	{
		return 0;
	}

	/* it is a bitmap ? */
	if (Pdepth != depth && pixmap)
	{
		pixmap_copy = PictureBitmapToPixmap(
			dpy, win, pixmap, Pdepth, gc, src_x, src_y, src_w,
			src_h);
		src_x = src_y = 0;
	}

	pam = FRenderCPRepeat;
	if (do_repeat)
	{
		pa.repeat = True;
	}
	else
	{
		pa.repeat = False;
	}

	/*
	 * build the src_picture
	 */
	if (pixmap == ParentRelative)
	{
		/* need backing store and good preparation of the win */
		if (gc == None)
		{
			gc = PictureDefaultGC(dpy, win);
		}
		pixmap_copy = XCreatePixmap(dpy, win, src_w, src_h, Pdepth);
		if (pixmap_copy && gc)
		{
			XCopyArea(
				dpy, win, pixmap_copy, gc,
				src_x, src_y, src_w, src_h, 0, 0);
		}
		src_x = src_y = 0;
	}
	else if (tint_percent > 0 && !pixmap_copy)
	{
		if (gc == None)
		{
			gc = PictureDefaultGC(dpy, win);
		}
		pixmap_copy = XCreatePixmap(dpy, win, src_w, src_h, Pdepth);
		if (pixmap_copy && gc)
		{
			XCopyArea(
				dpy, pixmap, pixmap_copy, gc,
				src_x, src_y, src_w, src_h, 0, 0);
		}
		src_x = src_y = 0;
	}
	else if (!pixmap_copy)
	{
		src_picture = FRenderCreatePicture(
			dpy, pixmap, PFrenderVisualFormat, pam, &pa);
	}

	if (!src_picture && pixmap_copy)
	{
		src_picture = FRenderCreatePicture(
			dpy, pixmap_copy, PFrenderVisualFormat, pam, &pa);
	}

	if (!src_picture)
	{
		goto bail;
	}

	/* tint the src, it is why we have done a pixmap copy */
	if (tint_percent > 0)
	{
		FRenderTintPicture(
			dpy, win, tint, tint_percent, src_picture,
			src_x, src_y, src_w, src_h);
	}

	if (added_alpha_percent >= 100)
	{
		if (alpha != None)
		{
			alpha_picture = FRenderCreatePicture(
				dpy, alpha, PFrenderAlphaFormat, pam, &pa);
		}
		else if (mask != None)
		{
			alpha_picture = FRenderCreatePicture(
				dpy, mask, PFrenderMaskFormat, pam, &pa);
		}
		else
		{
			/* fix a bug in certain XRender server implementation?
			 */
			if (!(shade_picture = FRenderCreateShadePicture(
				dpy, win, 100)))
			{
				goto bail;
			}
			alpha_x = alpha_y = 0;
		}
	}
	else
	{
		if (alpha != None)
		{
			alpha_copy = XCreatePixmap(dpy, win, src_w, src_h, 8);
			if (!alpha_gc)
			{
				alpha_gc = fvwmlib_XCreateGC(
					dpy, alpha, 0, NULL);
				free_alpha_gc = True;
			}
			if (alpha_copy && alpha_gc)
			{
				XCopyArea(dpy, alpha, alpha_copy, alpha_gc,
					  alpha_x, alpha_y, src_w, src_h, 0, 0);
				alpha_picture = FRenderCreatePicture(
					dpy, alpha_copy, PFrenderAlphaFormat,
					pam, &pa);
			}
			if (alpha_gc && free_alpha_gc)
			{
				XFreeGC(dpy, alpha_gc);
			}
			alpha_x = alpha_y = 0;
		}
		else if (mask != None)
		{
			alpha_copy = XCreatePixmap(dpy, win, src_w, src_h, 8);
			if (alpha_copy)
			{
				alpha_picture = FRenderCreatePicture(
					dpy, alpha_copy, PFrenderAlphaFormat,
					pam, &pa);
			}
			if (alpha_picture)
			{
				frc.red = frc.green = frc.blue = frc.alpha = 0;
				FRenderFillRectangle(
					dpy, FRenderPictOpSrc, alpha_picture,
					&frc, 0, 0, src_w, src_h);
			}
			mask_picture = FRenderCreatePicture(
				dpy, mask, PFrenderMaskFormat, pam, &pa);
		}
		else
		{
			alpha_x = alpha_y = 0;
		}

		if (!(shade_picture = FRenderCreateShadePicture(
			dpy, win, added_alpha_percent)))
		{
			goto bail;
		}


		if (alpha != None && alpha_picture && shade_picture)
		{
			if (!FRenderCompositeAndCheck(
				dpy, FRenderPictOpAtopReverse, shade_picture,
				alpha_picture, alpha_picture,
				0, 0, alpha_x, alpha_y, 0, 0, src_w, src_h))
			{
				goto bail;
			}
			alpha_x = alpha_y = 0;
		}
		else if (mask != None && alpha_picture && shade_picture)
		{
			if (!FRenderCompositeAndCheck(
				dpy, FRenderPictOpAtopReverse, shade_picture,
				mask_picture, alpha_picture,
				0, 0, alpha_x, alpha_y, 0, 0, src_w, src_h))
			{
				goto bail;
			}
			alpha_x = alpha_y = 0;
		}
	}

	if (alpha_picture == None)
	{
		alpha_picture = shade_picture;
	}

	dest_picture = FRenderCreatePicture(
		dpy, d, PFrenderVisualFormat, 0, &pa);

	if (dest_picture)
	{
		rv = FRenderCompositeAndCheck(
			dpy, FRenderPictOpOver, src_picture, alpha_picture,
			dest_picture, src_x, src_y, alpha_x, alpha_y,
			dest_x, dest_y, dest_w, dest_h);
	}

 bail:
	if (dest_picture)
	{
		FRenderFreePicture(dpy, dest_picture);
	}
	if (src_picture)
	{
		FRenderFreePicture(dpy, src_picture);
	}
	if (alpha_picture && alpha_picture != shade_picture)
	{
		FRenderFreePicture(dpy, alpha_picture);
	}
	if (mask_picture)
	{
		FRenderFreePicture(dpy, mask_picture);
	}
	if (root_picture)
	{
		FRenderFreePicture(dpy, root_picture);
	}
	if (alpha_copy)
	{
		XFreePixmap(dpy, alpha_copy);
	}
	if (pixmap_copy)
	{
		XFreePixmap(dpy, pixmap_copy);
	}
	return rv;
}
