// lcx11.cxx    X11 stuff - part of lin-city
// Copyright (c) I J Peters 1995,1996.  Please read the file 'COPYRIGHT'.

extern "C" {

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef SUNOS
#ifndef HP_UX
//#include <getopt.h>
#endif
#endif
#undef _DOS_H
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>

}
#include "lcx11.h"
#include "lin-city.h"

void setcustompalette(void)
{
        char s[100];
        int i,n,r,g,b,me=0,flag[256];
	long unsigned int plane_masks[3];
        FILE *inf;
	XColor pal[256];
	display.cmap=XDefaultColormap(display.dpy,display.screen);
	if (XAllocColorCells(display.dpy,display.cmap,0
		,plane_masks,0,colour_table,256)==0)
	{
		me=(*DefaultVisual(display.dpy,display.screen)).map_entries;
printf("DefaultVisual id=%d bp-rgb=%d map-entries=%d\n"
 ,(*DefaultVisual(display.dpy,display.screen)).visualid
 ,(*DefaultVisual(display.dpy,display.screen)).bits_per_rgb
 ,(*DefaultVisual(display.dpy,display.screen)).map_entries);
        	display.cmap=XCreateColormap(display.dpy,display.win  
                	,DefaultVisual(display.dpy,display.screen)
		/*	,PseudoColor */
			,AllocNone);
	XSetWindowColormap(display.dpy,display.win,display.cmap);
	        if (me==256 && XAllocColorCells(display.dpy,display.cmap,0
                	,plane_masks,0,colour_table,256)!=0)
			printf("Allocated 256 cells\n");
		else
			for (i=0;i<256;i++)
				colour_table[i]=i;
	}
        if (!display.cmap)
                HandleError("No default colour map",FATAL);
//printf("Got past create colourmap\n");

        for (i=0;i<256;i++)
                flag[i]=0;
	
        if ((inf=fopen(colour_pal_file,"r"))==0)
                HandleError("Can't find the colour pallet file",FATAL);

        while (feof(inf)==0)
        {
                fgets(s,99,inf);
                if (sscanf(s,"%d %d %d %d",&n,&r,&g,&b)==4)
                {
                        pal[n].red=r << 10;
                        pal[n].green=g << 10;
                        pal[n].blue=b << 10;
                        pal[n].flags= DoRed | DoGreen | DoBlue;
			if (me==256)
				pal[n].pixel=colour_table[n];
			else
			{
				if (XAllocColor(display.dpy
					,display.cmap,&(pal[n]))==0)
					HandleError("alloc colour failed"
						,FATAL);
				colour_table[n]=pal[n].pixel;
				XSetForeground(display.dpy
					,display.pixcolour_gc[n]
					,colour_table[n]);
			}
                        flag[n]=1;
                }
        }
        fclose (inf);
        for (i=0;i<256;i++)
                if (flag[i]==0)
                {
                        printf("Colour %d not loaded\n",i);
                        do_error("Can't continue");
                }
//	if (me==256)
//	        XStoreColors(display.dpy,display.cmap,pal,256);
//        XInstallColormap(display.dpy,display.cmap);
//        XSetWindowColormap(display.dpy,display.win,display.cmap);
	if (me==256)
	{
		XStoreColors(display.dpy,display.cmap,pal,256);
		XFlush(display.dpy);
	}
//	XInstallColormap(display.dpy,display.cmap);
}

void open_setcustompalette(XColor *inpal)
{
        char s[100];
        int i,n,r,g,b,me=0,flag[256];
	long unsigned int plane_masks[3];
	XColor pal[256];
	display.cmap=XDefaultColormap(display.dpy,display.screen);
	if (XAllocColorCells(display.dpy,display.cmap,0
		,plane_masks,0,colour_table,256)==0)
	{
		me=(*DefaultVisual(display.dpy,display.screen)).map_entries;
        	display.cmap=XCreateColormap(display.dpy,display.win  
                	,DefaultVisual(display.dpy,display.screen)
		/*	,PseudoColor */
			,AllocNone);
	        if (me==256 && XAllocColorCells(display.dpy,display.cmap,0
                	,plane_masks,0,colour_table,256)!=0)
			printf("Allocated 256 cells\n");
		else
			for (i=0;i<256;i++)
				colour_table[i]=i;
	}
        if (!display.cmap)
                HandleError("No default colour map",FATAL);
//printf("Got past create colourmap\n");

        for (i=0;i<256;i++)
                flag[i]=0;

	for (n=0;n<256;n++)
	{
                pal[n].red=inpal[n].red << 10;
                pal[n].green=inpal[n].green << 10;
                pal[n].blue=inpal[n].blue << 10;
                pal[n].flags= DoRed | DoGreen | DoBlue;
		if (me==256)
			pal[n].pixel=colour_table[n];
		else
		{
			if (XAllocColor(display.dpy
				,display.cmap,&(pal[n]))==0)
				HandleError("alloc colour failed"
					,FATAL);
			colour_table[n]=pal[n].pixel;
			XSetForeground(display.dpy
				,display.pixcolour_gc[n]
				,colour_table[n]);
		}
                flag[n]=1;
        }
        for (i=0;i<256;i++)
                if (flag[i]==0)
                {
                        printf("Colour %d not loaded\n",i);
                        do_error("Can't continue");
                }
//	if (me==256)
//	        XStoreColors(display.dpy,display.cmap,pal,256);
        XSetWindowColormap(display.dpy,display.win,display.cmap);
	if (me==256)
	{
		XStoreColors(display.dpy,display.cmap,pal,256);
		XFlush(display.dpy);
	}
}

void initfont()
{
	int i;
	FILE *finf;
	if ((finf=fopen(fontfile,"r"))==0)
		HandleError("Can't open the font file",FATAL);
	for (i=0;i<256*8;i++)
		myfont[i]=fgetc(finf);
	fclose(finf);
}

void Fgl_setfontcolors(int bg,int fg)
{
	text_fg=fg;
	text_bg=bg;
}

int globGetopt=0;
int getopt(int argc,char **argv,char *unused)
  {
  char *px;
  globGetopt++;
  if (argc<=globGetopt)
    return(EOF);
  px=argv[globGetopt];
  if (px==0) return(EOF);
  if (*px=='-') px++;
  if (*px==0) return('-');
  return(*px);
  }


void parse_args(int argc, char **argv, char **geometry)
{
	int         option;
	extern int  optind;
//	extern char *optarg;

	int bits,i;

	while ((option = getopt(argc,argv,"vbng:")) != EOF)
	{
        	switch (option)
        	{
            		case 'v':
                		verbose=TRUE;
                		break;
//           		case 'g':
//               		*geometry=optarg;
//               		break;
			case 'b' :
				borderx=0;
				bordery=0;
				break;
			case 'n' :
				no_init_help=TRUE;
				break;
		}
	}
	if (verbose)
		printf("Version %d\n",VERSION);
	if (!(display.dpy = XOpenDisplay(display.dname)))
	{
printf(" Can't open the dispay!\n");
		HandleError("Cannot open display.\n",FATAL);
		exit(-1);
	}
/* Record the screen number and root window. */
	display.screen = DefaultScreen(display.dpy);
	display.root = RootWindow(display.dpy, display.screen);

        winW = WINWIDTH+borderx*2;
        winH = WINHEIGHT+bordery*2;
        winX = (DisplayWidth(display.dpy, display.screen)
                - winW) / 2;
        winY = (DisplayHeight(display.dpy, display.screen)
                - winH) / 2;
        if (*geometry != NULL) 
            XParseGeometry(*geometry, &winX, &winY, &winW, &winH);
}


void Create_Window(char *geometry)
{
	short q;
	XSetWindowAttributes xswa;
	XSizeHints          sizehint;
	XWMHints            wmhints;
	unsigned char       wname[256];     /* Window Name */

        xswa.event_mask = 0;
        xswa.background_pixel = display.bg;
printf("DefaultVisual id=%d bp-rgb=%d map-entries=%d\n"
 ,(*DefaultVisual(display.dpy,display.screen)).visualid
 ,(*DefaultVisual(display.dpy,display.screen)).bits_per_rgb
 ,(*DefaultVisual(display.dpy,display.screen)).map_entries);
        display.win = XCreateWindow(display.dpy, display.root, 
            winX, winY, 
            winW, winH, 0, 
            DefaultDepth(display.dpy, display.screen), 
            InputOutput,  DefaultVisual(display.dpy, display.screen),
	/*	PseudoColor,  */
            CWEventMask | CWBackPixel , &xswa);
if (display.win==0) {printf("Cannot open Window\n");exit(1);}
        sizehint.x = winX;
        sizehint.y = winY;
        sizehint.width = winW;
        sizehint.height = winH;
        sizehint.min_width = winW;
        sizehint.min_height = winH;
        if (geometry != NULL)
            sizehint.flags = USPosition | USSize | PMinSize;
        else
            sizehint.flags = PPosition | PSize | PMinSize;
         XSetNormalHints(display.dpy, display.win, &sizehint);
            
        display.protocol_atom = XInternAtom(display.dpy, "WM_PROTOCOLS",
False);
        display.kill_atom = XInternAtom(display.dpy, "WM_DELETE_WINDOW",
False);

        /* Title */
        sprintf( (char *) wname, "xlin-city, Version %d, by IJ Peters",
            VERSION);
        XChangeProperty(display.dpy, display.win,
            XA_WM_NAME, XA_STRING, 8, PropModeReplace, wname, 
            strlen((char *)wname));

        /* Window Manager Hints (This is supposed to make input work.) */
        wmhints.flags = InputHint;
        wmhints.input = True;
        XSetWMHints(display.dpy, display.win, &wmhints);
       XMapWindow(display.dpy, display.win);
        XSelectInput(display.dpy, display.win,
                 KeyPressMask | ButtonPressMask | ButtonReleaseMask 
		| ExposureMask | StructureNotifyMask);
	for (q=0;q<256;q++)
	{
		display.pixcolour_gc[q]=XCreateGC(display.dpy
			,display.win,0,NULL);
		XSetForeground(display.dpy,display.pixcolour_gc[q],q);
		XSetBackground(display.dpy,display.pixcolour_gc[q]
			,display.bg);
	}
}


void HandleError(char *description, int degree)
{
    fprintf(stderr, "An error has occurred.  The description is below...\n");
    fprintf(stderr, "%s\n", description);

    if (degree == FATAL)
    {
        fprintf(stderr, "Program aborting...\n");
        exit(-1);
    }
}

void Fgl_setpixel(int x,int y,int col)
{
        int i;
	if (clipping_flag)
		if (x<xclip_x1 || x>xclip_x2 || y<xclip_y1 || y>xclip_y2)
			return; 
	col&=0xff;
//printf("In setpixel\n"); 
       i=(y+bordery)*(640+BORDERX)+x+borderx;
        if ((int)*(pixmap+i)!=col)
        {
                *(pixmap+i)=(unsigned char)col;
                XDrawPoint(display.dpy,display.win
                        ,display.pixcolour_gc[col],x+borderx,y+bordery);
        }
}

int Fgl_getpixel(int x,int y)
{
        return(*(pixmap+(y+bordery)*(640+BORDERX)+x+borderx));
}

void Fgl_hline(int x1,int y1,int x2,int col)
{
        int x,i;
//	if (clipping_flag)
//	{
//		if (x1<xclip_x1)
//			x1=xclip_x1;
//		else if (x1>xclip_x2)
//			x1=xclip_x2;
//		if (y1<xclip_y1)
//			return;
//		else if (y1>xclip_y2)
//			return;
//               if (x2<xclip_x1)
//                        x2=xclip_x1;
//                else if (x2>xclip_x2)
//                        x2=xclip_x2;
//	}

//printf("In hline %d %d %d %d\n",x1,y1,x2,col);
	col&=0xff;
        i=(y1+bordery)*(640+BORDERX);
        for (x=x1+borderx;x<=x2+borderx;x++)
                *(pixmap+i+x)=col;
        XDrawLine(display.dpy,display.win
                ,display.pixcolour_gc[col],x1+borderx
			,y1+bordery,x2+borderx,y1+bordery);
}

void Fgl_line(int x1,int y1,int dummy,int y2,int col)
// vertical lines only.
{
        int y,i;
//printf("In (v)line %d %d %d %d\n",x1,y1,y2,col);
//        if (clipping_flag)
//        {
//                if (x1<xclip_x1)
//                        x1=xclip_x1;
//                else if (x1>xclip_x2)
//                        x1=xclip_x2;
//                if (y1<xclip_y1)
//                        y1=xclip_y1;
//                else if (y1>xclip_y2)
//                        y1=xclip_y2;
//                if (y2<xclip_y1)
//                        y2=xclip_y1;
//                else if (y2>xclip_y2)
//                        y2=xclip_y2;
//	}

	col&=0xff;
        for (y=y1+bordery;y<=y2+bordery;y++)
                *(pixmap+x1+borderx+(y*(640+BORDERX)))=col;
//printf("In line - got set up pixmap\n");
        XDrawLine(display.dpy,display.win
                ,display.pixcolour_gc[col],x1+borderx
			,y1+bordery,x1+borderx,y2+bordery);
}

void Fgl_write(int x,int y, char *s)
{
	int i;
//printf("writing <%s>\n",s);
	for (i=0;i<strlen(s);i++)
		my_x_putchar(x+i*8,y,s[i]);
}

void open_write(int x,int y, char *s)
{
        int i;
//printf("writing <%s>\n",s);
        for (i=0;i<strlen(s);i++)
                open_x_putchar(x+i*8,y,s[i]);
}

void my_x_putchar(int xx,int yy,int c)
{
	int x,y,b;
//printf(" %c",c);
	for (y=0;y<8;y++)
	{
		b=myfont[c*8+y];
		for (x=0;x<8;x++)
		{
			if ((b & 0x80)==0)
				Fgl_setpixel(xx+x,yy+y,text_bg);
			else
				Fgl_setpixel(xx+x,yy+y,text_fg);
			b=b<<1;
		}
	}
}

void open_x_putchar(int xx,int yy,int c)
{                                          
        int x,y,b;                         
//printf(" %c",c);
        for (y=0;y<open_font_height;y++)                  
        {                                  
                b=open_font[c*open_font_height+y];           
                for (x=0;x<8;x++)          
                {                          
                        if ((b & 0x80)==0) 
                                Fgl_setpixel(xx+x,yy+y,text_bg);
                        else               
                                Fgl_setpixel(xx+x,yy+y,text_fg);
                        b=b<<1;            
                }                          
        }                                  
}

void Fgl_fillbox(int x1,int y1,int w,int h,int col)
{
	int x,y;
	if (clipping_flag)
	{
		if (x1<xclip_x1)
			x1=xclip_x1;
		if (x1+w>xclip_x2)
			w=xclip_x2-x1;
		if (y1<xclip_y1)
			y1=xclip_y1;
		if (y1+h>xclip_y2)
			h=xclip_y2-y1;
	}
	col&=0xff;
//printf("In fillbox %d %d %d %d %d\n",x1,y1,w,h,col);
//	if ((y1+h)>480)
//	{
//		printf("Bad y1+h in fillbox val=%d\n",y1+h);
//		h=y1-480;
//	}
	for (y=y1+bordery;y<y1+h+bordery;y++)
	{
		for (x=x1+borderx;x<x1+w+borderx;x++)
			*(pixmap+y*(640+BORDERX)+x)=col;
		XDrawLine(display.dpy,display.win,display.pixcolour_gc[col]
		,x1+borderx,y,x1+w-1+borderx,y);
	}
//printf("Done\n");
}

void Fgl_putbox(int x1,int y1,int w,int h,void *buf)
{
	unsigned char *b;
	b=(unsigned char *)buf;
	int x,y;
//printf("In Fgl_putbox(%d %d %d %d %p\n",x1,y1,w,h,buf);
	for (y=y1;y<y1+h;y++)
		for (x=x1;x<x1+w;x++)
		{
//printf(" %d ",*b);
			Fgl_setpixel(x,y,*(b++));
		}
}

void Fgl_getbox(int x1,int y1,int w,int h,void *buf)
{
        unsigned char *b;
        b=(unsigned char *)buf;
        int x,y;
        for (y=y1;y<y1+h;y++)
                for (x=x1;x<x1+w;x++)
                        *(b++)=(unsigned char)Fgl_getpixel(x,y);
}

void HandleEvent(XEvent *event)
{
//printf("XEvent - type=%d\n",event->type);
	switch (event->type)
	{
		case (KeyPress):
		{
			XKeyEvent *key_event = (XKeyEvent *) event;
                	char buf[128];
                	KeySym ks;
                	XComposeStatus status;
			XLookupString(key_event,buf,128,&ks,&status);
//			if (buf[0]=='q' || buf[0]=='Q')
//				stopflag=TRUE;
//			else if (buf[0]=='r' || buf[0]=='R')
//				refresh_screen(0,0,640,480);
			x_key_value=buf[0];
//printf("Key=%d\n",x_key_value);
			if (x_key_value==XK_Return)
				x_key_value=0xd;
			else if (x_key_value==XK_BackSpace
				|| x_key_value==XK_Delete || x_key_value==8)
				x_key_value=127;
		} break;

		case (ButtonPress):
		{
			XButtonEvent *ev = (XButtonEvent *) event;
			mouse_button=ev->button;
//printf("Button %d pressed\n",mouse_button);
//			do_buttons(0,0);
			cs_mouse_handler(mouse_button,0,0);
		} break;

		case (ButtonRelease):
		{
//printf("Button released\n");
			mouse_button=0;
			cs_mouse_handler(mouse_button,0,0);
		} break;

		case (Expose):
		{
			if (suppress_next_expose)
			{
// printf("Expose event supressed\n");
				suppress_next_expose=0;
				break;
			}
			XExposeEvent *ev = (XExposeEvent *) event;
// printf("Expose event %d %d %d %d\n",ev->x,ev->y,ev->x+ev->width
// ,ev->y+ev->height);
			refresh_screen(ev->x,ev->y
				,ev->x+ev->width,ev->y+ev->height);
		} break;

//		default: printf("Unknown event %d\n",event->type);
	}
}

void refresh_screen(int x1,int y1,int x2,int y2)  // bounds of refresh area
{
	int x,y;
//printf("In refresh %d %d %d %d\n",x1,y1,x2,y2);
	for (y=y1;y<y2;y++)
		for (x=x1;x<x2;x++)
                XDrawPoint(display.dpy,display.win
                        ,display.pixcolour_gc[*(pixmap
				+x+y*(640+BORDERX))&0xff],x,y);
//			Fgl_setpixel(x,y,*(pixmap+x+y*(640+BORDERX)));
//printf("Got past draws\n");
}

void Fgl_enableclipping(void)
{
	clipping_flag=1;
}

void Fgl_setclippingwindow(int x1,int y1,int x2,int y2)
{
	xclip_x1=x1;
	xclip_y1=y1;
	xclip_x2=x2;
	xclip_y2=y2;
}

void Fgl_disableclipping(void)
{
	clipping_flag=0;
}

void do_call_event(int wait)
{
	static int oldx=0,oldy=0;
        int dummy_int,x,y;
        Window dummy_win;
//printf("In call event\n");
	XEvent xev;
//	x_key_value=0;
        if (XPending(display.dpy) )  // || wait!=0)
        {
                XNextEvent(display.dpy, &xev);
                HandleEvent(&xev);
        }
	else if (wait)
		lc_usleep(1000);

        XQueryPointer(display.dpy,display.win,&dummy_win,&dummy_win
                ,&dummy_int,&dummy_int,&x,&y,(unsigned int *) &dummy_int);
	x-=borderx;
	y-=bordery;
	if (x!=oldx || y!=oldy)
	{
		cs_mouse_handler(mouse_button,x-oldx,y-oldy);
		oldx=x;
		oldy=y;
	}
}

void call_event(void)
{
        do_call_event(0);
}

void call_wait_event(void)
{
	do_call_event(1);
}

