/*  SCAVENGER by David Ashley  dash@linuxmotors.com */

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

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

#include "names.h"
#include "x.h"

/* Icon bitmaps */
#include "icon.xbm"


Display *dp;
GC copygc,andgc,orgc,xorgc;
int usedcolors;
uchar mymap[768];
int screen;
Window wi;
int fontbase,fontysize;
XImage *myimage;
char *imageloc;
XEvent event;
XKeyEvent *keyevent;
XButtonEvent *buttonevent;
XMotionEvent *motionevent;
XExposeEvent *exposeevent;

Pixmap artwork,artmask,bgframe,storeage;
long map2[256];
Colormap cmap,mycolormap;
uchar fmap[128];
int buttonstate=0,buttondown=0;
int mxpos,mypos;

int pressedcodes[KEYMAX],downcodes[KEYMAX],numpressed,numdown;



void dumpgfx()
{
	usedcolors = 0;
}

void createinout(int num)
{
gfxset *gs;
uchar *p;
int i,j,counts[256];
uchar red,green,blue;
int cnt;

	gs=gfxsets+num;
	p=gs->gs_pic;
	for(i=0;i<256;i++) counts[i]=0;
	i=64000;
	while(i--)
		counts[*p++]++;
	cnt=0;
	gs->gs_inout[0]=0;
	for(i=1;i<256;i++)
	{
		if(counts[i])
		{
			cnt++;
			p=gs->gs_colormap+i+i+i;
			red=*p++;
			green=*p++;
			blue=*p++;
			p=mymap;
			for(j=0;j<usedcolors;j++,p+=3)
			{
				if(red==*p && green==p[1] && blue==p[2])
				{
					gs->gs_inout[i]=j;
					break;
				}
			}
			if(j==usedcolors)
			{
				*p++=red;
				*p++=green;
				*p++=blue;
				gs->gs_inout[i]=usedcolors;
				usedcolors++; /* check for 256 overflow */
			}
		}
	}
}

void getcolors()
{
int i;
XColor ac;
uchar *p;
int res;
	p=mymap;
	for(i=0;i<usedcolors;i++)
	{
		ac.red=(*p++)<<8;
		ac.green=(*p++)<<8;
		ac.blue=(*p++)<<8;
		res=XAllocColor(dp,cmap,&ac);
		if(!res) printf("Failed on color %d\n",i);
		map2[i]=ac.pixel;
	}
}

void gfxfetch(int num,int source, int dest)
{
uchar *p;
gfxset *gs;
int dx,dy;
uchar ch;
unsigned long pixel;
uchar *map1;

	gs=gfxsets+num;
	map1=gs->gs_inout;
	p=gs->gs_pic+24*(source%12)+320*24*(source/12);
	for(dy=0;dy<24;dy++)
	{
		for(dx=0;dx<24;dx++)
		{
			pixel=*p++;
			if(pixel) pixel=map2[map1[pixel]];
			XPutPixel(myimage,dx,dy,pixel);
		}
		p+=320-24;
	}
	p-=320*24;
	XPutImage(dp,artwork,copygc,myimage,0,0,24*(dest%12),24*(dest/12),24,24);
	for(dy=0;dy<24;dy++)
	{
		for(dx=0;dx<24;dx++)
		{
			ch=*p++;
			XPutPixel(myimage,dx,dy,ch ? 0L : 0xffffffffL);
		}
		p+=320-24;
	}
	XPutImage(dp,artmask,copygc,myimage,0,0,24*(dest%12),24*(dest/12),24,24);

}

void puttile(int destx,int desty,int source)
{
int sx,sy;
	sx=24*(source%12);
	sy=24*(source/12);
	XCopyArea(dp,artmask,bgframe,andgc,sx,sy,24,24,destx,desty);
	XCopyArea(dp,artwork,bgframe,orgc,sx,sy,24,24,destx,desty);
}

void store(int x,int y,int which)
{
	XCopyArea(dp,bgframe,storeage,copygc,x,y,24,24,0,which*24);
}

void restore(int x,int y,int which)
{
	XCopyArea(dp,storeage,bgframe,copygc,0,which*24,24,24,x,y);
}

void copyup()
{
	XCopyArea(dp,bgframe,wi,copygc,0,0,640,480,0,0);
	needwhole=needtoptext=needbottomtext=0;
}

void copyupxy(int x,int y)
{
	XCopyArea(dp,bgframe,wi,copygc,x,y,24,24,x,y);
}

void copyupxysize(int x,int y,int xsize,int ysize)
{
	XCopyArea(dp,bgframe,wi,copygc,x,y,xsize,ysize,x,y);
}

void getfigures()
{
int i;
	for(i=0;i<12*32;i++)
		gfxfetch(i/96,i%96,i);
}

unsigned long getcolor(char *name)
{
XColor color1,color2;

	if(!XAllocNamedColor(dp,cmap,name,&color1,&color2))
	{
		printf("Couldn't allocate color %s\n",name);
		exit(4);
	}
	return color1.pixel;
}

/* FIXME: to be more sophisticated, create an icon window, since we
 * already have a color icon for Scavenger. The B/W icon should still be
 * here for those icon-window-deprived WM's.
 */
Pixmap create_icon() {
	Pixmap icon;

	icon=XCreateBitmapFromData(dp, DefaultRootWindow(dp), icon_bits,
		icon_width, icon_height);
	if (!icon) {
		fprintf(stderr, "Can't open icon file %s\n", ICONPATH);
		exit(5);
	}
	return icon;
}

void openx(int argc, char **argv)
{
/*
   Font font;
   char str[64];
 */
XFontStruct *afont;
XGCValues values;
XSizeHints *sizehints;
XWMHints *wmhints;
XClassHint *classhints;
XTextProperty windowName;
XTextProperty iconName;
char *title = "X Scavenger by David Ashley 1997";
char *title1 = "X Scavenger";
int depth;

	dp=XOpenDisplay(0);
	if(!dp)
	{
		printf("Cannot open display\n");
		exit(1);
	}
	screen=XDefaultScreen(dp);
	cmap=DefaultColormap(dp,screen);
	depth = DefaultDepth(dp, screen);
	wi=XCreateSimpleWindow(dp,RootWindow(dp,screen),20,20,640,480,0,0,0);

	wmhints = XAllocWMHints ();
	/* Various window manager settings */
	wmhints->initial_state = NormalState;
	wmhints->input = True;
	wmhints->flags |= StateHint | InputHint;
	wmhints->icon_pixmap = create_icon ();
	wmhints->flags = IconPixmapHint;


	/* Set the class for this program */
	classhints = XAllocClassHint ();
	classhints->res_name = title1;
	classhints->res_class = title1;

	/* Setup the max and minimum size that the window will be */
	sizehints = XAllocSizeHints ();
	sizehints->flags = PSize | PMinSize | PMaxSize;
	sizehints->min_width = 640;
	sizehints->min_height = 480;
	sizehints->max_width = 640;
	sizehints->max_height = 480;
	/* Create the window/icon name properties */
	if (XStringListToTextProperty (&title, 1, &windowName) == 0)
	{
		fprintf (stderr, "X: Cannot create window name resource!\n");
		exit (3);
	}
	if (XStringListToTextProperty (&title1, 1, &iconName) == 0)
	{
		fprintf (stderr, "X: Cannot create window name resource!\n");
		exit (3);
	}

	/* Now set the window manager properties */
	XSetWMProperties (dp, wi, &windowName, &iconName,
		argv, argc, sizehints, wmhints, classhints);
	XFree ((void *) wmhints);
	XFree ((void *) classhints);
	XFree ((void *) sizehints);

	XMapWindow(dp,wi);
	XSelectInput(dp,wi,KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask);

	copygc=XCreateGC(dp,wi,0,NULL);
	values.function=GXor;
	orgc=XCreateGC(dp,wi,GCFunction,&values);
	values.function=GXand;
	andgc=XCreateGC(dp,wi,GCFunction,&values);
	values.function=GXxor;
	xorgc=XCreateGC(dp,wi,GCFunction,&values);
	afont=XQueryFont(dp,XGContextFromGC(copygc));
	fontbase=afont->ascent;
	fontysize=afont->ascent+afont->descent;
	imageloc=malloc(24*24*4);
	myimage=XCreateImage(dp,DefaultVisual(dp,screen),depth,ZPixmap,0,imageloc,24,24,8,0);
	artwork=XCreatePixmap(dp,wi,24*12,24*32,depth);
	artmask=XCreatePixmap(dp,wi,24*12,24*32,depth);
	bgframe=XCreatePixmap(dp,wi,640,480,depth);
	storeage=XCreatePixmap(dp,wi,24,24*20,depth);
	keyevent=(XKeyEvent *)&event;
	buttonevent=(XButtonEvent *)&event;
	motionevent=(XMotionEvent *)&event;
	exposeevent=(XExposeEvent *)&event;

	clear();

	numpressed=numdown=0;
}

void closex()
{
	XFreeGC(dp,copygc);
	XFreeGC(dp,andgc);
	XFreeGC(dp,orgc);
	XFreeGC(dp,xorgc);
	XDestroyWindow(dp,wi);
	XCloseDisplay(dp);
}

int checkpressed(int code)
{
int *p,i;
	i=numpressed;
	p=pressedcodes;
	while(i--)
		if(*p++==code) return 1;
	return 0;
}

int checkdown(int code)
{
int *p,i;
	i=numdown;
	p=downcodes;
	while(i--)
		if(*p++==code) return 1;
	return 0;
}

int checkbutton(int button)
{
	return buttonstate & (1<<button);
}

int checkbuttondown(int button)
{
	return buttondown & (1<<button);
}

int anydown()
{
	return numdown;
}

int firstdown()
{
	return *downcodes;
}

static int checkrepeat(XEvent *event)
{
XEvent peekevent;
int val;

	val = 0;
	if(XPending(dp))
	{
		XPeekEvent(dp, &peekevent);
		if ( (peekevent.type == KeyPress) &&
		     (peekevent.xkey.keycode == event->xkey.keycode) &&
		     (peekevent.xkey.time == event->xkey.time) ) {
			val = 1;
			XNextEvent(dp, &peekevent);
		}
	}
	return val;
}

void scaninput()
{
int i,*ip,code;
	numdown=0;
	buttondown=0;
	while(XCheckMaskEvent(dp,~0,&event))
		switch(event.type)
		{
		case KeyPress:
			code=XLookupKeysym(keyevent,0);
			if(numdown<KEYMAX)
				downcodes[numdown++]=code;
			ip=pressedcodes;
			i=numpressed;
			while(i)
				if(*ip++==code) break;
				else i--;
			if(!i && numpressed<KEYMAX)
				pressedcodes[numpressed++]=code;
			break;
		case KeyRelease:
			if(checkrepeat(&event)) break;
			code=XLookupKeysym(keyevent,0);
			i=numpressed;
			ip=pressedcodes;
			while(i)
				if(*ip++==code)
				{
					*--ip=pressedcodes[--numpressed];
					break;
				} else i--;
			break;
		case ButtonPress:
			i=1<<buttonevent->button;
			buttonstate|=i;
			buttondown|=i;
			break;
		case ButtonRelease:
			buttonstate&=~(1<<buttonevent->button);
			break;
		case MotionNotify:
			mxpos=motionevent->x;
			mypos=motionevent->y;
			break;
		case Expose:
			copyup();
			needwhole=0;
			break;
		case FocusOut:
			numpressed=0;
			break;
		}
}

void fontinit()
{
uchar *p="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:!?#/\\*-,>< =";
int i;
	for(i=0;i<128;i++) fmap[i]=53;
	i=0;
	while(*p)
	{
		fmap[tolower(*p)]=fmap[*p++]=i++;
	}
}

void writechar(int x,int y,uchar ch)
{
int sx,sy,ch2;

	ch2=fmap[ch];
	sx=216+((ch2%9)<<3);
	sy=120+12*(ch2/9);
	XCopyArea(dp,artwork,bgframe,copygc,sx,sy,8,12,x,y);
}

void clear()
{
	XSetForeground(dp,copygc,0);
	XFillRectangle(dp,bgframe,copygc,0,0,640,480);
}

void xflush() {XFlush(dp);}
void xsync() {XSync(dp,0);}

void drawbox(int x,int y,int size,int color)
{
	XSetForeground(dp,copygc,xcolors[color]);
	XDrawRectangle(dp,bgframe,copygc,x,y,size,size);
}

void drawbox2(int x,int y,int sizex,int sizey,int color)
{
	XSetForeground(dp,copygc,xcolors[color]);
	XDrawRectangle(dp,bgframe,copygc,x,y,sizex,sizey);
}

void drawfillrect(int x,int y,int size,int color)
{
	XSetForeground(dp,copygc,xcolors[color]);
	XFillRectangle(dp,bgframe,copygc,x,y,size,size);
}

void bigpixel(int x,int y,int color)
{
	XSetForeground(dp,copygc,xcolors[color]);
	XFillRectangle(dp,bgframe,copygc,x,y,8,8);
}

void invert(int x,int y)
{
	XSetForeground(dp,xorgc,0xffffffff);
	XFillRectangle(dp,bgframe,xorgc,x,y,11,11);
}

int getmousex() { return mxpos;}
int getmousey() { return mypos;}
void drawsquare(int x,int y,uchar *source)
{
int i,j;
	for(j=0;j<24;j++)
		for(i=0;i<24;i++)
			XPutPixel(myimage,i,j,xcolors[*source++]);
	XPutImage(dp,bgframe,copygc,myimage,0,0,x,y,24,24);
}

void colormapon()
{
int alloc;
Visual *visual;

	visual = DefaultVisual(dp, screen);
	switch (visual->class)
	{
	case StaticGray:
	case StaticColor:
	case TrueColor:
		alloc=AllocNone;
		break;
	default:
		alloc=AllocAll;
		break;
	}
	mycolormap=XCreateColormap(dp,wi,DefaultVisual(dp,screen),alloc);
	XSetWindowColormap(dp,wi,mycolormap);
}

void colormapoff()
{
	XSetWindowColormap(dp,wi,cmap);
	XFreeColormap(dp,mycolormap);
}
int xcolors[256];

void palette(uchar *pal)
{
int i;
XColor acolor;
Visual *visual;
int res;

	visual = DefaultVisual(dp, screen);
	if ((visual->class == StaticGray) || (visual->class == StaticColor) ||
            (visual->class == TrueColor))
		for(i=0;i<256;i++)
		{
			acolor.red=*pal++<<8;
			acolor.green=*pal++<<8;
			acolor.blue=*pal++<<8;
			res=XAllocColor(dp,mycolormap,&acolor);
			xcolors[i]=acolor.pixel;
		}
	else
		for(i=0;i<256;i++)
		{
			acolor.red=*pal++<<8;
			acolor.green=*pal++<<8;
			acolor.blue=*pal++<<8;
			acolor.pixel=i;
			acolor.flags=DoRed | DoGreen | DoBlue;
			xcolors[i]=i;
			XStoreColor(dp,mycolormap,&acolor);
		}
}

void drawstring(char *str,int x,int y,int color)
{
	XSetForeground(dp,copygc,xcolors[color]);
	XDrawString(dp,bgframe,copygc,x,y+12,str,strlen(str));
}
