/*
 * common.c - common code for datamodel libraries
 *
 * Copyright (C) 2024, IOPSYS Software Solutions AB.
 *
 * Author Suvendhu Hansa <suvendhu.hansa@iopsys.eu>
 *
 * See LICENSE file for license related information.
 *
 */

#include "common.h"

void remove_next_line(char *line)
{
	if (line == NULL)
		return;

	int len = strlen(line);

	if (line[len - 1] == '\n') {
		line[len - 1] = '\0';
	}
}

void remove_whitespaces(char *str)
{
	if (str == NULL)
		return;

	/* First remove leading spaces */
	const char* first_valid = str;

	while(*first_valid != '\0' && *first_valid == ' ') {
		++first_valid;
	}

	size_t len = strlen(first_valid) + 1;

	memmove(str, first_valid, len);

	/* Now remove trailing strip-char */
	char* end_str = str + strlen(str) - 1;

	while (str < end_str  && *end_str == ' ') {
		*end_str = '\0';
		--end_str ;
	}
}

void exec_cmd(const char *cmd, struct list_head *result_list)
{
	if (cmd == NULL)
		return;

	// flawfinder: ignore
	FILE *pp = popen(cmd, "r");
	if (pp != NULL) {
		char line[MAX_CMD_LEN] = {0};

		if (result_list != NULL) {
			while(fgets(line, sizeof(line), pp) != NULL) {
				// strip newline from end
				remove_next_line(line);

				// add to result
				struct cmd_result *node = (struct cmd_result *)malloc(sizeof(struct cmd_result));
				if (!node)
					break;

				memset(node, 0, sizeof(struct cmd_result));
				snprintf(node->output, sizeof(node->output), "%s", line);

				INIT_LIST_HEAD(&node->list);
				list_add_tail(&node->list, result_list);

				memset(line, 0, sizeof(line));
			}
		}
		pclose(pp);
	}
}

void collect_value(const char *path, struct list_head *result_list, char **value)
{
	struct cmd_result *res;

	if (result_list == NULL || value == NULL)
		return;

	list_for_each_entry(res, result_list, list) {
		char *param = NULL, *val = NULL;
		char tmp[MAX_CMD_LEN] = {0};


		strncpy(tmp, res->output, MAX_CMD_LEN);
		param = tmp;
		val = strstr(tmp, "=>");
		if (!val)
			continue;

		*val = '\0';
		val = val + 2; // move after =>

		if (param && val) {
			// remove whitespaces
			remove_whitespaces(param);
			remove_whitespaces(val);

			if (strcmp(path, param) == 0) {
				dmasprintf(value, "%s", strlen(val) ? val : "");
				break;
			}
		}
	}

	return;
}

void collect_instance(const char *path, struct list_head *result_list, char **value)
{
	struct cmd_result *res = NULL;
	int path_len = DM_STRLEN(path);

	if (result_list == NULL || value == NULL || path_len == 0)
		return;

	list_for_each_entry(res, result_list, list) {
		char *param = NULL;
		char tmp[MAX_CMD_LEN] = {0};


		strncpy(tmp, res->output, MAX_CMD_LEN);
		param = strstr(tmp, "Added");
		if (!param)
			continue;

		param = param + 6; // move to the DM path

		// remove whitespaces
		remove_whitespaces(param);

		int new_ob_len = DM_STRLEN(param);
		if ((new_ob_len > 0) && (new_ob_len > path_len)) {
			if (strncmp(path, param, path_len) == 0) {
				param = param + path_len;
				char *val = strchr(param, '.');
				if ((val != NULL) || (strlen(param) == 0))
					continue;

				dmasprintf(value, "%s", param);
				break;
			}
		}
	}

	return;
}

void delete_cmd_result(struct list_head *result_list)
{
	struct cmd_result *iter, *node;

	list_for_each_entry_safe(iter, node, result_list, list) {
		list_del(&iter->list);
		free(iter);
	}
}

void create_json_format(json_object *root, char *path_ob[], int max_ob, int ix, char *val)
{
	int arr_index = 0;

	if (root == NULL || val == NULL)
		return;

	if (ix == max_ob) {
		json_object_object_add(root, path_ob[ix], json_object_new_string(val));
		return;
	}

	arr_index = strtol(path_ob[ix+1], NULL, 10);
	if (arr_index != 0) {
		// array object
		json_object *arr = NULL;
		json_object *curr = NULL;

		json_object_object_get_ex(root, path_ob[ix], &arr);
		if (arr == NULL) {
			arr = json_object_new_array();
			json_object_object_add(root, path_ob[ix], arr);

			curr = json_object_new_object();
			json_object_array_put_idx(arr, arr_index - 1, curr);

			create_json_format(curr, path_ob, max_ob, ix+2, val);
		} else {
			// find the indx obj
			curr = json_object_array_get_idx(arr, arr_index-1);
			if (curr == NULL) {
				curr = json_object_new_object();
				json_object_array_put_idx(arr, arr_index - 1, curr);
			}

			create_json_format(curr, path_ob, max_ob, ix+2, val);
		}
	} else {
		// Normal object
		json_object *curr = NULL;
		json_object_object_get_ex(root, path_ob[ix], &curr);

		if (curr == NULL) {
			curr = json_object_new_object();
			json_object_object_add(root, path_ob[ix], curr);
		}

		create_json_format(curr, path_ob, max_ob, ix+1, val);
	}
}

void convert_list_to_json(json_object *root, struct list_head *result_list)
{
	struct cmd_result *res;

	if (root == NULL || result_list == NULL)
		return;

	list_for_each_entry(res, result_list, list) {
		char *param = NULL, *val = NULL;
		char tmp[MAX_CMD_LEN] = {0};
		int j = 0;

		strncpy(tmp, res->output, MAX_CMD_LEN);
		param = tmp;
		val = strstr(tmp, "=>");
		if (!val)
			continue;

		*val = '\0';
		val = val + 2; // move after =>

		if (param && val) {
			char *path_obj[256] = {0};

			// remove whitespaces
			remove_whitespaces(param);
			remove_whitespaces(val);

			if (DM_STRLEN(param) == 0 || DM_STRLEN(val) == 0) {
				continue;
			}

			char *ob = strtok(param, ".");
			while (ob) {
				path_obj[j] = strdup(ob);
				j++;
				ob = strtok(NULL, ".");
			}

			if (j == 0) {
				continue;
			}

			create_json_format(root, path_obj, j-1, 0, val);

			while (j) {
				FREE(path_obj[j-1]);
				j--;
			}
		}
	}
}

int set_succeed(char *param_path, char *value)
{
	char cmd[MAX_CMD_LEN] = {0};

	if (param_path == NULL || value == NULL)
		return FAULT_9002;

	LIST_HEAD(result_list);
	snprintf(cmd, sizeof(cmd), "%s %s %s | grep \"%s => %s\"", PROG_CMD_SET, param_path, value, param_path, value);
	exec_cmd(cmd, &result_list);

	if (!list_empty(&result_list)) {
		delete_cmd_result(&result_list);
		return 0;
	}

	return FAULT_9002;
}

int get_instance_number(char *ob_path, char **instance)
{
	char cmd[MAX_CMD_LEN] = {0};

	int len = DM_STRLEN(ob_path);
	if (instance == NULL || len == 0)
		return FAULT_9002;

	LIST_HEAD(result_list);
	snprintf(cmd, sizeof(cmd), "%s %s | grep \"Added %s\"", PROG_CMD_ADD, ob_path, ob_path);
	exec_cmd(cmd, &result_list);

	collect_instance(ob_path, &result_list, instance);
	delete_cmd_result(&result_list);

	if (DM_STRLEN(*instance) != 0) {
		return 0;
	}

	return FAULT_9002;
}

int deletion_succeed(char *ob_path)
{
	char cmd[MAX_CMD_LEN] = {0};

	if (DM_STRLEN(ob_path) == 0)
		return FAULT_9002;

	LIST_HEAD(result_list);
	snprintf(cmd, sizeof(cmd), "%s %s | grep \"Deleted\"", PROG_CMD_DEL, ob_path);
	exec_cmd(cmd, &result_list);

	if (!list_empty(&result_list)) {
		delete_cmd_result(&result_list);
		return 0;
	}

	return FAULT_9002;
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
/* ********** DynamicObj ********** */
DM_MAP_OBJ tDynamicObj[] = {
/* parentobj, nextobject, parameter */
{"Device.", tDevObj, NULL},
{0}
};

/*** Device. ***/
DMOBJ tDevObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
{"USPAgent", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tUSPAgentObj, tUSPAgentParams, NULL, BBFDM_CWMP},
{"LocalAgent", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tLocalAgentParams, NULL, BBFDM_CWMP},
#ifdef ENABLE_MQTT
{"MQTT", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tMQTTObj, tMQTTParams, NULL, BBFDM_CWMP},
#endif
#ifndef DISABLE_STOMP
{"STOMP", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tSTOMPObj, tSTOMPParams, NULL, BBFDM_CWMP},
#endif
{0}
};
