/*
A simple graphics library for CSE 20211 by Douglas Thain
  Updated / extended by R. Ansari (Dec 2024) Paris-Saclay Univ., for Magistere C-language course project 

This work is licensed under a Creative Commons Attribution 4.0 International License.  https://creativecommons.org/licenses/by/4.0/

For complete documentation, see:
http://www.nd.edu/~dthain/courses/cse20211/fall2013/gfx
Version 3, 11/07/2012 - Now much faster at changing colors rapidly.
Version 2, 9/23/2011 - Fixes a bug that could result in jerky animation.

------ December 2024
Version V=4 ?  27/12/2024 - by R. Ansari , Univ. Paris-Saclay, for Magistere C-language course project  

*/

#include <X11/Xlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "gfx.h"

/*
gfx_open creates several X11 objects, and stores them in globals
for use by the other functions in the library.
*/

static Display *gfx_display=0;
static Window  gfx_window;
static GC      gfx_gc;
static Colormap gfx_colormap;
static int      gfx_fast_color_mode = 0;

/*   R. Ansari - December 2024 , added predefined color values  */
#define NPREDEFCOLS 15 
XColor gfx_predef_colors[NPREDEFCOLS];
/* These values are saved by gfx_wait then retrieved later by gfx_xpos and gfx_ypos. */

static int saved_xpos = 0;
static int saved_ypos = 0;

/* Open a new graphics window - default background color=black  */
void gfx_open( int width, int height, const char *title)
{
  gfx_open_c(width, height, title, gfx_black);
}

/* Open a new graphics window with a specified background color */
void gfx_open_c( int width, int height, const char *title, enum gfx_colors bkgcol)
{
	gfx_display = XOpenDisplay(0);
	if(!gfx_display) {
		fprintf(stderr,"gfx_open: unable to open the graphics window.\n");
		exit(1);
	}

	Visual *visual = DefaultVisual(gfx_display,0);
	if(visual && visual->class==TrueColor) {
		gfx_fast_color_mode = 1;
	} else {
		gfx_fast_color_mode = 0;
	}

	unsigned long blackColor = BlackPixel(gfx_display, DefaultScreen(gfx_display));
	unsigned long whiteColor = WhitePixel(gfx_display, DefaultScreen(gfx_display));

	gfx_colormap = DefaultColormap(gfx_display,0);

	// R. Ansari - December 2024 : Allocating predefinded colors
	XColor exact_color;
	for(int i=0; i<NPREDEFCOLS; i++)
	  XAllocNamedColor(gfx_display, gfx_colormap, "Black", gfx_predef_colors+i, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Black", gfx_predef_colors+(int)gfx_black, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "White", gfx_predef_colors+(int)gfx_white, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Gray", gfx_predef_colors+(int)gfx_grey, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Dark Gray", gfx_predef_colors+(int)gfx_darkgrey, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Red", gfx_predef_colors+(int)gfx_red, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Green", gfx_predef_colors+(int)gfx_green, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Blue", gfx_predef_colors+(int)gfx_blue, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Yellow", gfx_predef_colors+(int)gfx_yellow, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Orange", gfx_predef_colors+(int)gfx_orange, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Magenta", gfx_predef_colors+(int)gfx_magenta, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Purple", gfx_predef_colors+(int)gfx_purple, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Sky Blue", gfx_predef_colors+(int)gfx_skyblue, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Teal", gfx_predef_colors+(int)gfx_teal, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Ivory", gfx_predef_colors+(int)gfx_ivory, &exact_color);
	XAllocNamedColor(gfx_display, gfx_colormap, "Wheat", gfx_predef_colors+(int)gfx_wheat, &exact_color);


	// Creating window 
	unsigned long bkgcolor = blackColor;
	if (bkgcol != gfx_black)  bkgcolor = gfx_predef_colors[(int)bkgcol].pixel;
	gfx_window = XCreateSimpleWindow(gfx_display, DefaultRootWindow(gfx_display), 0, 0, width, height, 0, bkgcolor, bkgcolor);

	XSetWindowAttributes attr;
	attr.backing_store = Always;

	XChangeWindowAttributes(gfx_display,gfx_window,CWBackingStore,&attr);

	XStoreName(gfx_display,gfx_window,title);

	XSelectInput(gfx_display, gfx_window, StructureNotifyMask|KeyPressMask|ButtonPressMask);

	XMapWindow(gfx_display,gfx_window);

	gfx_gc = XCreateGC(gfx_display, gfx_window, 0, 0);
	
	XSetForeground(gfx_display, gfx_gc, whiteColor);

	// Wait for the MapNotify event
	for(;;) {
		XEvent e;
		XNextEvent(gfx_display, &e);
		if (e.type == MapNotify)
			break;
	}
}

/* Unmap the window and closes the connection to XWindow */
void gfx_close()
{
  XUnmapWindow(gfx_display,gfx_window);
  XDestroyWindow(gfx_display,gfx_window);
  XCloseDisplay(gfx_display);
}

/* Draw a single point at (x,y) */

void gfx_point( int x, int y )
{
	XDrawPoint(gfx_display,gfx_window,gfx_gc,x,y);
}

/* Draw a line from (x1,y1) to (x2,y2) */

void gfx_line( int x1, int y1, int x2, int y2 )
{
	XDrawLine(gfx_display,gfx_window,gfx_gc,x1,y1,x2,y2);
}

/* R. Ansari - December 2024 : draw a rectangle  */
void gfx_rect( int x, int y, unsigned int w, unsigned int h)
{
  XDrawRectangle(gfx_display,gfx_window,gfx_gc,x,y,w,h);
}

void gfx_fillrect( int x, int y, unsigned int w, unsigned int h)
{
  XFillRectangle(gfx_display,gfx_window,gfx_gc,x,y,w,h);

}

/* R. Ansari - December 2024 : draw a circle   */
void gfx_circle( int xc, int yc, unsigned int r)
{
  XDrawArc(gfx_display,gfx_window,gfx_gc,xc-(int)r,yc-(int)r,2*r,2*r,0,360*64);
}
/* R. Ansari - December 2024 : draw a filled circle   */
void gfx_fillcircle( int xc, int yc, unsigned int r)
{
  XFillArc(gfx_display,gfx_window,gfx_gc,xc-(int)r,yc-(int)r,2*r,2*r,0,360*64);
}

/* R. Ansari - December 2024 : draw a string at the specified position   */
void gfx_string( int x, int y, const char * str)
{
  XDrawString(gfx_display,gfx_window,gfx_gc,x,y,str,strlen(str));
}


/* Change the current drawing color. */

void gfx_color( int r, int g, int b )
{
	XColor color;

	if(gfx_fast_color_mode) {
		/* If this is a truecolor display, we can just pick the color directly. */
		color.pixel = ((b&0xff) | ((g&0xff)<<8) | ((r&0xff)<<16) );
	} else {
		/* Otherwise, we have to allocate it from the colormap of the display. */
		color.pixel = 0;
		color.red = r<<8;
		color.green = g<<8;
		color.blue = b<<8;
		XAllocColor(gfx_display,gfx_colormap,&color);
	}

	XSetForeground(gfx_display, gfx_gc, color.pixel);
}

/* R. Ansari - December 2024 : Change the current drawing color, specified as gfx_colors  */
void gfx_color_p(enum gfx_colors col)
{
  XSetForeground(gfx_display, gfx_gc,  gfx_predef_colors[(int)col].pixel);
}

/* Clear the graphics window to the background color. */

void gfx_clear()
{
	XClearWindow(gfx_display,gfx_window);
}

/* Change the current background color. */

void gfx_clear_color( int r, int g, int b )
{
	XColor color;
	color.pixel = 0;
	color.red = r<<8;
	color.green = g<<8;
	color.blue = b<<8;
	XAllocColor(gfx_display,gfx_colormap,&color);

	XSetWindowAttributes attr;
	attr.background_pixel = color.pixel;
	XChangeWindowAttributes(gfx_display,gfx_window,CWBackPixel,&attr);
}

/*   R. Ansari - December 2024 ,Change the current background color, selection from pre-defined colors  */
void gfx_clear_color_p(enum gfx_colors col)
{
	XSetWindowAttributes attr;
	attr.background_pixel = gfx_predef_colors[(int)col].pixel;
	XChangeWindowAttributes(gfx_display,gfx_window,CWBackPixel,&attr);
}

int gfx_event_waiting()
{
       XEvent event;

       gfx_flush();

       while (1) {
               if(XCheckMaskEvent(gfx_display,-1,&event)) {
                       if(event.type==KeyPress) {
                               XPutBackEvent(gfx_display,&event);
                               return 1;
                       } else if (event.type==ButtonPress) {
                               XPutBackEvent(gfx_display,&event);
                               return 1;
                       } else {
                               return 0;
                       }
               } else {
                       return 0;
               }
       }
}

/* Wait for the user to press a key or mouse button. */

char gfx_wait()
{
	XEvent event;

	gfx_flush();

	while(1) {
		XNextEvent(gfx_display,&event);

		if(event.type==KeyPress) {
			saved_xpos = event.xkey.x;
			saved_ypos = event.xkey.y;
			return XLookupKeysym(&event.xkey,0);
		} else if(event.type==ButtonPress) {
			saved_xpos = event.xkey.x;
			saved_ypos = event.xkey.y;
			return event.xbutton.button;
		}
	}
}

/* Return the X and Y coordinates of the last event. */

int gfx_xpos()
{
	return saved_xpos;
}

int gfx_ypos()
{
	return saved_ypos;
}

/* Flush all previous output to the window. */

void gfx_flush()
{
	XFlush(gfx_display);
}

