/*********************************************************************
*                                                                    *          
*                              FLY                                   *
*                           --E#Y#E--				     *
*                         ===MUSCLE===                               *
*                                                                    *
*********************************************************************/
/* Header Files */
#include "win.h"
 
/*const char bitfont[] = {"*"};*/
/*static char bitfont[] = {"-misc-liberation mono-bold-i-normal--0-0-0-0-m-0-iso8859-15"};*/
float FPSE;

/* Display Variables */
unsigned long windowAttributes;
static Display *display;
static Window monitor;
static int depth;
int screen;

/*-------------------------------------------------------------------------*/

/* Display Attributes */
int att[] =   { GLX_RENDER_TYPE, 
                GLX_RGBA_BIT,
                GLX_DRAWABLE_TYPE, 
                GLX_WINDOW_BIT,
                #if USE_DOUBLEBUFFER
                GLX_DOUBLEBUFFER, TRUE,
                #else
                GLX_DOUBLEBUFFER, FALSE,
                #endif
                GLX_USE_GL,
		GLX_RGBA,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_ALPHA_SIZE, 4,
		GLX_AUX_BUFFERS,  0,
		GLX_ACCUM_RED_SIZE, 0,
		GLX_ACCUM_GREEN_SIZE, 0,
		GLX_ACCUM_BLUE_SIZE, 0, 
		GLX_ACCUM_ALPHA_SIZE, 0,
		GLX_BUFFER_SIZE, 16,
		GLX_DEPTH_SIZE, 16,
		GLX_STENCIL_SIZE, 8,
		None };

/*-------------------------------------------------------------------------*/

/* Display Context */
GC 			mono;
Font 			flyfont;
XFontStruct		*flyinfo;
XVisualInfo             *vi;
Colormap                cmap;
XColor			color;
Window                  win;
GLXWindow               glx;
GLXContext              glc;
GLXFBConfig             *fbconfigs, fbconfig;
XWindowAttributes       gwa;
XEvent                  xev;

/*-------------------------------------------------------------------------*/

/* FB config */
void desc_fbconfig(GLXFBConfig fbconfig)
{
	int doublebuffer;
	int red_bits, green_bits, blue_bits, alpha_bits; 
	int buffer_bits, depth_bits, stencil_bits;

	glXGetFBConfigAttrib(display, fbconfig, GLX_DOUBLEBUFFER, &doublebuffer);
	glXGetFBConfigAttrib(display, fbconfig, GLX_RED_SIZE, &red_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_GREEN_SIZE, &green_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_BLUE_SIZE, &blue_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_ALPHA_SIZE, &alpha_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_BUFFER_SIZE, &buffer_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_DEPTH_SIZE, &depth_bits);
	glXGetFBConfigAttrib(display, fbconfig, GLX_STENCIL_SIZE, &stencil_bits);
	
	printf(" FB CONFIG SELECTED: \n"
	       " DOUBLEBUFFER: %d \n"
	       " RED BITS: %d \n" 
	       " GREEN BITS: %d \n"
	       " BLUE BITS: %d \n"
	       " ALPHA BITS: %d \n" 
	       " BUFFER BITS: %d \n"
	       " DEPTH BITS: %d \n"
	       " STENCIL BITS: %d \n",
	       
	doublebuffer, 
	red_bits, green_bits, blue_bits, alpha_bits, 
	buffer_bits, depth_bits, stencil_bits);
}

/*-------------------------------------------------------------------------*/

/* Create Window */
void createWin(int width, int height, int bpp, char wide)
{	
        int x; int y;
        static XSizeHints sizeh;
        XSetWindowAttributes swa;

	/* Window-Name */
	const char *sname = "[ LANDSCAPE - 1.4 ]";
	const char *nicon = "[ LANDSCAPE - 1.4 ]";
	
	/* Connect to X-Server */
	if((display = XOpenDisplay(NULL)) == NULL) 
	{
		printf(" CANNOT CONNECT TO X SERVER: \n");
		exit(0);
	}

	/* Default Window */
	screen = DefaultScreen(display);            /* Get the default screen id */
	monitor = RootWindow(display, screen);
	/*monitor = DefaultRootWindow(display);*/
      
	#if USE_CHOOSE_FBCONFIG
	int i = 0;
	int bestMode = -1;
	static int modeNum;
	
	fbconfigs = glXChooseFBConfig(display, screen, att, &modeNum);
	fbconfig = 0;
	
	/* Graphic Mode */
        for(i = 0; i < modeNum; i++)
        {
                /* Visual Config */
        	vi = (XVisualInfo*) glXGetVisualFromFBConfig(display, fbconfigs[i]);
		if(!vi)
		{
		        continue;
		}
                
                /* FB Config */
                fbconfig = fbconfigs[i];  
                /* FB Mode */
                if(fbconfigs[i])
                {
                        bestMode = i;
                }
        }
	
	/* FB config */
	if(!fbconfig) 
	{
		printf(" NO MATCHING FB CONFIG FOUND: \n");
		exit(0);
	}
        
        /* Graphic Config */
        desc_fbconfig(fbconfig);
        if(bestMode == -1)
        {
                printf(" COULD NOT FIND A SUITABLE GRAPHICS MODE: \n");
                exit(0);
        }
	#else
	
	/* Choose Visual */ 
	if((vi = glXChooseVisual(display, screen, att)) == NULL) 
	{
		printf(" NO MATCHING VISUAL: \n");
		exit(0);
	}
	#endif
      
	/* Default Depth */
    	depth = XDefaultDepth(display, monitor);
        
	/* Create Colormap */
	if((cmap = XCreateColormap(display, monitor, vi->visual, AllocNone)) == 0) 
	{
		printf(" CANNOT CREATE COLORMAP: \n");
		exit(0);
	}
	
	/*------------------------------------------------------------------*/
		
	swa.background_pixel = 0;
	swa.border_pixel = 0;
	swa.colormap = cmap;
	swa.event_mask =  
	                  ExposureMask | 
	                  KeyPressMask | 
	                  KeyReleaseMask | 
	                  ButtonPressMask | 
	                  ButtonReleaseMask | 
	                  OwnerGrabButtonMask |
	                  EnterWindowMask | 
	                  LeaveWindowMask | 
	                  StructureNotifyMask;
	                  
	/* Window Border */                  
	swa.override_redirect = FALSE;
	
	/* Window Atrributes */
	windowAttributes = 
	               	   CWBackPixel | 
	                   CWBorderPixel | 
	                   CWColormap |  
	                   CWEventMask | 
	                   CWOverrideRedirect; 
	
	/* Max-Min Size */
	sizeh.flags = PSize | PMinSize | PMaxSize;
        x = width, y = height;
        sizeh.min_width = width;
	sizeh.min_height = height;
	
	/*------------------------------------------------------------------*/
	
	/* Fullscreen */
	if(wide)
	{
		/* Fullscreen Screen */
		windowAttributes = 
		                   CWBorderPixel | 
		                   CWColormap | 
		                   CWEventMask | 
		                   CWOverrideRedirect; 
		
		/* X Display */
		width = XDisplayWidth(display, screen);
		height = XDisplayHeight(display, screen);
		x = width, y = height;
		sizeh.max_width = width;
	        sizeh.max_height = height;
		
		/* Window Border */
		swa.override_redirect = FALSE;
		
		/* Pointer & Keyboard */
		XWarpPointer(display, None, monitor, 0, 0, 0, 0, 0, 0);
                XMapRaised(display, monitor);
		XGrabKeyboard(display, monitor, TRUE, GrabModeAsync, GrabModeAsync, CurrentTime);
		XGrabPointer(display, monitor, TRUE, ButtonPressMask, GrabModeAsync, 
		                        GrabModeAsync, monitor, None, CurrentTime);
	}
	
	/*------------------------------------------------------------------*/

	/* Create Window */
	win = XCreateWindow(display, 
	                    monitor,
	                    x, y, width, height,
	                    2, 
	                    vi->depth, 
	                    InputOutput, 
	                    vi->visual, 
	                    windowAttributes, 
	                    &swa);
	
	#if USE_GLX_CREATE_WINDOW
	int glXattr[] = { None };
	glx = glXCreateWindow(display, fbconfig, win, glXattr);
	if(!glx) 
	{
		printf(" COULD NOT CREATE THE GLX WINDOW \n");
		exit(0);
	}
        #else
	glx = win;
        #endif
        
        /*------------------------------------------------------------------*/
	
	/* Properties */
	XSetStandardProperties(display,win,sname,nicon,None,0,0,&sizeh);
	XStoreName(display, win, "[ LANDSCAPE - 1.4 ]");
	        
	/* Map Window */
	XMapWindow(display, win);
}
  
/*-------------------------------------------------------------------------*/
  
/* Error Handler */
int ctxErrorHandler(Display *display, XErrorEvent *ev)
{
        printf(" ERROR AT CONTEXT CREATION: \n");
        return 0;
}

/*-------------------------------------------------------------------------*/

/* Create Render Context */
void createRender(int width, int height, int bpp, char wide)
{
	/* Window Attributes */
	XGetWindowAttributes(display, win, &gwa);

	int dummy;
	if(!glXQueryExtension(display, &dummy, &dummy)) 
	{
		printf(" OPENGL NOT SUPPORTED BY X SERVER: \n");
		exit(0);
	}
	
	/* GLEW */
	glewInit();
	
	/*------------------------------------------------------------------*/
	
	#if USE_GLX_CREATE_CONTEXT_ATTRIB
	
	#define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
	#define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
        
        glc = NULL;
        
        /* Extension Supported */
        if(glewIsExtensionSupported(glXQueryExtensionsString(display, screen)), "GLX_ARB_create_context" ) 
        {
	typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, char, const int*);
	glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 
	(glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte *) "glXCreateContextAttribsARB" );
	
	if(glXCreateContextAttribsARB) 
	{
      
      	/* Context Attribs */
        int context_attribs[] =
	{
	      GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
	      GLX_CONTEXT_MINOR_VERSION_ARB, 0,
	      GLX_CONTEXT_FLAGS_ARB, 
	      GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
	      /*GLX_CONTEXT_DEBUG_BIT_ARB,*/
	      0
	};
      
	int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(ctxErrorHandler);
	
	/* Create Context */
	glc = glXCreateContextAttribsARB(display, fbconfig, 0, TRUE, context_attribs);
	
	XSync( display, FALSE );
	XSetErrorHandler( oldHandler );
      	
        } else {
	          printf(" CREATE CONTEXT COULD NOT BE RETRIEVED: \n"); }
	} else { 
	          printf(" CREATE CONTEXT NOT SUPPORTED: \n"); }
        
	{
	#else 
	{
        #endif
        
        /*------------------------------------------------------------------*/
        
        #if USE_VERSION
        glc = glXCreateNewContext(display, fbconfig, GLX_RGBA_TYPE, 0, TRUE);
        #else
        glc = glXCreateContext(display, vi, 0, TRUE);
        #endif
        
        if(!glc) 
        {
		  printf("FAILED TO CREATE A GL CONTEXT: \n");
		  exit(0);
	}}
	
        /* Make Current */
	/*glXMakeCurrent(display, win, glc);*/ 
	glXMakeContextCurrent(display, glx, glx, glc); 
	
	/* Screen Resolution */
	printf(" SCREEN RESOLUTION: %dx%d \n", gwa.width, gwa.height);
	
	/* Debug */
	/*glEnable(GL_DEBUG_OUTPUT);*/
}

/*-------------------------------------------------------------------------*/

/* Look / View */
void perspective(int width, int height) 
{	
	/* Window */
	XGetWindowAttributes(display, win, &gwa);
	
	/* Viewport */
	glViewport(0, 0, gwa.width, gwa.height);
	glScissor(0, 0, gwa.width, gwa.height);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

        /* Perspective */
        const GLdouble fieldOfView = 52.0f;
        const GLdouble aspect = (float)(gwa.width) / (float)(gwa.height);
        const GLdouble zNear = 1.0f;
        const GLdouble zFar = 100.0f;

	/* Perspective */
	gluPerspective(fieldOfView, aspect, zNear, zFar);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	#if USE_DOUBLEBUFFER
	glXSwapBuffers(display, glx);
        #else
	glFlush();
	/* usleep(10000); */
	#endif

	/* Screen Resolution */
	printf(" %dx%d SCREEN ", gwa.width, gwa.height);
}

/*-------------------------------------------------------------------------*/

/* Event Loop */
void eventloop() 
{ 	
	/* Events */
	XEvent xev;
	int events;
	XFlush(display);
	events = XPending(display);
	while(( events != 0 )) 
	{
		/* Events */
		events--;
		XNextEvent(display, &xev);
		process_event2(xev);
		process_event1(xev);
	}
}

/*---------------------------------------------------------------------------*/

/* X-Load Font */
void loadFont()
{
        #if X_FONT    
        char muser[ID];
        char screen[ID];
        char frames[ID];
        char mdate[DATE];
        
        /* Date Time */
        struct tm *date;
        time_t ndate;	
	time(&ndate);
	date = localtime(&ndate);
        
        mono = XCreateGC(display, win,0,0);
        XClearWindow(display, win);
        
        /* Load Font */
        flyfont = XLoadFont(display, bitfont);
        flyinfo = XQueryFont(display, flyfont);
        XSetFont(display, mono, flyfont);

        /* Print Font */
        strftime(mdate, DATE," %X", date);
        sprintf(muser,"[ Vulkano ]:");
        sprintf(screen," %4d x%4d", gwa.width, gwa.height);
        sprintf(frames," %04.2f fps", FPSE);
	
	/* Draw Font White */
	XSetFont(display, mono, flyfont);
        XSetForeground(display, mono , 248248255);
        XDrawString(display, win, mono , 5, 25, mdate, ID);
        /* Draw Font Orange */
        XSetFont(display, mono, flyfont);
        XSetForeground(display, mono , 100100100);
        XDrawString(display, win, mono , 110, 25, muser, ID);
        /* Draw Font White */
        XSetFont(display, mono, flyfont);
        XSetForeground(display, mono , 248248255);
        XDrawString(display, win, mono , gwa.width - 240, 25, screen, ID);
        XDrawString(display, win, mono , gwa.width - 110, 25, frames, ID);

        /* Free Mem */
        XUnloadFont(display, flyfont);
        XFreeGC(display, mono);
        XFlush(display);
	#endif
}

/*-------------------------------------------------------------------------*/

/* Close Window */
void closeWin()
{
	glXMakeCurrent(display, None, NULL);
	glXDestroyContext(display, glc);
	XDestroyWindow(display, win);
	XCloseDisplay(display);
	exit(0);
}

/*-------------------------------------------------------------------------*/

