/*
 * Copyright (C) 2023 iopsys Software Solutions AB
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 *	  Author Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
 *
 */

#include "dotso_plugin.h"
#include "../dmplugin.h"

LIST_HEAD(loaded_library_list);

struct loaded_library
{
	struct list_head list;
	void *library;
};

static void add_list_loaded_libraries(struct list_head *library_list, void *library)
{
	struct loaded_library *lib = NULL;

	lib = (struct loaded_library *)calloc(1, sizeof(struct loaded_library));
	if (!lib)
		return;

	list_add_tail(&lib->list, library_list);
	lib->library = library;
}

static void free_all_list_open_library(struct list_head *library_list)
{
	struct loaded_library *lib = NULL, *tmp = NULL;

	list_for_each_entry_safe(lib, tmp, library_list, list) {
		list_del(&lib->list);

		if (lib->library) {
			dlclose(lib->library);
			lib->library = NULL;
		}

		FREE(lib);
	}
}

static void dotso_plugin_disable_requested_entries(DMOBJ *entryobj, DMOBJ *requested_obj, DMLEAF *requested_leaf, const char *parent_obj, const char *plugin_path)
{
	if (!entryobj)
		return;

	for (; (requested_obj && requested_obj->obj); requested_obj++)
		disable_entry_obj(entryobj, requested_obj->obj, parent_obj, plugin_path);

	for (; (requested_leaf && requested_leaf->parameter); requested_leaf++)
		disable_entry_leaf(entryobj, requested_leaf->parameter, parent_obj, plugin_path);
}

int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
{
	void *handle = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL);
	if (!handle) {
		char *err_msg = dlerror();
		BBF_ERR("Failed to add DotSo plugin '%s', [%s]\n", plugin_path, err_msg);
		return 0;
	}

	//Load Dynamic Object handler
	DM_MAP_OBJ *dynamic_obj = NULL;
	*(void **) (&dynamic_obj) = dlsym(handle, "tDynamicObj");

	if (dynamic_obj == NULL) {
		dlclose(handle);
		BBF_ERR("Plugin %s missing init symbol ...", plugin_path);
		return 0;
	}

	for (int i = 0; dynamic_obj[i].path; i++) {

		DMOBJ *dm_entryobj = find_entry_obj(entryobj, dynamic_obj[i].path);
		if (!dm_entryobj) {
			BBF_ERR("Failed to add DotSo plugin '%s' to main tree with parent DM index '%d' => '%s'", plugin_path, i, dynamic_obj[i].path);
			continue;
		}

		dotso_plugin_disable_requested_entries(dm_entryobj, dynamic_obj[i].root_obj, dynamic_obj[i].root_leaf, dynamic_obj[i].path, plugin_path);

		if (dynamic_obj[i].root_obj) {

			if (dm_entryobj->nextdynamicobj == NULL) {
				dm_entryobj->nextdynamicobj = (struct dm_dynamic_obj *)calloc(__INDX_DYNAMIC_MAX, sizeof(struct dm_dynamic_obj));
				dm_entryobj->nextdynamicobj[INDX_JSON_MOUNT].idx_type = INDX_JSON_MOUNT;
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].idx_type = INDX_LIBRARY_MOUNT;
			}

			if (dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj == NULL) {
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj = (DMOBJ **)calloc(2, sizeof(DMOBJ *));
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj[0] = dynamic_obj[i].root_obj;
			} else {
				int idx = get_obj_idx(dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj);
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj = (DMOBJ **)realloc(dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj, (idx + 2) * sizeof(DMOBJ *));
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj[idx] = dynamic_obj[i].root_obj;
				dm_entryobj->nextdynamicobj[INDX_LIBRARY_MOUNT].nextobj[idx+1] = NULL;
			}

		}

		if (dynamic_obj[i].root_leaf) {

			if (dm_entryobj->dynamicleaf == NULL) {
				dm_entryobj->dynamicleaf = (struct dm_dynamic_leaf *)calloc(__INDX_DYNAMIC_MAX, sizeof(struct dm_dynamic_leaf));
				dm_entryobj->dynamicleaf[INDX_JSON_MOUNT].idx_type = INDX_JSON_MOUNT;
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].idx_type = INDX_LIBRARY_MOUNT;
			}

			if (dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf == NULL) {
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf = (DMLEAF **)calloc(2, sizeof(DMLEAF *));
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf[0] = dynamic_obj[i].root_leaf;
			} else {
				int idx = get_leaf_idx(dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf);
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf = (DMLEAF **)realloc(dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf, (idx + 2) * sizeof(DMLEAF *));
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf[idx] = dynamic_obj[i].root_leaf;
				dm_entryobj->dynamicleaf[INDX_LIBRARY_MOUNT].nextleaf[idx+1] = NULL;
			}

		}

		if (dynamic_obj[i].init_module)
			dynamic_obj[i].init_module(NULL);
	}
	add_list_loaded_libraries(&loaded_library_list, handle);

	return 0;
}

int free_dotso_plugins(void)
{
	free_all_list_open_library(&loaded_library_list);
	return 0;
}
