/*
 * event.c: event handler
 *
 * Copyright (C) 2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: Iryna Antsyferova <iryna.antsyferova@iopsys.eu>
 *
 * See LICENSE file for license related information.
 */
#include "libdmtree/dmcommon.h"
#include "event.h"
#include <unistd.h>
#include <netinet/in.h>

#define FIREWALL_RULE_DATA_SIP "Device.Services.VoiceService.1.SIP.Network.1.X_RDK_Firewall_Rule_Data"
#define FIREWALL_RULE_DATA_SIP_PATTERN "Device\\.Services\\.VoiceService\\.[0-9]+\\.SIP\\.Network\\.[0-9]+\\.X_RDK_Firewall_Rule_Data"

#define FIREWALL_RULE_DATA_RTP "Device.Services.VoiceService.1.VoIPProfile.1.RTP.X_RDK_Firewall_Rule_Data"
#define FIREWALL_RULE_DATA_RTP_PATTERN "Device\\.Services\\.VoiceService\\.[0-9]+\\.VoIPProfile\\.[0-9]+\\.RTP\\.X_RDK_Firewall_Rule_Data"

// 4 IPv6 rules max
#define FIREWALL_RULE_STR_LEN_MAX 218

struct rule_node {
	struct list_head list;
	char ip[INET6_ADDRSTRLEN];
	uint16_t port;
};

static struct list_head sip_list, rtp_list;

int subs_event_init()
{
	INIT_LIST_HEAD(&sip_list);
	INIT_LIST_HEAD(&rtp_list);

	return 0;
}

void subs_event_uninit()
{
	struct rule_node *iter = NULL, *node = NULL;

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

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

int subs_event_handler(const json_object *jmsg, int param_count, json_object *jreply)
{
	int ret = RETURN_OK;
	int param_index = 0;
	hal_subscribe_event_request_t param_request;

	if (jmsg == NULL || jreply == NULL) {
		ERR("event handler: Invalid memory");
		return RETURN_ERR;
	}

	memset(&param_request, 0, sizeof(param_request));

	for (param_index = 0; param_index < param_count; param_index++) {
		/* Unpack the request parameter */
		if (json_hal_get_subscribe_event_request((json_object *)jmsg,
			param_index, &param_request) != RETURN_OK) {
			INFO("Failed to get subscription data from the server");
			return RETURN_ERR;
		}

		if (!match(param_request.name, FIREWALL_RULE_DATA_SIP_PATTERN, 0, NULL) &&
			!match(param_request.name, FIREWALL_RULE_DATA_RTP_PATTERN, 0, NULL)) {
			ret = RETURN_ERR;
			WARNING("Unexpected event [%s][%d]", param_request.name, (int)param_request.type);
		}
		else
			INFO("Subscribed [%s][%d]", param_request.name, (int)param_request.type);
	}

	ret = json_hal_add_result_status(jreply, ret == RETURN_OK ? RESULT_SUCCESS :
		RESULT_FAILURE);

	INFO("[%s] Replly msg = %s", __FUNCTION__,
		json_object_to_json_string_ext(jreply, JSON_C_TO_STRING_PRETTY));

	return ret;
}

static int update_fw_rule_list(bool enable, char *ip, int port,
	struct list_head *rule_list, bool *changed)
{
	struct rule_node *node = NULL;
	bool found = false;

	*changed = false;

	list_for_each_entry(node, rule_list, list) {
		if (DM_STRNCMP(node->ip, ip, sizeof(node->ip)) == 0 &&
			node->port == port) {
			found = true;
			break;
		}
	}

	if (enable == found) {
		return 0;
	}

	if (!enable && found) {
		list_del(&node->list);
		free(node);
	}

	if (enable && !found) {
		node = malloc(sizeof(struct rule_node));
		if (!node) {
			ERR("failed to allocate memory");
			return -1;
		}

		DM_STRNCPY(node->ip, ip, sizeof(node->ip));
		node->port = port;
		INIT_LIST_HEAD(&node->list);
		list_add_tail(&node->list, rule_list);
	}

	*changed = true;

	return 0;
}

/* Expected format of rule : ip,port;
   Example: 192.168.0.11,5060;192.168.0.12,5060;
*/
static int create_fw_rule_payload(struct list_head *rule_list, char *buf, int buf_size)
{
	struct rule_node *node = NULL;
	char *pos = buf, *end = buf + buf_size;

	list_for_each_entry(node, rule_list, list) {
		if (pos < end) {
			pos += snprintf(pos, end - pos, "%s,%d;", node->ip, node->port);
		}
	}

	if (pos == end) {
		ERR("fw rule string overflow");
		return -1;
	}

	return 0;
}

static int pub_fw_rule_event(bool enable, char *ip, int port,
	struct list_head *rule_list, const char *param_name)
{
	int ret;
	bool changed = false;
	char buf[FIREWALL_RULE_STR_LEN_MAX] = {0};

	ret = update_fw_rule_list(enable, ip, port, rule_list, &changed);
	if (ret) {
		ERR("failed to create fw rule list");
		return -1;
	}

	if (!changed) {
		return 0;
	}

	ret = create_fw_rule_payload(rule_list, buf, sizeof(buf));
	if (ret) {
		ERR("failed to create fw rule");
		return -1;
	}

	ret = json_hal_server_publish_event(param_name, buf);
	if (ret != RETURN_OK) {
		ERR("failed to send fw event");
		return -1;
	}

	return 0;
}

int send_fw_rule_event(bool enable, char *protocol, char *ip, int port)
{
	if (DM_STRCMP(protocol, "sip") == 0) {
		return pub_fw_rule_event(enable, ip, port, &sip_list, FIREWALL_RULE_DATA_SIP);
	}

	if (DM_STRCMP(protocol, "rtp") == 0) {
		return pub_fw_rule_event(enable, ip, port, &rtp_list, FIREWALL_RULE_DATA_RTP);
	}

	DEBUG("wrong protocol: %s", protocol);
	return -1;
}

int send_fw_rules_clear_event()
{
	int ret = 0;
	char fw_rule[] = {0};
	struct rule_node *iter, *node;

	ret = json_hal_server_publish_event(FIREWALL_RULE_DATA_SIP, fw_rule);
	if (ret != RETURN_OK) {
		ERR("failed to send fw sip event");
		return -1;
	}

	ret = json_hal_server_publish_event(FIREWALL_RULE_DATA_RTP, fw_rule);
	if (ret != RETURN_OK) {
		ERR("failed to send fw rtp event");
		return -1;
	}

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

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

	return 0;
}