// SDPGL
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\author Timothy M. Shead (tshead@k-3d.com)
*/

#include "info.h"
#include "platform.h"

#if defined SDPGL_WIN32
#include <windows.h>
#else // SDPGL_WIN32
#include <GL/glx.h>
#endif // !SDPGL_WIN32

#include <GL/gl.h>
#include <GL/glu.h>

#include <cassert>
#include <iostream>
#include <sstream>
#include <string>

namespace
{

#if defined SDPGL_POSIX

class visual_list
{
public:
	visual_list(Display* XDisplay, int XScreen)
	{
		// Sanity checks ...
		assert(XDisplay);

		m_display = XDisplay;
		m_screen = XScreen;

		XVisualInfo match;
		match.screen = XScreen;

		m_visuals = XGetVisualInfo(XDisplay, VisualScreenMask, &match, &m_visual_count);
	}

	~visual_list()
	{
		if(m_visuals)
			XFree(m_visuals);

		m_display = 0;
		m_screen = 0;
		m_visuals = 0;
		m_visual_count = 0;
	}

	unsigned long count()
	{
		// Sanity checks ...
		assert(m_visual_count >= 0);
		return static_cast<unsigned long>(m_visual_count);
	}

	XVisualInfo* operator[](unsigned long Index)
	{
		// Sanity checks ...
		assert(Index < count());
		return m_visuals+Index;
	}

	XVisualInfo* GetGLXCapableVisual()
	{
		// Sanity checks ...
		assert(m_display);
		assert(m_visuals);
		assert(m_visual_count);

		for(XVisualInfo* visual = m_visuals; visual < m_visuals + m_visual_count; visual++)
			{
				int glxcapable = 0;
				glXGetConfig(m_display, visual, GLX_USE_GL, &glxcapable);

				if(glxcapable)
					return visual;
			}

		return 0;
	}

private:
	Display* m_display;
	int m_screen;
	XVisualInfo* m_visuals;
	int m_visual_count;
};

void put_list(std::ostream& Stream, const char* const String)
{
	// Sanity checks ...
	assert(Stream.good());

	// If the string is empty, we're done ...
	if(!String)
		return;

	if(!strlen(String))
		return;

	// Parse the string into pieces ...
	std::istringstream instring(String);
	std::string substring;
	for(instring >> substring; !instring.eof(); instring >> substring)
		Stream << "\t" << substring << std::endl;
}

void put_list(std::ostream& Stream, const GLubyte* String)
{
	put_list(Stream, reinterpret_cast<const char*>(String));
}

const std::string class_of(int XVisualClass)
{
	switch(XVisualClass)
		{
			case StaticGray:
				return "sg";
			case GrayScale:
				return "gs";
			case StaticColor:
				return "sc";
			case PseudoColor:
				return "pc";
			case TrueColor:
				return "tc";
			case DirectColor:
				return "dc";
			default:
				return "??";
		}
}

#endif // SDPGL_POSIX

} // namespace

namespace sdpgl
{

#if defined SDPGL_POSIX

bool exists(std::ostream& Stream)
{
	// Get the X display ...
	Display* display = XOpenDisplay((const char*)NULL);
	if(!display)
		{
			Stream << "*** Could not connect to " << XDisplayName((const char*)NULL) << std::endl;
			return false;
		}

	// Get the X screen ...
	int screen = DefaultScreen(display);

	// Make sure the X server supports GLX ...
	if(!glXQueryExtension(display, (int*)NULL, (int*)NULL))
		{
			Stream << "*** GLX extensions not supported by X server" << std::endl;
			return false;
		}

	// Try loading the available visuals for this display & screen ...
	visual_list visuallist(display, screen);
	if(0 == visuallist.count())
		{
			Stream << "*** Unable to load any X visuals" << std::endl;
			return false;
		}

	// See if any are GLX capable ...
	if(0 == visuallist.GetGLXCapableVisual())
		{
			Stream << "No GLX capable visual found" << std::endl;
			return false;
		}

	return true;
}

void implementation(std::ostream& Stream)
{
	// Get the X display ...
	Display* display = XOpenDisplay((const char*)NULL);
	if(!display)
		{
			Stream << "Could not open display" << std::endl;
			return;
		}

	// Get the X screen ...
	int screen = DefaultScreen(display);

	// Make sure the X server supports GLX ...
	if(!glXQueryExtension(display, (int*)NULL, (int*)NULL))
		{
			Stream << "Display does not support GLX" << std::endl;
			return;
		}

	// GLX server details ...
	Stream << "GLX Server Vendor: " << glXQueryServerString(display, screen, GLX_VENDOR) << std::endl;
	Stream << "GLX Server Version: " << glXQueryServerString(display, screen, GLX_VERSION) << std::endl;
	Stream << "GLX Server Extensions: " << std::endl;
	put_list(Stream, glXQueryServerString(display, screen, GLX_EXTENSIONS));
	Stream << std::endl;

	// GLX client details ...
	Stream << "GLX Client Vendor: " << glXGetClientString(display, GLX_VENDOR) << std::endl;
	Stream << "GLX Client Version: " << glXGetClientString(display, GLX_VERSION) << std::endl;
	Stream << "GLX Client Extensions: " << std::endl;
	put_list(Stream, glXGetClientString(display, GLX_EXTENSIONS));
	Stream << std::endl;

	// Load available visuals ...
	visual_list visuallist(display, screen);
	if(!visuallist.count())
		{
			Stream << "No visuals found" << std::endl;
			return;
		}

	// Get a GLX capable visual ...
	XVisualInfo* glxcapablevisual = visuallist.GetGLXCapableVisual();
	if(!glxcapablevisual)
		{
			Stream << "Could not find a GLX-capable visual" << std::endl;
			return;
		}

	// Create an OpenGL context, so we can grab some info about the implementation ...
	GLXContext context = glXCreateContext(display, glxcapablevisual, 0, GL_TRUE);
	Colormap colormap = XCreateColormap(display, RootWindow(display, glxcapablevisual->screen), glxcapablevisual->visual, AllocNone);
	XSetWindowAttributes attributes;
	attributes.colormap = colormap;
	attributes.border_pixel = 0;
	Window window = XCreateWindow(display, RootWindow(display, glxcapablevisual->screen), 0, 0, 100, 100, 0, glxcapablevisual->depth, InputOutput, glxcapablevisual->visual, CWBorderPixel | CWColormap, &attributes);
	glXMakeCurrent(display, window, context);

	// OpenGL details ...
	Stream << "OpenGL Vendor: " << glGetString(GL_VENDOR) << std::endl;
	Stream << "OpenGL Renderer: " << glGetString(GL_RENDERER) << std::endl;
	Stream << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
	Stream << "OpenGL Extensions: " << std::endl;
	put_list(Stream, glGetString(GL_EXTENSIONS));
	Stream << std::endl;

	// GLU details ...
	Stream << "GLU Version: " << gluGetString(GLenum(GLU_VERSION)) << std::endl;
	Stream << "GLU Extensions: " << std::endl;
	put_list(Stream, gluGetString(GLenum(GLU_EXTENSIONS)));
	Stream << std::endl;

	// DRI details ...
	if(glXIsDirect(display, context))
		Stream << "Direct Rendering: supported" << std::endl;
	else
		Stream << "Direct Rendering: not supported" << std::endl;
	Stream << std::endl;

	// Create a listing of available GLX visuals ...
	Stream << "--------------------------------------------------------------------------------------------------------------" << std::endl;
	Stream << "id depth class buffer level rgba double stereo aux red green blue alpha depth stencil ared agreen ablue aalpha" << std::endl;
	Stream << "--------------------------------------------------------------------------------------------------------------" << std::endl;

	// For each available visual ...
	for(unsigned long i = 0; i < visuallist.count(); i++)
		{
			XVisualInfo* visual = visuallist[i];

			int glxcapable;
			glXGetConfig(display, visual, GLX_USE_GL, &glxcapable);

			// Skip non-GLX-capable visuals ...
			if(!glxcapable)
				continue;

			// Display visual information ...
			Stream << visual->visualid << "   " << visual->depth << "    " << class_of(visual->c_class) << "    ";

			int value;

			glXGetConfig(display, visual, GLX_BUFFER_SIZE, &value);
			Stream << value << "     ";

			glXGetConfig(display, visual, GLX_LEVEL, &value);
			Stream << value << "    ";

			glXGetConfig(display, visual, GLX_RGBA, &value);
			Stream << (value ? "Y" : " ") << "     ";

			glXGetConfig(display, visual, GLX_DOUBLEBUFFER, &value);
			Stream << (value ? "Y" : " ") << "      ";

			glXGetConfig(display, visual, GLX_STEREO, &value);
			Stream << (value ? "Y" : " ") << "     ";

			glXGetConfig(display, visual, GLX_AUX_BUFFERS, &value);
			Stream << value << "   ";

			glXGetConfig(display, visual, GLX_RED_SIZE, &value);
			Stream << value << "    ";

			glXGetConfig(display, visual, GLX_GREEN_SIZE, &value);
			Stream << value << "    ";

			glXGetConfig(display, visual, GLX_BLUE_SIZE, &value);
			Stream << value << "     ";

			glXGetConfig(display, visual, GLX_ALPHA_SIZE, &value);
			Stream << value << "    ";

			glXGetConfig(display, visual, GLX_DEPTH_SIZE, &value);
			Stream << value << "      ";

			glXGetConfig(display, visual, GLX_STENCIL_SIZE, &value);
			Stream << value << "     ";

			glXGetConfig(display, visual, GLX_ACCUM_RED_SIZE, &value);
			Stream << value << "    ";

			glXGetConfig(display, visual, GLX_ACCUM_GREEN_SIZE, &value);
			Stream << value << "     ";

			glXGetConfig(display, visual, GLX_ACCUM_BLUE_SIZE, &value);
			Stream << value << "     ";

			glXGetConfig(display, visual, GLX_ACCUM_ALPHA_SIZE, &value);
			Stream << value << std::endl;
		}

	Stream << std::endl;
}

#elif defined SDPGL_WIN32

bool exists(std::ostream& Stream)
{
	// There's really nothing to do, here.  If the user doesn't have
	// opengl32.dll and glu32.dll, we'll never get this far.
	return true;
}

void configuration(std::ostream& Stream)
{
}

#else // SDPGL_WIN32

#error You need to implement the exists() and configuration() functions for your platform, here!

#endif // Unknown platform

} // namespace sdpgl


