// -*- C++ -*-
/*
 * simple.cc:
 * Simple gtkglextmm example.
 *
 * written by Naofumi Yasufuku  <naofumi@users.sourceforge.net>
 */

#include <iostream>
#include <cstdlib>

#include <gtkmm.h>

#include <gtkglmm.h>

#ifdef G_OS_WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#endif

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


//
// OpenGL frame buffer configuration utilities.
//

struct GLConfigUtil
{
	static void print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
			const char* attrib_str,
			int attrib,
			bool is_boolean);

	static void examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig);
};

//
// Print a configuration attribute.
//
void GLConfigUtil::print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
		const char* attrib_str,
		int attrib,
		bool is_boolean)
{
	int value;

	if (glconfig->get_attrib(attrib, value))
	{
		std::cout << attrib_str << " = ";
		if (is_boolean)
			std::cout << (value == true ? "true" : "false") << std::endl;
		else
			std::cout << value << std::endl;
	}
	else
	{
		std::cout << "*** Cannot get "
			<< attrib_str
			<< " attribute value\n";
	}
}

//
// Print configuration attributes.
//
void GLConfigUtil::examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig)
{
	std::cout << "\nOpenGL visual configurations :\n\n";

	std::cout << "glconfig->is_rgba() = "
		<< (glconfig->is_rgba() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->is_double_buffered() = "
		<< (glconfig->is_double_buffered() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->is_stereo() = "
		<< (glconfig->is_stereo() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->has_alpha() = "
		<< (glconfig->has_alpha() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->has_depth_buffer() = "
		<< (glconfig->has_depth_buffer() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->has_stencil_buffer() = "
		<< (glconfig->has_stencil_buffer() ? "true" : "false")
		<< std::endl;
	std::cout << "glconfig->has_accum_buffer() = "
		<< (glconfig->has_accum_buffer() ? "true" : "false")
		<< std::endl;

	std::cout << std::endl;

	print_gl_attrib(glconfig, "Gdk::GL::USE_GL",           Gdk::GL::USE_GL,           true);
	print_gl_attrib(glconfig, "Gdk::GL::BUFFER_SIZE",      Gdk::GL::BUFFER_SIZE,      false);
	print_gl_attrib(glconfig, "Gdk::GL::LEVEL",            Gdk::GL::LEVEL,            false);
	print_gl_attrib(glconfig, "Gdk::GL::RGBA",             Gdk::GL::RGBA,             true);
	print_gl_attrib(glconfig, "Gdk::GL::DOUBLEBUFFER",     Gdk::GL::DOUBLEBUFFER,     true);
	print_gl_attrib(glconfig, "Gdk::GL::STEREO",           Gdk::GL::STEREO,           true);
	print_gl_attrib(glconfig, "Gdk::GL::AUX_BUFFERS",      Gdk::GL::AUX_BUFFERS,      false);
	print_gl_attrib(glconfig, "Gdk::GL::RED_SIZE",         Gdk::GL::RED_SIZE,         false);
	print_gl_attrib(glconfig, "Gdk::GL::GREEN_SIZE",       Gdk::GL::GREEN_SIZE,       false);
	print_gl_attrib(glconfig, "Gdk::GL::BLUE_SIZE",        Gdk::GL::BLUE_SIZE,        false);
	print_gl_attrib(glconfig, "Gdk::GL::ALPHA_SIZE",       Gdk::GL::ALPHA_SIZE,       false);
	print_gl_attrib(glconfig, "Gdk::GL::DEPTH_SIZE",       Gdk::GL::DEPTH_SIZE,       false);
	print_gl_attrib(glconfig, "Gdk::GL::STENCIL_SIZE",     Gdk::GL::STENCIL_SIZE,     false);
	print_gl_attrib(glconfig, "Gdk::GL::ACCUM_RED_SIZE",   Gdk::GL::ACCUM_RED_SIZE,   false);
	print_gl_attrib(glconfig, "Gdk::GL::ACCUM_GREEN_SIZE", Gdk::GL::ACCUM_GREEN_SIZE, false);
	print_gl_attrib(glconfig, "Gdk::GL::ACCUM_BLUE_SIZE",  Gdk::GL::ACCUM_BLUE_SIZE,  false);
	print_gl_attrib(glconfig, "Gdk::GL::ACCUM_ALPHA_SIZE", Gdk::GL::ACCUM_ALPHA_SIZE, false);

	std::cout << std::endl;
}


//
// Simple OpenGL scene.
//

class SimpleGLScene : public Gtk::DrawingArea,
	public Gtk::GL::Widget<SimpleGLScene>
{
	public:
		SimpleGLScene();
		virtual ~SimpleGLScene();

	protected:
		virtual void on_realize();
		virtual bool on_configure_event(GdkEventConfigure* event);
		virtual bool on_expose_event(GdkEventExpose* event);

};

SimpleGLScene::SimpleGLScene()
{
	//
	// Configure OpenGL-capable visual.
	//

	Glib::RefPtr<Gdk::GL::Config> glconfig;

	// Try double-buffered visual
	glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB    |
			Gdk::GL::MODE_DEPTH  |
			Gdk::GL::MODE_DOUBLE);
	if (!glconfig)
	{
		std::cerr << "*** Cannot find the double-buffered visual.\n"
			<< "*** Trying single-buffered visual.\n";

		// Try single-buffered visual
		glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB   |
				Gdk::GL::MODE_DEPTH);
		if (!glconfig)
		{
			std::cerr << "*** Cannot find any OpenGL-capable visual.\n";
			std::exit(1);
		}
	}

	// print frame buffer attributes.
	GLConfigUtil::examine_gl_attrib(glconfig);

	//
	// Set OpenGL-capability to the widget.
	//

	set_gl_capability(glconfig);
}

SimpleGLScene::~SimpleGLScene()
{
}

void SimpleGLScene::on_realize()
{
	// We need to call the base on_realize()
	Gtk::DrawingArea::on_realize();

	//
	// Get GL::Window.
	//

	Glib::RefPtr<Gdk::GL::Window> glwindow = get_gl_window();

	//
	// GL calls.
	//

	// *** OpenGL BEGIN ***
	if (!glwindow->gl_begin(get_gl_context()))
		return;

	GLUquadricObj* qobj = gluNewQuadric();
	gluQuadricDrawStyle(qobj, GLU_FILL);
	glNewList(1, GL_COMPILE);
	gluSphere(qobj, 1.0, 20, 20);
	glEndList();

	static GLfloat light_diffuse[] = {1.0, 0.0, 0.0, 1.0};
	static GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);

	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClearDepth(1.0);

	glViewport(0, 0, get_width(), get_height());

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(40.0, 1.0, 1.0, 10.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 0.0, 3.0,
			0.0, 0.0, 0.0,
			0.0, 1.0, 0.0);
	glTranslatef(0.0, 0.0, -3.0);

	glwindow->gl_end();
	// *** OpenGL END ***
}

bool SimpleGLScene::on_configure_event(GdkEventConfigure* event)
{
	//
	// Get GL::Window.
	//

	Glib::RefPtr<Gdk::GL::Window> glwindow = get_gl_window();

	//
	// GL calls.
	//

	// *** OpenGL BEGIN ***
	if (!glwindow->gl_begin(get_gl_context()))
		return false;

	glViewport(0, 0, get_width(), get_height());

	glwindow->gl_end();
	// *** OpenGL END ***

	return true;
}

bool SimpleGLScene::on_expose_event(GdkEventExpose* event)
{
	//
	// Get GL::Window.
	//

	Glib::RefPtr<Gdk::GL::Window> glwindow = get_gl_window();

	//
	// GL calls.
	//

	// *** OpenGL BEGIN ***
	if (!glwindow->gl_begin(get_gl_context()))
		return false;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glCallList(1);

	// Swap buffers.
	if (glwindow->is_double_buffered())
		glwindow->swap_buffers();
	else
		glFlush();

	glwindow->gl_end();
	// *** OpenGL END ***

	return true;
}


//
// The application class.
//

class Simple : public Gtk::Window
{
	public:
		Simple();
		virtual ~Simple();

	protected:
		// signal handlers:
		void on_button_quit_clicked();

	protected:
		// member widgets:
		Gtk::VBox m_VBox;
		SimpleGLScene m_SimpleGLScene;
		Gtk::Button m_ButtonQuit;
};

Simple::Simple()
	: m_VBox(false, 0), m_ButtonQuit("Quit")
{
	//
	// Top-level window.
	//

	set_title("Simple");

	// Get automatically redrawn if any of their children changed allocation.
	set_reallocate_redraws(true);

	add(m_VBox);

	//
	// Simple OpenGL scene.
	//

	m_SimpleGLScene.set_size_request(200, 200);

	m_VBox.pack_start(m_SimpleGLScene);

	//
	// Simple quit button.
	//

	m_ButtonQuit.signal_clicked().connect(
			sigc::mem_fun(*this, &Simple::on_button_quit_clicked));

	m_VBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK, 0);

	//
	// Show window.
	//

	show_all();
}

Simple::~Simple()
{}

void Simple::on_button_quit_clicked()
{
	Gtk::Main::quit();
}


//
// Main.
//

int main(int argc, char** argv)
{
	Gtk::Main kit(argc, argv);

	//
	// Init gtkglextmm.
	//

	Gtk::GL::init(argc, argv);

	//
	// Query OpenGL extension version.
	//

	int major, minor;
	Gdk::GL::query_version(major, minor);
	std::cout << "OpenGL extension version - "
		<< major << "." << minor << std::endl;

	//
	// Instantiate and run the application.
	//

	Simple simple;

	kit.run(simple);

	return 0;
}