/*
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  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 "wconfig.h"

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

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

#include <wraster.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "wcore.h"
#include "texture.h"

extern WPreferences wPreferences;



static void drawFrame(WCoreWindow *core, WTexSolid *texture);

static Pixmap renderGradient(WScreen *scr, int width, int height, 
			     WTexGradient *texture);




WTexSolid *
wTextureMakeSolid(WScreen *scr, XColor *color, int relief)
{
    WTexSolid *texture;
    int gcm;
    XGCValues gcv;
    int r, g, b;
    
    texture = wmalloc(sizeof(WTexture));

    texture->type = WTEX_SOLID;
    texture->subtype = 0;
    texture->relief = relief;

    XAllocColor(dpy, scr->w_colormap, color);
    texture->normal = *color;
    if (color->red==0 && color->blue==0 && color->green == 0) {
	texture->light.red = 0xb6da;
	texture->light.green = 0xb6da;
	texture->light.blue = 0xb6da;
	texture->dim.red = 0x6185;
	texture->dim.green = 0x6185;
	texture->dim.blue = 0x6185;
	texture->dark.red = 0;
	texture->dark.green = 0;
	texture->dark.blue = 0;
    } else {
	r = (color->red * 17)/10;
	g = (color->green * 17)/10;
	b = (color->blue * 17)/10;
	texture->light.red = (r > 0xffff ? 0xffff : r);
	texture->light.green = (g > 0xffff ? 0xffff : g);
	texture->light.blue = (b > 0xffff ? 0xffff : b);
	r = (color->red * 5)/10;
	g = (color->green * 5)/10;
	b = (color->blue * 5)/10;
	texture->dim.red = r;
	texture->dim.green = g;
	texture->dim.blue = b;
	texture->dark.red = 0;
	texture->dark.green = 0;
	texture->dark.blue = 0;
    }
    XAllocColor(dpy, scr->w_colormap, &texture->light);
    XAllocColor(dpy, scr->w_colormap, &texture->dim);
    XAllocColor(dpy, scr->w_colormap, &texture->dark);
    
    gcm = GCForeground|GCBackground;
    gcv.background = gcv.foreground = texture->light.pixel;    
    texture->light_gc = XCreateGC(dpy, scr->w_win,gcm, &gcv);
    gcv.background = gcv.foreground = texture->dim.pixel;	
    texture->dim_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
    gcv.background = gcv.foreground = texture->dark.pixel;
    texture->dark_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    gcv.background = gcv.foreground = color->pixel;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
    return texture;
}


static int
dummyErrorHandler(Display *foo, XErrorEvent *bar)
{
#ifdef DEBUG
    wWarning("your server is buggy. Tell the author if some error related to color occurs");
#endif
    return 0;
}


void
wTextureDestroy(WScreen *scr, WTexture *texture)
{
    int count=0;
    unsigned long colors[8];

#ifdef DEBUG    
    if (texture==NULL) {
	printf("BUG: trying to free NULL texture\n");
	return;
    }
#endif
    
    /* 
     * some stupid servers don't like white or black being freed...
     */
#define CANFREE(c) (c!=scr->black_pixel && c!=scr->white_pixel)
    switch (texture->any.type) {
     case WTEX_SOLID:
	if (texture->solid.relief!=WREL_FLAT) {
	    XFreeGC(dpy, texture->solid.light_gc);
	    XFreeGC(dpy, texture->solid.dark_gc);
	    XFreeGC(dpy, texture->solid.dim_gc);
	    if (CANFREE(texture->solid.light.pixel))
	      colors[count++] = texture->solid.light.pixel;
	    if (CANFREE(texture->solid.dim.pixel))
	      colors[count++] = texture->solid.dim.pixel;
	    if (CANFREE(texture->solid.dark.pixel))
	      colors[count++] = texture->solid.dark.pixel;
	}
	break;
    }
    if (CANFREE(texture->any.color.pixel))
      colors[count++] = texture->any.color.pixel;
    if (count > 0) {
	XErrorHandler oldhandler;
	
	/* ignore error from buggy servers that don't know how
	 * to do reference counting for colors. */
	oldhandler = XSetErrorHandler(dummyErrorHandler);
	XFreeColors(dpy, scr->colormap, colors, count, 0);
	XSync(dpy,0);
	XSetErrorHandler(oldhandler);
    }
    XFreeGC(dpy, texture->any.gc);
    wfree(texture);
#undef CANFREE    
}



WTexGradient *
wTextureMakeGradient(WScreen *scr, int style, XColor *from, XColor *to, 
		     int relief)
{
    WTexGradient *texture;
    XGCValues gcv;

    texture = wmalloc(sizeof(WTexture));
    memset(texture, 0, sizeof(WTexture));
    texture->type = WTEX_GRADIENT;
    texture->subtype = style;
    texture->relief = relief;
    
    texture->color1 = *from;
    texture->color2 = *to;

    texture->normal.red = (from->red + to->red)/2;
    texture->normal.green = (from->green + to->green)/2;
    texture->normal.blue = (from->blue + to->blue)/2;

    XAllocColor(dpy, scr->w_colormap, &texture->normal);
    gcv.background = gcv.foreground = texture->normal.pixel;
    texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground,
				   &gcv);

    return texture;
}


/*
 *---------------------------------------------------------------------- 
 * wTextureFreeData--
 * 	Frees the texture data for a single texture instance. Must
 * be called when the texture has to be re-rendered, like when a
 * window is resized.
 * 
 * Side effects:
 * 	The texture data is freed and the texture data variable is 
 * set to NULL
 *---------------------------------------------------------------------- 
 */
void
wTextureFreeData(WTexture *texture, WTexData *data)
{
    switch (texture->any.type) {
     case WTEX_GRADIENT:
	if (*data)
	  XFreePixmap(dpy, *data);
	*data = None;
	break;
    }
}



void
wTexturePaint(WTexture *texture, WTexData *data, WCoreWindow *core, 
	      int repaint)
{
    int d;

    switch (texture->any.relief) {
     case WREL_TITLEBAR:
	d = 1;
	break;
     case WREL_MENUENTRY:
     case WREL_ICON:
     case WREL_RAISED:
	d = 1;
	break;
     case WREL_MENUTITLE:
        d = 2;
	break;
     default:
	d = 0;
    }
    switch (texture->any.type) {
     case WTEX_GRADIENT:
	if (!*data) {
	    *data = renderGradient(core->screen_ptr, core->width, core->height,
				   (WTexGradient*)texture);
	    if (*data)
	      XSetWindowBackgroundPixmap(dpy, core->window, *data);
	} else if (!repaint) {
	    if (*data)
	      XSetWindowBackgroundPixmap(dpy, core->window, *data);
	}
	XClearWindow(dpy, core->window);	    
	break;
     case WTEX_SOLID:
	if (!repaint) {
	    XSetWindowBackground(dpy, core->window,
				 texture->solid.normal.pixel);
	    XClearArea(dpy, core->window, d, d, core->width-2*d,
		       core->height-2*d, False);
	}
	drawFrame(core, (WTexSolid*)texture);
	break;
    }
}


/* used only for menu entries */
void
wTextureRender(WScreen *scr, WTexture *texture, WTexData *data, 
	       int width, int height)
{
    switch (texture->any.type) {
     case WTEX_GRADIENT:
	if (!*data) {
	    *data = renderGradient(scr, width, height, (WTexGradient*)texture);
	}
	break;
    }
}


static void
bevelImage(RImage *image, int relief)
{
    int width = image->width;
    int height = image->height;
    int x, y, ofs;
    unsigned char *r, *g, *b;

    r = image->data[0];
    g = image->data[1];
    b = image->data[2];
    switch (relief) {
     case WREL_MENUENTRY:
	r++; 	g++;	b++;
	for (x=0; x<(width-2); x++) {
	    *r = (*r<175 ? *r+80 : 255);
	    *g = (*g<175 ? *g+80 : 255);
	    *b = (*b<175 ? *b+80 : 255);	    
	    r++; g++; b++;
	}
	r = image->data[0];
	g = image->data[1];
	b = image->data[2];
	for (y=0; y<height; y++) {
	    ofs = y*width;
	    r[ofs]>>=1;
	    g[ofs]>>=1;
	    b[ofs++]>>=1;
	    r[ofs] = (r[ofs] <175 ? r[ofs]+80 : 255);
	    g[ofs] = (g[ofs] <175 ? g[ofs]+80 : 255);
	    b[ofs] = (b[ofs] <175 ? b[ofs]+80 : 255);
	    ofs = y*width+(width-2);
	    r[ofs] = (r[ofs] >40 ? r[ofs]-40 : 0);
	    g[ofs] = (g[ofs] >40 ? g[ofs]-40 : 0);
	    b[ofs] = (b[ofs] >40 ? b[ofs]-40 : 0);
	    ofs++;
	    r[ofs]=0;
	    g[ofs]=0;
	    b[ofs++]=0;
	}
	r = image->data[0] + (height-2)*width + 1;
	g = image->data[1] + (height-2)*width + 1;
	b = image->data[2] + (height-2)*width + 1;
	for (x=1; x<width; x++) {
	    *r = (*r>40 ? *r-40 : 0);
	    *g = (*g>40 ? *g-40 : 0);
	    *b = (*b>40 ? *b-40 : 0);
	    r++; g++; b++;
	}
	memset(r, 0, width);
	memset(g, 0, width);
	memset(b, 0, width);	
	break;

     case WREL_MENUTITLE:
	for (x=0; x<(width-1); x++) {
	    *r = (*r>40 ? *r-40 : 0);
	    *g = (*g>40 ? *g-40 : 0);
	    *b = (*b>40 ? *b-40 : 0);
	    r++; g++; b++;
	}
	*(r++)=0;
	*(g++)=0;
	*(b++)=0;
	r = image->data[0] + width + 1;
	g = image->data[1] + width + 1;
	b = image->data[2] + width + 1;
	for (x=0; x<(width-2); x++) {
	    *r = (*r<175 ? *r+80 : 255);
	    *g = (*g<175 ? *g+80 : 255);
	    *b = (*b<175 ? *b+80 : 255);
	    r++; g++; b++;
	}
	r = image->data[0];
	g = image->data[1];
	b = image->data[2];
	for (y=1; y<height-1; y++) {
	    ofs = y*width;
	    r[ofs] = (r[ofs] >50 ? r[ofs]-50 : 0);
	    g[ofs] = (g[ofs] >50 ? g[ofs]-50 : 0);
	    b[ofs] = (b[ofs] >50 ? b[ofs]-50 : 0);
	    ofs++;
	    r[ofs] = (r[ofs] <175 ? r[ofs]+80 : 255);
	    g[ofs] = (g[ofs] <175 ? g[ofs]+80 : 255);
	    b[ofs] = (b[ofs] <175 ? b[ofs]+80 : 255);
	    ofs = y*width+(width-2);
	    r[ofs]>>=1;
	    g[ofs]>>=1;
	    b[ofs++]>>=1;
	    r[ofs]=0;
	    g[ofs]=0;
	    b[ofs]=0;
	}
	r = image->data[0] + (height-2)*width;
	g = image->data[1] + (height-2)*width;
	b = image->data[2] + (height-2)*width;	
	for (x=0; x<(width-1); x++) {
	    *r = (*r>40 ? *r-40 : 0);
	    *g = (*g>40 ? *g-40 : 0);
	    *b = (*b>40 ? *b-40 : 0);
	    r++; g++; b++;
	}
	r++; g++; b++;
	memset(r, 0, width);
	memset(g, 0, width);
	memset(b, 0, width);	
	break;
    }
}



static Pixmap
renderGradient(WScreen *scr, int width, int height, WTexGradient *texture)
{
    RImage *img;
    Pixmap pix;
    int style, relief=0;
    RColor from, to;

    switch (texture->subtype) {
     case WTEX_HGRADIENT:
	style = WGRD_HORIZONTAL;
	break;
	
     case WTEX_VGRADIENT:
	style = WGRD_VERTICAL;
	break;

     case WTEX_DGRADIENT:
	style = WGRD_DIAGONAL;
	break;

     default:
	wWarning("invalid gradient type in renderGradient()");
	wAbort();
	exit(1);
    }
    switch (texture->relief) {
     case WREL_RAISED:
	relief = WBEV_RAISED2;
	break;
	
     case WREL_SUNKEN:
	relief = WBEV_SUNKEN;
	break;
	
     case WREL_FLAT:
	relief = 0;
	break;
	
     case WREL_TITLEBAR:
	relief = WBEV_RAISED2;
	width++;
	break;

     case WREL_MENUTITLE:
	relief = -1;
	break;
	
     case WREL_MENUENTRY:
	relief = -2;
	break;
    }
    from.red = texture->color1.red>>8;
    from.green = texture->color1.green>>8;
    from.blue = texture->color1.blue>>8;
    to.red = texture->color2.red>>8;
    to.green = texture->color2.green>>8;
    to.blue = texture->color2.blue>>8;
    img = RRenderGradient(width, height, &from, &to, style);
    if (!img) {
	wWarning(_("could not render gradient: %s"), RErrorString);
	return None;
    }
    if (relief > 0)
      RBevelImage(img, relief);
    else if (relief < 0) {
	bevelImage(img, texture->relief);
    }
    RConvertImage(scr->rcontext, img, &pix);
    RDestroyImage(img);
    return pix;
}
 

static void
drawFrame(WCoreWindow *core, WTexSolid *texture)
{
    GC light, dim, dark;
    XSegment segs[4];

    if (texture->relief==WREL_FLAT) return;

    light = texture->light_gc;
    dim = texture->dim_gc;
    dark = texture->dark_gc;
    switch (texture->relief) {
     case WREL_FLAT:
	return;
     case WREL_MENUENTRY:
     case WREL_RAISED:
     case WREL_ICON:
	segs[0].x1 = 1;
	segs[0].x2 = core->width - 2;
	segs[0].y2 = segs[0].y1 = core->height - 2;
	segs[1].x1 = core->width - 2;
	segs[1].y1 = 1;
	segs[1].x2 = core->width - 2;
	segs[1].y2 = core->height - 2;
	XDrawSegments(dpy, core->window, dim, segs, 2);
	segs[0].x1 = 0;
	segs[0].x2 = core->width - 1;
	segs[0].y2 = segs[0].y1 = core->height - 1;
	segs[1].x1 = segs[1].x2 = core->width - 1;
	segs[1].y1 = 0;
	segs[1].y2 = core->height - 1;
	XDrawSegments(dpy, core->window, dark, segs, 2);
	segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
	segs[0].x2 = core->width - 2;
	segs[1].x1 = segs[1].y1 = 0;
	segs[1].x2 = 0;
	segs[1].y2 = core->height - 2;
	XDrawSegments(dpy, core->window, light, segs, 2);
	if (texture->type==WREL_ICON) {
	    segs[0].x1 = segs[0].y1 = segs[0].y2 = 1;
	    segs[0].x2 = core->width - 2;
	    segs[1].x1 = segs[1].y1 = 1;
	    segs[1].x2 = 1;
	    segs[1].y2 = core->height - 2;
	    XDrawSegments(dpy, core->window, light, segs, 2);
	}
	break;

     case WREL_TITLEBAR:
	segs[0].x1 = 1;
	segs[0].x2 = core->width - 1;
	segs[0].y2 = segs[0].y1 = core->height - 2;
	segs[1].x2 = segs[1].x1 = core->width - 1;
	segs[1].y1 = 0;
	segs[1].y2 = core->height - 1;
	XDrawSegments(dpy, core->window, dim, segs, 2);
	segs[0].x1 = 0;
	segs[0].x2 = core->width - 1;
	segs[0].y2 = segs[0].y1 = core->height - 1;
	XDrawSegments(dpy, core->window, dark, segs, 1);
	segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
	segs[0].x2 = core->width - 2;
	segs[1].x1 = segs[1].y1 = 0;
	segs[1].x2 = 0;
	segs[1].y2 = core->height - 2;
	XDrawSegments(dpy, core->window, light, segs, 2);
	break;
	
     case WREL_MENUTITLE:
	segs[0].x1 = segs[0].x2 = 0;
	segs[0].y1 = core->height - 1;
	segs[0].y2 = 0;
	segs[1].x1 = 0;
	segs[1].x2 = core->width - 1;
	segs[1].y1 = segs[1].y2 = 0;
	segs[2].x1 = 2;
	segs[2].x2 = core->width - 2;
	segs[2].y1 = segs[2].y2 = core->height - 2;
	segs[3].x1 = segs[3].x2 = core->width - 2;
	segs[3].y1 = core->height - 2;
	segs[3].y2 = 2;
	XDrawSegments(dpy, core->window, dim, segs, 4);
	segs[0].x1 = 1;
	segs[0].x2 = core->width - 1;
	segs[0].y1 = segs[0].y2 = core->height - 1;
	segs[1].x1 = segs[1].x2 = core->width - 1;
	segs[1].y1 = core->height - 1;
	segs[1].y2 = 1;
	XDrawSegments(dpy, core->window, dark, segs, 2);
	segs[0].x1 = segs[0].x2 = 1;
	segs[0].y1 = core->height - 2;
	segs[0].y2 = 1;
	segs[1].x1 = 1;
	segs[1].x2 = core->width - 2;
	segs[1].y1 = segs[1].y2 = 1;
	XDrawSegments(dpy, core->window, light, segs, 2);
	break;
		
     case WREL_SUNKEN:
	/*
	 segs[0].x1 = 1;
	 segs[0].x2 = core->width - 2;
	 segs[0].y2 = segs[0].y1 = 1;
	 segs[1].x2 = segs[1].x1 = 1;
	 segs[1].y1 = 1;
	 segs[1].y2 = core->height - 2;
	 XDrawSegments(dpy, core->window, dark, segs, 2);
	 */
	segs[0].x1 = 0;
	segs[0].x2 = core->width - 1;
	segs[0].y2 = segs[0].y1 = 0;
	segs[1].x1 = segs[1].x2 = 0;
	segs[1].y1 = 0;
	segs[1].y2 = core->height - 1;
	XDrawSegments(dpy, core->window, dark, segs, 2);
	
	segs[0].x1 = 0;
	segs[0].y1 = segs[0].y2 = core->height - 1;
	segs[0].x2 = core->width - 1;
	segs[1].x2 = segs[1].x1 = core->width - 1;
	segs[1].y1 = 1;
	segs[1].y2 = core->height - 1;
	XDrawSegments(dpy, core->window, light, segs, 2);
	break;	
    }
}

