/* drivers.c
 * (c) 2002 Mikulas Patocka
 * This file is a part of the Links program, released under GPL
 */

#include "cfg.h"

#ifdef G

#include "links.h"

int F = 0;

struct graphics_driver *drv = NULL;

#ifdef GRDRV_X
extern struct graphics_driver x_driver;
#endif
#ifdef GRDRV_SVGALIB
extern struct graphics_driver svga_driver;
#endif
#ifdef GRDRV_FB
extern struct graphics_driver fb_driver;
#endif
#ifdef GRDRV_DIRECTFB
extern struct graphics_driver directfb_driver;
#endif
#ifdef GRDRV_PMSHELL
extern struct graphics_driver pmshell_driver;
#endif
#ifdef GRDRV_ATHEOS
extern struct graphics_driver atheos_driver;
#endif
#ifdef GRDRV_SDL
extern struct graphics_driver sdl_driver;
#endif

struct graphics_driver *graphics_drivers[] = {
#ifdef GRDRV_PMSHELL
	&pmshell_driver,
#endif
#ifdef GRDRV_ATHEOS
	&atheos_driver,
#endif
#ifdef GRDRV_X
	&x_driver,
#endif
#ifdef GRDRV_DIRECTFB
	&directfb_driver,
#endif
#ifdef GRDRV_FB
	&fb_driver,
#endif
#ifdef GRDRV_SVGALIB
	&svga_driver,
#endif
#ifdef GRDRV_SDL
	&sdl_driver,
#endif
	NULL
};

/* prototypes */
unsigned char *init_graphics_driver(struct graphics_driver *, unsigned char *, unsigned char *);
unsigned char *list_graphics_drivers(void);

int dummy_block(struct graphics_device *dev)
{
	return 0;
}

void dummy_unblock(struct graphics_device *dev)
{
}

unsigned char *list_graphics_drivers(void)
{ 
	unsigned char *d = init_str();
	int l = 0;
	struct graphics_driver **gd;
	for (gd = graphics_drivers; *gd; gd++) {
		if (l) add_to_str(&d, &l, " ");
		add_to_str(&d, &l, (*gd)->name);
	}
	return d;
}

/* Driver je jednorazovy argument, kterej se preda grafickymu driveru, nikde se dal
 * neuklada.  Param se skladuje v default_driver param a uklada se do konfiguraku. Pred
 * ukoncenim grafickeho driveru se nastavi default_driver_param podle
 * drv->get_driver_param.
 */
unsigned char *init_graphics_driver(struct graphics_driver *gd, unsigned char *param, unsigned char *display)
{
	unsigned char *r;
	unsigned char *p = param;
	struct driver_param *dp=get_driver_param(gd->name);
	if (!param || !*param) p = dp->param;
	gd->codepage=dp->codepage;
	gd->shell=mem_calloc(MAX_STR_LEN);
	if (dp->shell) strncpy(gd->shell,dp->shell,MAX_STR_LEN);
	gd->shell[MAX_STR_LEN-1]=0;
	r = gd->init_driver(p,display);
	if (r) mem_free(gd->shell), gd->shell = NULL;
	return r;
}

unsigned char *init_graphics(unsigned char *driver, unsigned char *param, unsigned char *display)
{
	unsigned char *s = init_str();
	int l = 0;
	struct graphics_driver **gd;
	for (gd = graphics_drivers; *gd; gd++) {
		if (!driver || !*driver || !strcasecmp((*gd)->name, driver)) {
			unsigned char *r;
			if ((!driver || !*driver) && (*gd)->flags & GD_NOAUTO) continue;
			if (!(r = init_graphics_driver(*gd, param, display))) {
				mem_free(s);
				drv = *gd;
				F = 1;
				return NULL;
			}
			if (!l) {
				if (!driver || !*driver) add_to_str(&s, &l, "Could not initialize any graphics driver. Tried the following drivers:\n");
				else add_to_str(&s, &l, "Could not initialize graphics driver ");
			}
			add_to_str(&s, &l, (*gd)->name);
			add_to_str(&s, &l, ":\n");
			add_to_str(&s, &l, r);
			mem_free(r);
		}
	}
	if (!l) {
		add_to_str(&s, &l, "Unknown graphics driver ");
		if (driver) add_to_str(&s, &l, driver);
		add_to_str(&s, &l, ".\nThe following graphics drivers are supported:\n");
		for (gd = graphics_drivers; *gd; gd++) {
			if (gd != graphics_drivers) add_to_str(&s, &l, ", ");
			add_to_str(&s, &l, (*gd)->name);
		}
		add_to_str(&s, &l, "\n");
	}
	return s;
}

void shutdown_graphics(void)
{
	if (drv)
	{
		if (drv->shell) mem_free(drv->shell);
		drv->shutdown_driver();
	}
}

void update_driver_param(void)
{
	if (drv)
	{
		struct driver_param *dp=get_driver_param(drv->name);
		dp->codepage=drv->codepage;
		if (dp->param) mem_free(dp->param);
		dp->param=stracpy(drv->get_driver_param());
		if (dp->shell) mem_free(dp->shell);
		dp->shell=stracpy(drv->shell);
		dp->nosave = 0;
	}
}

#if defined(GRDRV_SVGALIB)|defined(GRDRV_FB)

struct graphics_driver *virtual_device_driver;
struct graphics_device **virtual_devices;
int n_virtual_devices = 0;
struct graphics_device *current_virtual_device;

int virtual_device_timer;

int init_virtual_devices(struct graphics_driver *drv, int n)
{
	if (n_virtual_devices) {
		internal("init_virtual_devices: already initialized");
		return -1;
	}
	if ((unsigned)n > MAXINT / sizeof(struct graphics_device *)) overalloc();
	virtual_devices = mem_calloc(n * sizeof(struct graphics_device *));
	n_virtual_devices = n;
	virtual_device_driver = drv;
	virtual_device_timer = -1;
	current_virtual_device = NULL;
	return 0;
}

struct graphics_device *init_virtual_device()
{
	int i;
	for (i = 0; i < n_virtual_devices; i++) if (!virtual_devices[i]) {
		struct graphics_device *dev;
		dev = mem_calloc(sizeof(struct graphics_device));
		dev->drv = virtual_device_driver;
		dev->size.x2 = virtual_device_driver->x;
		dev->size.y2 = virtual_device_driver->y;
		current_virtual_device = virtual_devices[i] = dev;
		virtual_device_driver->set_clip_area(dev, &dev->size);
		return dev;
	}
	return NULL;
}

static void virtual_device_timer_fn(void *p)
{
	virtual_device_timer = -1;
	if (current_virtual_device && current_virtual_device->redraw_handler) {
		drv->set_clip_area(current_virtual_device, &current_virtual_device->size);
		current_virtual_device->redraw_handler(current_virtual_device, &current_virtual_device->size);
	}
}

void switch_virtual_device(int i)
{
	if (i == VD_NEXT) {
		int j;
		int t = 0;
		for (j = 0; j < n_virtual_devices * 2; j++)
			if (virtual_devices[j % n_virtual_devices] == current_virtual_device) t = 1;
			else if (virtual_devices[j % n_virtual_devices] && t) {
				current_virtual_device = virtual_devices[j % n_virtual_devices];
				goto ok_switch;
			}
		return;
	}
	if (i < 0 || i >= n_virtual_devices || !virtual_devices[i]) return;
	current_virtual_device = virtual_devices[i];
	ok_switch:
	if (virtual_device_timer == -1)
		virtual_device_timer = install_timer(0, virtual_device_timer_fn, NULL);
}

void shutdown_virtual_device(struct graphics_device *dev)
{
	int i;
	for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i] == dev) {
		virtual_devices[i] = NULL;
		mem_free(dev);
		if (current_virtual_device != dev) return;
		for (; i < n_virtual_devices; i++) if (virtual_devices[i]) {
			switch_virtual_device(i);
			return;
		}
		for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) {
			switch_virtual_device(i);
			return;
		}
		current_virtual_device = NULL;
		return;
	}
	mem_free(dev);
	/*internal("shutdown_virtual_device: device not initialized");*/
}

void shutdown_virtual_devices(void)
{
	int i;
	if (!n_virtual_devices) {
		internal("shutdown_virtual_devices: already shut down");
		return;
	}
	for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) internal("shutdown_virtual_devices: virtual device %d is still active", i);
	mem_free(virtual_devices);
	n_virtual_devices = 0;
	if (virtual_device_timer != -1) kill_timer(virtual_device_timer), virtual_device_timer = -1;
}

#endif

#endif
