/*
 * wmxmms2
 * Copyright (c)2005 Remy Bosch (remybosch@zonnet.nl)
 * 
 * This software covered by the GPL.  See COPYING file for details.
 *
 * wmgeneral.c
 *
 */

#include "wmgeneral.h"
#include "../pixmaps/charset.xpm"

  /*****************/
 /* X11 Variables */
/*****************/

int		screen;
int		x_fd;
int		d_depth;
XSizeHints	mysizehints;
XWMHints	mywmhints;
Pixel		back_pix, fore_pix;
char		*Geometry = "";
GC		NormalGC;
XpmIcon		wmgen;
XpmIcon		wmfont;
Pixmap		pixmask;

  /*****************/
 /* Mouse Regions */
/*****************/

typedef struct {
	int		enable;
	int		top;
	int		bottom;
	int		left;
	int		right;
} MOUSE_REGION;

MOUSE_REGION	mouse_region[MAX_MOUSE_REGION];

  /***********************/
 /* Function Prototypes */
/***********************/

static void GetXPM(XpmIcon *, char **);
static Pixel GetColor(char *);

/************************************************************************\
|* GetXPM								*|
\************************************************************************/
static void GetXPM(XpmIcon *wmgen, char *pixmap_bytes[]) {
	XWindowAttributes attributes;	
	int     err;

	/* For the colormap */
	XGetWindowAttributes(display, Root, &attributes);

	wmgen->attributes.valuemask |= 
		(XpmReturnPixels | XpmReturnExtensions);

	err = XpmCreatePixmapFromData(display, Root, pixmap_bytes,
			&(wmgen->pixmap),
			&(wmgen->mask),
			&(wmgen->attributes));
	
	if (err != XpmSuccess) {
		fprintf(stderr, "Not enough free colorcells.\n");
		exit(1);
	}
}


/************************************************************************\
|* GetColor								*|
\************************************************************************/

static Pixel GetColor(char *name) {

	XColor	color;
	XWindowAttributes	attributes;

	XGetWindowAttributes(display, Root, &attributes);

	color.pixel = 0;
	if (!XParseColor(display, attributes.colormap, name, &color)) {
		fprintf(stderr, "wm.app: can't parse %s.\n", name);
	} else if (!XAllocColor(display, attributes.colormap, &color)) {
		fprintf(stderr, "wm.app: can't allocate %s.\n", name);
	}
	return color.pixel;
}

/************************************************************************\
|* flush_expose								*|
\************************************************************************/

static int flush_expose(Window w) {

	XEvent 	dummy;
	int	i=0;

	while (XCheckTypedWindowEvent(display, w, Expose, &dummy))
		i++;

	return i;
}

/************************************************************************\
|* RedrawWindow								*|
\************************************************************************/

void RedrawWindow(void) {
	
	flush_expose(iconwin);
	XCopyArea(display, wmgen.pixmap, iconwin, NormalGC, 
				0,0, wmgen.attributes.width, wmgen.attributes.height, 0,0);
	flush_expose(win);
	XCopyArea(display, wmgen.pixmap, win, NormalGC,
				0,0, wmgen.attributes.width, wmgen.attributes.height, 0,0);
}

/************************************************************************\
|* RedrawWindowXY							*|
\*************************************************************************/

void RedrawWindowXY(int x, int y) {
	
	flush_expose(iconwin);
	XCopyArea(display, wmgen.pixmap, iconwin, NormalGC, 
				x,y, wmgen.attributes.width, wmgen.attributes.height, 0,0);
	flush_expose(win);
	XCopyArea(display, wmgen.pixmap, win, NormalGC,
				x,y, wmgen.attributes.width, wmgen.attributes.height, 0,0);
}

/************************************************************************\
|* AddMouseRegion							*|
\************************************************************************/

void AddMouseRegion(int index, int left, int top, int right, int bottom) {

	if (index < MAX_MOUSE_REGION) {
		mouse_region[index].enable = 1;
		mouse_region[index].top = top;
		mouse_region[index].left = left;
		mouse_region[index].bottom = bottom;
		mouse_region[index].right = right;
	}
}

/************************************************************************\
|* CheckMouseRegion							*|
\************************************************************************/

int CheckMouseRegion(int x, int y) {

	int	i;
	int	found;

	found = 0;

	for (i=0; i<MAX_MOUSE_REGION && !found; i++) {
		if (mouse_region[i].enable &&
			x <= mouse_region[i].right &&
			x >= mouse_region[i].left &&
			y <= mouse_region[i].bottom &&
			y >= mouse_region[i].top)
			found = 1;
	}
	if (!found)
		return -1;
	return (i-1);
}

/************************************************************************\
|* createXBMfromXPM							*|
\************************************************************************/
void createXBMfromXPM(char *xbm, char **xpm, int sx, int sy) {

	int	i,j,k;
	int	width, height, numcol, depth;
	int	zero=0;
	unsigned char	bwrite;
	int	bcount;
	int	curpixel;
	
	sscanf(*xpm, "%d %d %d %d", &width, &height, &numcol, &depth);

	for (k=0; k!=depth; k++){
		zero <<=8;
		zero |= xpm[1][k];
	}

	for (i=numcol+1; i < numcol+sy+1; i++) {
		bcount = 0;
		bwrite = 0;
		for (j=0; j<sx*depth; j+=depth) {
			bwrite >>= 1;
			curpixel=0;
			for (k=0; k!=depth; k++){
				curpixel <<=8;
				curpixel |= xpm[i][j+k];
			}
			if ( curpixel != zero ) {
				bwrite += 128;
			}
			bcount++;
			if (bcount == 8) {
				*xbm = bwrite;
				xbm++;
				bcount = 0;
				bwrite = 0;
			}
		}
	}
}

/************************************************************************\
|* copyXPMArea								*|
\************************************************************************/

void copyXPMArea(int x, int y, int sx, int sy, int dx, int dy) {

	XCopyArea(display, wmgen.pixmap, wmgen.pixmap, NormalGC, x, y, sx, sy, dx, dy);

}

/************************************************************************\
|* copyXBMArea								*|
\************************************************************************/

void copyXBMArea(int x, int y, int sx, int sy, int dx, int dy) {

	XCopyArea(display, wmgen.mask, wmgen.pixmap, NormalGC, x, y, sx, sy, dx, dy);
}

/************************************************************************\
|* setMaskXY								*|
\************************************************************************/

void setMaskXY(int x, int y) {

	 XShapeCombineMask(display, win, ShapeBounding, x, y, pixmask, ShapeSet);
	 XShapeCombineMask(display, iconwin, ShapeBounding, x, y, pixmask, ShapeSet);
}

/************************************************************************\
|* openXwindow								*|
\************************************************************************/
void openXwindow(int argc, char *argv[], char *pixmap_bytes[], char *pixmask_bits, int pixmask_width, int pixmask_height) {

	unsigned int	borderwidth = 1;
	XClassHint	classHint;
	char		*display_name = NULL;
	char		*wname = argv[0];
	XTextProperty	name;

	XGCValues	gcv;
	unsigned long	gcm;

	char		*geometry = NULL;

	int		dummy=0;
	int		i, wx, wy;

	for (i=1; argv[i]; i++) {
		if (!strcmp(argv[i], "-display")) {
			display_name = argv[i+1];
			i++;
		}
		if (!strcmp(argv[i], "-geometry")) {
			geometry = argv[i+1];
			i++;
		}
	}

	if (!(display = XOpenDisplay(display_name))) {
		fprintf(stderr, "%s: can't open display %s\n", 
			wname, XDisplayName(display_name));
		exit(1);
	}
	screen  = DefaultScreen(display);
	Root    = RootWindow(display, screen);
	d_depth = DefaultDepth(display, screen);
	x_fd    = XConnectionNumber(display);

	/* Convert XPM to XImage */
	GetXPM(&wmgen, pixmap_bytes);
	/* Create a window to hold the stuff */
	mysizehints.flags = USSize | USPosition;
	mysizehints.x = 0;
	mysizehints.y = 0;

	back_pix = GetColor("white");
	fore_pix = GetColor("black");

	XWMGeometry(display, screen, Geometry, NULL, borderwidth, &mysizehints,
		&mysizehints.x, &mysizehints.y,&mysizehints.width,&mysizehints.height, &dummy);

	mysizehints.width = 64;
	mysizehints.height = 64;
		
	win = XCreateSimpleWindow(display, Root, mysizehints.x, mysizehints.y,
		mysizehints.width, mysizehints.height, borderwidth, fore_pix, back_pix);
	
	iconwin = XCreateSimpleWindow(display, win, mysizehints.x, mysizehints.y,
		mysizehints.width, mysizehints.height, borderwidth, fore_pix, back_pix);

	/* Activate hints */
	XSetWMNormalHints(display, win, &mysizehints);
	classHint.res_name = wname;
	classHint.res_class = wname;
	XSetClassHint(display, win, &classHint);

	XSelectInput(display, win, ButtonPressMask | ExposureMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask);
	XSelectInput(display, iconwin, ButtonPressMask | ExposureMask | ButtonReleaseMask | PointerMotionMask  | StructureNotifyMask);

	if (XStringListToTextProperty(&wname, 1, &name) == 0) {
		fprintf(stderr, "%s: can't allocate window name\n", wname);
		exit(1);
	}

	XSetWMName(display, win, &name);

	/* Create GC for drawing */
	
	gcm = GCForeground | GCBackground | GCGraphicsExposures;
	gcv.foreground = fore_pix;
	gcv.background = back_pix;
	gcv.graphics_exposures = 0;
	NormalGC = XCreateGC(display, Root, gcm, &gcv);

	/* ONLYSHAPE ON */

	pixmask = XCreateBitmapFromData(display, win, pixmask_bits, pixmask_width, pixmask_height);

	XShapeCombineMask(display, win, ShapeBounding, 0, 0, pixmask, ShapeSet);
	XShapeCombineMask(display, iconwin, ShapeBounding, 0, 0, pixmask, ShapeSet);

	/* ONLYSHAPE OFF */

	mywmhints.initial_state = WithdrawnState;
	mywmhints.icon_window = iconwin;
	mywmhints.icon_x = mysizehints.x;
	mywmhints.icon_y = mysizehints.y;
	mywmhints.window_group = win;
	mywmhints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;

	XSetWMHints(display, win, &mywmhints);

	XSetCommand(display, win, argv, argc);
	XMapWindow(display, win);

	if (geometry) {
		if (sscanf(geometry, "+%d+%d", &wx, &wy) != 2) {
			fprintf(stderr, "Bad geometry string.\n");
			exit(1);
		}
		XMoveWindow(display, win, wx, wy);
	}
}

/************************************************************************\
|* fontColorInit							*|
|* Handles colour subtitution for the characters			*|
\************************************************************************/
void fontColorInit(const char *colorOption){
	char			newXpmColorString[32];
	XColor			charColor;
	int			brightValueIndex=3;
	char			brightValueSign='+';
	XWindowAttributes	attributes;
	
	XGetWindowAttributes(display, Root, &attributes);
	/* Check supplied value and convert to usable format*/
	if (!XParseColor(display, attributes.colormap, colorOption, &charColor )){
		printf("Parsed colour %s is no valid colouroption\n",
				colorOption);
		exit(1);
	}
	/* construct a new string for the xpm array */
	sprintf(newXpmColorString, "%c  c #%04X%04X%04X", brightValueSign,
			charColor.red, charColor.green, charColor.blue);
	
	/* Applay changes to xpm array */
	charset_xpm[brightValueIndex] = &newXpmColorString[0];
	
	/* Construct pixmap */
	XpmCreatePixmapFromData(display, Root, charset_xpm,
			&(wmfont.pixmap),
			&(wmfont.mask),
			&(wmfont.attributes));
}

/************************************************************************\
|* drawChar								*|
\************************************************************************/
void drawChar(char c, int x, int y){
	int sx, sy;
	sx = ((c - 32) * FONT_WIDTH ) +2;
	sy = 0;
	XCopyArea(display, wmfont.pixmap, wmgen.pixmap, NormalGC, sx, sy, FONT_WIDTH , FONT_HEIGHT , x, y);
}

/************************************************************************\
|* drawString								*|
\************************************************************************/
void drawString(char *s, int x, int y){
	drawPartString(s,x,y,0,strlen(s)-1);
}

/************************************************************************\
|* drawPartString							*|
\************************************************************************/
void drawPartString(char *s, int x, int y, unsigned int s_begin, unsigned int s_end ){
	unsigned int i = s_begin;
	unsigned int offset=0;
	if (strlen(s)<=s_begin || s_begin > s_end){
		fprintf(stderr,"Your code is shit!\nRepair it!\n");
		fprintf(stderr,"strlen=%d\ns_begin=%d\ns_end=%d\n",strlen(s),s_begin,s_end);
	}
	while (i < strlen(s)) {
		offset=i-s_begin;
		if ((x + (FONT_WIDTH * offset )) < DISPLAY_LEN) {
			drawChar(s[i], x + (FONT_WIDTH * offset), y);
		}
		else
			break;
		i++;
	}
	offset++;
	while(offset<NUM_CHARS){
		drawChar(' ', x+(FONT_WIDTH * offset), y);
		offset++;
	}
}

/************************************************************************\
|* scroller								*|
\************************************************************************/
void scroller(char *s, int x, int y){
	static	char		S_HOLD [100];
	static	unsigned int	s_offset;
	static	int		start_offset;
	if(s==NULL || s=="" || s=="\0" || strlen(s)==0 ){
		s=" ";
		s_offset=0;
		strcpy(S_HOLD,s);
		drawString("       ",x,y);
	}else if(strcmp(S_HOLD,s)!=0){
		s_offset=0;
		start_offset=NUM_CHARS-1;
		strcpy(S_HOLD,s);
		drawString("       ",x,y);
		drawString(s,x+(start_offset*FONT_WIDTH),y);
	}else{
		if (start_offset<1)
			s_offset++;
		else
			start_offset--;
		
		if(strlen(s)==s_offset){
			s_offset =0;
			start_offset=NUM_CHARS-1;
			drawChar(' ',x,y);
			drawString(s,x+(start_offset*FONT_WIDTH),y);
		}
		drawPartString(s,x+(start_offset*FONT_WIDTH),y,s_offset,strlen(s));
	}
	RedrawWindow();
}


/************************************************************************\
|* lineDraw								*|
\************************************************************************/

void lineDraw(unsigned int color, int x,int y,unsigned int length){
	unsigned int Pbar[]={ 122,124, 126};
	if (color!=RED && color!=GREEN && color!=BLACK)
		return;
	if (length<56){
		copyXPMArea(4, Pbar[color], length+1, 2, x, y);
		copyXPMArea(4, Pbar[2], 56-(length+1), 2, x+1+length, y);
	}
}
