/* $Id$ */
/* Copyright (c) 2004-2020 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Graphics GToolkit */
/* All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */



#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "GToolkit/GWindow.h"
#include "common.h"


/* GToolkit */
/* private */
/* types */
typedef struct _GToolkit 
{
	int init;

	/* main loop */
	unsigned int loop;

	GWindow ** windows;
	size_t windows_cnt;

	/* not portable */
	Display * display;
	int screen;
	XVisualInfo * visual;
	Colormap colormap;
} GToolkit;


/* variables */
static GToolkit _gt;


/* prototypes */
/* accessors */
static GWindow * _gtoolkit_get_gwindow(Window window);

/* useful */
static int _gtoolkit_error(char const * message, int ret);


/* public */
/* functions */
/* accessors */
Colormap gtoolkit_get_colormap(void)
{
	return _gt.colormap;
}


/* gtoolkit_get_display */
Display * gtoolkit_get_display(void)
{
	return _gt.display;
}


/* gtoolkit_get_visual */
XVisualInfo * gtoolkit_get_visual(void)
{
	return _gt.visual;
}


/* useful */
/* gtoolkit_register_window */
void gtoolkit_deregister_window(GWindow * gwindow)
{
	size_t i;

	/* FIXME free memory if possible */
	for(i = 0; i < _gt.windows_cnt; i++)
		if(_gt.windows[i] == gwindow)
		{
			_gt.windows[i] = NULL;
			return;
		}
}


/* gtoolkit_init */
int gtoolkit_init(void)
{
	int attrd[] =
	{
		GLX_RGBA, GLX_DOUBLEBUFFER,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_DEPTH_SIZE, 16,
		None
	};
	int attrs[] =
	{
		GLX_RGBA,
		GLX_RED_SIZE, 4,
		GLX_GREEN_SIZE, 4,
		GLX_BLUE_SIZE, 4,
		GLX_DEPTH_SIZE, 16,
		None
	};

	if(_gt.init != 0)
		return 0;
	if((_gt.display = XOpenDisplay(NULL)) == NULL)
		return _gtoolkit_error("Could not open display", 1);
	_gt.screen = DefaultScreen(_gt.display);
	if((_gt.visual = glXChooseVisual(_gt.display, _gt.screen, attrd))
			== NULL)
		_gt.visual = glXChooseVisual(_gt.display, _gt.screen, attrs);
	if(_gt.visual == NULL)
		return _gtoolkit_error("Could not choose visual", 1);
	_gt.colormap = XCreateColormap(_gt.display,
			RootWindow(_gt.display, _gt.screen),
			_gt.visual->visual, AllocNone);
	_gt.loop = 0;
	_gt.init = 1;
	return 0;
}


/* gtoolkit_main */
static void _main_event(void);
static void _main_event_configure(XConfigureEvent * event);
static void _main_event_expose(XExposeEvent * event);

void gtoolkit_main(void)
{
	size_t i;

	for(_gt.loop++; _gt.loop >= 1;)
		while(XPending(_gt.display) > 0)
			_main_event();
	for(i = 0; i < _gt.windows_cnt; i++)
		if(_gt.windows[i] != NULL)
			gwindow_delete(_gt.windows[i]);
	XCloseDisplay(_gt.display);
	memset(&_gt, 0, sizeof(_gt));
}

static void _main_event(void)
{
	XEvent event;

	XNextEvent(_gt.display, &event);
	switch(event.type)
	{
		case ConfigureNotify:
			_main_event_configure(&event.xconfigure);
			break;
		case Expose:
			_main_event_expose(&event.xexpose);
			break;
		case ClientMessage:
		case KeyPress:
		case KeyRelease:
		default:
#ifdef DEBUG
			fprintf(stderr, "DEBUG: %s() Event %d\n", __func__,
					event.type);
#endif
			break;
	}
}

static void _main_event_configure(XConfigureEvent * event)
{
	GWindow * gwindow;

	if((gwindow = _gtoolkit_get_gwindow(event->window)) == NULL)
		return;
	gwindow_event_configure(gwindow, event);
}

static void _main_event_expose(XExposeEvent * event)
{
	GWindow * gwindow;

	if((gwindow = _gtoolkit_get_gwindow(event->window)) == NULL)
		return;
	gwindow_event_expose(gwindow, event);
}


/* gtoolkit_main_quit */
void gtoolkit_main_quit(void)
{
	if(_gt.init == 0)
		return;
	if(_gt.loop > 0)
		_gt.loop--;
}


/* gtoolkit_register_window */
void gtoolkit_register_window(GWindow * gwindow)
{
	GWindow ** p;

	/* FIXME look for an empty spot first */
	if((p = realloc(_gt.windows, sizeof(*p) * _gt.windows_cnt + 1)) == NULL)
		/* XXX ignore errors */
		return;
	_gt.windows = p;
	_gt.windows[_gt.windows_cnt++] = gwindow;
}


/* private */
/* accessors */
/* gtoolkit_get_gwindow */
static GWindow * _gtoolkit_get_gwindow(Window window)
{
	size_t i;

	for(i = 0; i < _gt.windows_cnt; i++)
		if(_gt.windows[i] != NULL
				&& gwindow_get_window(_gt.windows[i]) == window)
			return _gt.windows[i];
	/* XXX report error */
	return NULL;
}


/* useful */
/* gtoolkit_error */
static int _gtoolkit_error(char const * message, int ret)
{
	fprintf(stderr, "%s%s\n", "GToolkit: ", message);
	return ret;
}
