#include <stdio.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "imbean.h"

#include <stdio.h>
#include <dlfcn.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "imbean.h"
#include "aux_module.h"

#define MAX_NUM_AUX_MODULES  32
#define MAX_LEN_CLASS_NAME   56

#define AUX_MODULES_CONFIG_FILE "aux_modules.xml"

AuxModuleRec aux_modules[MAX_NUM_AUX_MODULES] = {
	{ NULL, NULL, NULL, NULL},
};

int aux_manager_print()
{
#if DEBUG
	AuxModule aux_module;

	printf("aux_manger_print ====\n");
	for (aux_module = aux_modules; aux_module->class_name != NULL; aux_module ++) { 
		printf("classname: %s, aux_module: %p\n", aux_module->class_name, aux_module);
	}

	return (AUX_OK);
#endif
}


AuxModule aux_manager_get_module_by_classname(const char *classname)
{
	AuxModule aux_module;

	DEBUG_printf("aux_manager_get_module_by_classname: classname: %s\n", classname);

	for (aux_module = aux_modules; aux_module->class_name != NULL; aux_module ++) { 
		if (!strcasecmp(aux_module->class_name, classname))
			return (aux_module);
	}

	return NULL;
}

char *aux_manager_get_full_file_path(char *file_path)
{
	int len;
	char *full_file_path = NULL;

	if (!file_path || !*file_path)
		return (NULL);

	if (file_path[0] == '/') {
		len = strlen (file_path);

		full_file_path = (char *)calloc(len + 1, sizeof(char));
		if (full_file_path == NULL) return (NULL);

		snprintf(full_file_path, len + 1, "%s", file_path);
	} else {
		len = strlen(LE_AUX_MODULES_DIR) + 1 + strlen (file_path);

		full_file_path = (char *)calloc(len + 1, sizeof(char));
		if (full_file_path == NULL) return (NULL);

		snprintf(full_file_path, len + 1, "%s/%s", LE_AUX_MODULES_DIR, file_path);
	}

	return (full_file_path);
}

int aux_manager_get_custom_aux_modules(char *aux_modules_config_file)
{
#define AUX_MODULE_CLASS_NAME  "class_name"
#define AUX_MODULE_OBJECT_FILE "object_file"

	AuxModuleRec *p;
	int num_aux_modules;
	int num_aux_modules_custom;

	IbmlData *ibml_data;
	IbmlCatagory    *ibml_catagory;
	IbmlElement     *ibml_element;
	IbmlProperty    *ibml_property;
	int i, j, k, n;

	char *id, *scope;
	char *name, *value;

	num_aux_modules = 0;
	for (p=aux_modules; p->class_name; p++) num_aux_modules++;

	ibml_data = (IbmlData *)imbean_config_new_from_file(aux_modules_config_file);
	if (ibml_data == NULL) return(AUX_FAIL);

	num_aux_modules_custom = 0;
	for (k=0; k<ibml_data->num_catagories; k++) {
		ibml_catagory = ibml_data->catagories[k];

		if (ibml_catagory->scope == NULL) continue;
		if (! *ibml_catagory->scope) continue;
		if (strcasecmp(ibml_catagory->scope, "gtk2")) continue;

		for (i=0; i<ibml_catagory->num_elements; i++) {
			CARD16 *utfname_ptr;
			char *aux_module_class_name = NULL;
			char *aux_module_object_file = NULL;
			char *object_file_full_path = NULL;
			void *module_handler = NULL;
			AuxModuleMethodsRec *module_methods = NULL;

			ibml_element = ibml_catagory->elements[i];
			if (!ibml_element) continue;

			id = (char *)ibml_element->id;
			scope = (char *)ibml_element->scope;
			DEBUG_printf("id:%s, scope:%s\n", id?id:"NULL", scope?scope:"NULL");
			if (!id || !*id) continue;

			for(j=0; j<ibml_element->num_properties; j++) {
				ibml_property = ibml_element->properties[j];
				if (!ibml_property) continue;

				name = (char *)ibml_property->name;
				if (!name || !*name)
					continue;

				value = (char *)ibml_property->value;
				if (!value || !*value)
					continue;

				if (!strcasecmp(name, AUX_MODULE_CLASS_NAME)) {
					aux_module_class_name = value;
				} else if (!strcasecmp(name, AUX_MODULE_OBJECT_FILE)) {
					aux_module_object_file = value;
				}
			}

			if (!aux_module_class_name || !*aux_module_class_name)
				continue;
			if (!aux_module_object_file || !*aux_module_object_file)
				continue;

			if (num_aux_modules + num_aux_modules_custom >= MAX_NUM_AUX_MODULES-1)
				break;

			object_file_full_path = aux_manager_get_full_file_path(aux_module_object_file);
			if (!object_file_full_path || !*object_file_full_path)
				continue;

			module_handler = (void *)dlopen (object_file_full_path, RTLD_LAZY);
			DEBUG_printf("object_file_full_path: %s, module_handler: 0x%x\n",
				object_file_full_path, module_handler);

			if (module_handler == NULL) {
				fprintf(stderr, "Error: could not open module file: %s\n",
					object_file_full_path);
				free ((char *)object_file_full_path);
				continue;
			}

			module_methods = (AuxModuleMethodsRec *) dlsym(module_handler, "aux_module_methods");
			if (module_methods == NULL) {
				fprintf(stderr, "Error: could not dlsym AuxModuleMethodsRec: %s\n", object_file_full_path);
				free ((char *)object_file_full_path);
				dlclose(module_handler);
				continue;
			}

			free ((char *)object_file_full_path);

			p = &(aux_modules[num_aux_modules + num_aux_modules_custom]);
			p->file_name = (char *)strdup(aux_module_object_file);
			p->handler   = module_handler;
			p->class_name = (char *)strdup(aux_module_class_name);
			p->methods   = module_methods;

			num_aux_modules_custom ++;
		}
	}

	p = &(aux_modules[num_aux_modules + num_aux_modules_custom]);
	p->file_name = NULL;
	p->handler   = NULL;
	p->class_name = NULL;
	p->methods   = NULL;

	ibml_data_free(ibml_data);
	return (AUX_OK);
}

void aux_manager_init_aux_modules()
{
	char *aux_modules_config_file = NULL;

	aux_modules_config_file =  aux_manager_get_full_file_path(AUX_MODULES_CONFIG_FILE);

	if (aux_modules_config_file) {
		DEBUG_printf("aux_modules_config_file: %s\n", aux_modules_config_file);
		aux_manager_get_custom_aux_modules(aux_modules_config_file);
		free ((char *)aux_modules_config_file);
	}

	aux_manager_print();

	return;
}

void aux_manager_register_aux_modules(Display *display, Window window)
{
	AuxModule aux_module;
	Atom atom_aux_module;

	DEBUG_printf("aux_manager_register_aux_modules \n");
	for (aux_module = aux_modules; aux_module->class_name != NULL; aux_module ++) { 
		DEBUG_printf("classname: %s\n", aux_module->class_name);
		atom_aux_module = XInternAtom(display, aux_module->class_name, False);
		if (atom_aux_module == (Atom)None)
			continue;
		XSetSelectionOwner(display, atom_aux_module, window, CurrentTime);
	}

	return;
}

int aux_manager_destroy()
{
	AuxModule aux_module;

	for (aux_module = aux_modules; aux_module->handler != NULL; aux_module ++) { 
		if (aux_module->file_name) free ((char *)aux_module->file_name);
		if (aux_module->class_name) free ((char *)aux_module->class_name);
		if (aux_module->handler) dlclose(aux_module->handler);
	}

	return (AUX_OK);
}
