/*
 * Copyright (C) 2020 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 Rahul Thakur <rahul.thakur@iopsys.eu>
 *
 */

#include "x_iopsys_eu_common.h"


void synchronize_specific_config_sections_with_dmmap_mcast_filter(const char *package, const char *section_type,
		void *data, const char *dmmap_package, const char *dmmap_sec, const char *proto,
		struct list_head *dup_list)
{
	struct uci_section *s = NULL, *dmmap_sect = NULL, *d_sec = NULL, *stmp = NULL;
	char *v, *s_name;

	uci_foreach_option_eq(package, section_type, "proto", proto, s) {
		if (strcmp(section_name(s), section_name(((struct dm_data *)data)->config_section)) != 0)
			continue;
		/*
		 * create/update corresponding dmmap section that have same config_section link and using param_value_array
		 */
		struct uci_list *l = NULL;

		dmuci_get_value_by_section_list(s, "filter", &l);
		if (l != NULL) {
			struct uci_element *e = NULL;
			uci_foreach_element(l, e) {
				char *ip_addr = dmstrdup(e->name);
				int found = 0;
				uci_path_foreach_option_eq(bbfdm, dmmap_package, dmmap_sec, "ipaddr", ip_addr, d_sec) {
					dmuci_get_value_by_section_string(d_sec, "section_name", &s_name);
					if (strcmp(s_name, section_name(s)) == 0) {
						add_dmmap_config_dup_list(dup_list, s, d_sec);
						found = 1;
						break;
					}
				}

				if (found == 0) {
					// add entry in dmmap for this
					dmuci_add_section_bbfdm(dmmap_package, dmmap_sec, &d_sec);
					dmuci_set_value_by_section_bbfdm(d_sec, "section_name", section_name(s));
					dmuci_set_value_by_section_bbfdm(d_sec, "ipaddr", ip_addr);
					dmuci_set_value_by_section_bbfdm(d_sec, "enable", "1");
					add_dmmap_config_dup_list(dup_list, s, d_sec);
				}
			}
		}

		char *f_ip, *f_enable;
		// There can be entries in the dmmap_mcast file that do not have an IP address set.
		// For such entries, now add to dup_list
		uci_path_foreach_option_eq(bbfdm, dmmap_package, dmmap_sec, "section_name", section_name(s), dmmap_sect) {
			dmuci_get_value_by_section_string(dmmap_sect, "ipaddr", &f_ip);
			dmuci_get_value_by_section_string(dmmap_sect, "enable", &f_enable);

			if ((f_ip[0] == '\0') || (DM_LSTRCMP(f_enable, "0") == 0))
				add_dmmap_config_dup_list(dup_list, s, dmmap_sect);
		}
	}

	/*
	 * Delete unused dmmap sections
	 */
	uci_path_foreach_sections_safe(bbfdm, dmmap_package, dmmap_sec, stmp, s) {
		dmuci_get_value_by_section_string(s, "section_name", &v);
		if (get_origin_section_from_config(package, section_type, v) == NULL)
			dmuci_delete_by_section(s, NULL, NULL);
	}
}

int get_mcast_snooping_interface_val(struct dm_reference *reference_args, char *ifname, size_t s_ifname)
{
	/* Check if the value is valid or not. */
	if (DM_LSTRNCMP(reference_args->path, "Device.Bridging.Bridge.", 23) != 0)
		return -1;

	snprintf(ifname, s_ifname, "%s", reference_args->value);
	return 0;
}

void del_dmmap_sec_with_opt_eq(const char *dmmap_file, const char *section, const char *option, const char *value)
{
	struct uci_section *d_sec = NULL;
	struct uci_section *stmp = NULL;
	char *opt_val;

	uci_path_foreach_sections_safe(bbfdm, dmmap_file, section, stmp, d_sec) {
		dmuci_get_value_by_section_string(d_sec, option, &opt_val);
		if (DM_STRCMP(opt_val, value) == 0)
			dmuci_delete_by_section(d_sec, NULL, NULL);
	}
}

void sync_dmmap_bool_to_uci_list(struct uci_section *s, const char *section, const char *value, bool b)
{
	struct uci_list *v = NULL;
	struct uci_element *e = NULL;
	char *val = NULL;

	dmuci_get_value_by_section_list(s, section, &v);
	if (v != NULL) {
		uci_foreach_element(v, e) {
			val = dmstrdup(e->name);
			if (val && DM_STRCMP(val, value) == 0) {
				if (!b) {
					// remove this entry
					dmuci_del_list_value_by_section(s, section, value);
				}

				// Further action is not required
				return;
			}
		}
	}

	// If control has reached this point, that means, either the entry was not found
	// in the list, hence, if b is true, add this entry to the list
	if (b) {
		dmuci_add_list_value_by_section(s, section, value);
	}
}

int del_proxy_obj(void *data, const char *proto, unsigned char del_action)
{
	// first delete all filter child nodes related to this object
	del_dmmap_sec_with_opt_eq("dmmap_mcast", "proxy_filter", "section_name", section_name(((struct dm_data *)data)->config_section));

	// Now delete all interface child nodes related to this object
	del_dmmap_sec_with_opt_eq("dmmap_mcast", "proxy_interface", "section_name", section_name(((struct dm_data *)data)->config_section));

	// Now delete the proxy node
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	return 0;
}

static void sync_mcast_dmmap_iface_sec(struct uci_list *proxy_iface, const char *s_mode,
                struct uci_section *s, const char *dmmap_package, const char *dmmap_sec,
                struct list_head *dup_list, const char *up_iface)
{
	struct uci_element *e = NULL;

	if (!proxy_iface || !s_mode || !s || !dmmap_package || !dmmap_sec || !dup_list || !up_iface)
		return;

	uci_foreach_element(proxy_iface, e) {
		struct uci_section *d_sec = NULL;
		char key[1024] = {0};
		bool found = 0;

		char *p_ifname = dmstrdup(e->name);
		DM_STRNCPY(key, p_ifname, sizeof(key));

		// Verify if interface section for this already exists in dmmap_mcast file.
		// In case yes, add this to the dup_list, else create entry in the dmmap_mcast
		// file corresponding to this interface
		uci_path_foreach_option_eq(bbfdm, dmmap_package, dmmap_sec, "ifname", key, d_sec) {
			char *s_name = NULL, *intf_dir = NULL, *intf_smode = NULL;

			dmuci_get_value_by_section_string(d_sec, "section_name", &s_name);
			dmuci_get_value_by_section_string(d_sec, "upstream", &intf_dir);
			dmuci_get_value_by_section_string(d_sec, "snooping_mode", &intf_smode);

			if (strcmp(s_name, section_name(s)) == 0 &&
					strcmp(intf_dir, up_iface) == 0 &&
					strcmp(intf_smode, s_mode) == 0) {
				add_dmmap_config_dup_list(dup_list, s, d_sec);
				found = 1;
				break;
			}
		}

		if (found == 0) {
			// add entry in dmmap for this
			dmuci_add_section_bbfdm(dmmap_package, dmmap_sec, &d_sec);
			dmuci_set_value_by_section_bbfdm(d_sec, "section_name", section_name(s));
			dmuci_set_value_by_section_bbfdm(d_sec, "ifname", key);
			dmuci_set_value_by_section_bbfdm(d_sec, "upstream", up_iface);
			dmuci_set_value_by_section_bbfdm(d_sec, "snooping_mode", s_mode);
			add_dmmap_config_dup_list(dup_list, s, d_sec);
		}
	}
}

static void add_empty_mcast_iface_to_list(const char *dmmap_package, const char *dmmap_sec,
                struct uci_section *s, struct list_head *dup_list)
{
	struct uci_section *dmmap_sect = NULL;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, dmmap_sec, "section_name", section_name(s), dmmap_sect) {
		char *f_ifname = NULL;

		dmuci_get_value_by_section_string(dmmap_sect, "ifname", &f_ifname);

		if (f_ifname && *f_ifname == '\0')
			add_dmmap_config_dup_list(dup_list, s, dmmap_sect);
	}
}

void synchronize_specific_config_sections_with_dmmap_mcast_iface(const char *package, const char *section_type,
		void *data, const char *dmmap_package, const char *dmmap_sec, const char *proto,
		struct list_head *dup_list)
{
	struct uci_section *s = NULL, *stmp = NULL;
	char *v;

	uci_foreach_option_eq(package, section_type, "proto", proto, s) {
		if (strcmp(section_name(s), section_name(((struct dm_data *)data)->config_section)) != 0)
			continue;

		// The list snooping_interface and proxy_interface in the uci file corresponds to the
		// proxy_interface section in the dmmap. First, read the list of proxy interfaces
		// and update the dmmap section accordingly. The do the same exercise for the list
		// snooping_interface
		struct uci_list *proxy_iface = NULL;

		dmuci_get_value_by_section_list(s, "upstream_interface", &proxy_iface);
		if (proxy_iface != NULL)
			sync_mcast_dmmap_iface_sec(proxy_iface, "0", s, dmmap_package, dmmap_sec, dup_list, "1");

		struct uci_list *snooping_iface = NULL;
		char *s_mode;
		dmuci_get_value_by_section_list(s, "downstream_interface", &snooping_iface);
		dmuci_get_value_by_section_string(s, "snooping_mode", &s_mode);
		if (snooping_iface != NULL)
			sync_mcast_dmmap_iface_sec(snooping_iface, s_mode, s, dmmap_package, dmmap_sec, dup_list, "0");

		// There can be entries in the dmmap_mcast file that do not have an ifname set.
		// For such entries, now add to dup_list
		add_empty_mcast_iface_to_list(dmmap_package, dmmap_sec, s, dup_list);
	}

	/*
	 * Delete unused dmmap sections
	 */
	uci_path_foreach_sections_safe(bbfdm, dmmap_package, dmmap_sec, stmp, s) {
		dmuci_get_value_by_section_string(s, "section_name", &v);
		if (get_origin_section_from_config(package, section_type, v) == NULL)
			dmuci_delete_by_section(s, NULL, NULL);
	}
}


int del_snooping_obj(void *data)
{
	// first delete all filter child nodes related to this object
	del_dmmap_sec_with_opt_eq("dmmap_mcast", "snooping_filter", "section_name", section_name(((struct dm_data *)data)->config_section));

	// Now delete all interface child nodes related to this object
	del_dmmap_sec_with_opt_eq("dmmap_mcast", "snooping_interface", "section_name", section_name(((struct dm_data *)data)->config_section));

	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	return 0;
}


int del_mcasts_filter_obj(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *d_sec = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "snooping_filter", "section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		char *f_inst = NULL, *ip_addr = NULL;
		bool found = false;

		dmuci_get_value_by_section_string(d_sec, "filter_instance", &f_inst);

		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "ipaddr", &ip_addr);
			dmuci_delete_by_section(d_sec, NULL, NULL);
			found = true;
		}

		if (found) {
			dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section, "filter", ip_addr);
			break;
		}
	}

	return 0;
}

int browse_proxy_interface_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, const char *proto)
{
	struct dm_data *curr_data = NULL;
	LIST_HEAD(dup_list);
	char *inst = NULL;

	synchronize_specific_config_sections_with_dmmap_mcast_iface("mcast", "proxy", prev_data, "dmmap_mcast", "proxy_interface", proto, &dup_list);
	list_for_each_entry(curr_data, &dup_list, list) {

		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "iface_instance", "iface_alias");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)curr_data, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

int browse_filter_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, const char *section_type, const char *option_name, const char *option_value)
{
	struct dm_data *curr_data = NULL;
	LIST_HEAD(dup_list);
	char *inst = NULL;

	synchronize_specific_config_sections_with_dmmap_mcast_filter("mcast", section_type, prev_data, "dmmap_mcast", option_name, option_value, &dup_list);
	list_for_each_entry(curr_data, &dup_list, list) {

		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "filter_instance", "filter_alias");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)curr_data, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}


int get_mcasts_filter_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *f_sec = NULL;
	char *f_inst, *f_enable = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "snooping_filter",
			"section_name", section_name(((struct dm_data *)data)->config_section), f_sec) {
		dmuci_get_value_by_section_string(f_sec, "filter_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(f_sec, "enable", &f_enable);
			break;
		}
	}

	if (DM_LSTRCMP(f_enable, "1") == 0) {
		*value = dmstrdup("true");
	} else {
		*value = dmstrdup("false");
	}

	return 0;
}

int set_mcasts_filter_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *f_sec;
	char *f_inst, *ip_addr;
	bool b;
	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "snooping_filter",
				"section_name", section_name(((struct dm_data *)data)->config_section), f_sec) {
			dmuci_get_value_by_section_string(f_sec, "filter_instance", &f_inst);
			if (DM_STRCMP(instance, f_inst) == 0) {
				dmuci_get_value_by_section_string(f_sec, "ipaddr", &ip_addr);
				dmuci_set_value_by_section(f_sec, "enable", (b) ? "1" : "0");
				if (ip_addr[0] != '\0') {
					sync_dmmap_bool_to_uci_list(((struct dm_data *)data)->config_section,
							"filter", ip_addr, b);
				}
				break;
			}
		}
		break;
	}

	return 0;
}

int get_mcasts_filter_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *d_sec = NULL;
	char *f_inst, *ip_addr = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "snooping_filter",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "filter_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "ipaddr", &ip_addr);
			break;
		}
	}

	if (DM_STRLEN(ip_addr) == 0) {
		*value = dmstrdup("");
	} else {
		*value = dmstrdup(ip_addr);
	}

	return 0;
}

int set_mcasts_filter_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *s = NULL;
	char *s_inst, *up;
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
			return FAULT_9007;

		break;
	case VALUESET:
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "snooping_filter",
				"section_name", section_name(((struct dm_data *)data)->config_section), s) {
			dmuci_get_value_by_section_string(s, "filter_instance", &s_inst);
			if (DM_STRCMP(s_inst, instance) == 0) {
				dmuci_set_value_by_section(s, "ipaddr", value);
				dmuci_get_value_by_section_string(s, "enable", &up);
				string_to_bool(up, &b);
				sync_dmmap_bool_to_uci_list(((struct dm_data *)data)->config_section,
						"filter", value, b);
				break;
			}
		}

		break;
	}

	return 0;
}

int get_mcast_snooping_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enable", "0");
	return 0;
}

int set_mcast_snooping_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int get_mcast_snooping_mode(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *val;
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "snooping_mode", &val);

	if (DM_LSTRCMP(val, "1") == 0)
		*value = dmstrdup("Standard");
	else if (DM_LSTRCMP(val, "2") == 0)
		*value = dmstrdup("Blocking");
	else
		*value = dmstrdup("Disabled");

	return 0;
}

int set_mcast_snooping_mode(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char val[4];

	switch (action) {
	case VALUECHECK:
		if ((DM_LSTRCMP(value, "Standard") != 0)
			&& (DM_LSTRCMP(value, "Blocking") != 0)
			&& (DM_LSTRCMP(value, "Disabled") != 0))
			return FAULT_9007;
		break;
	case VALUESET:
		if (DM_LSTRCMP(value, "Standard") == 0)
			DM_STRNCPY(val, "1", sizeof(val));
		else if (DM_LSTRCMP(value, "Blocking") == 0)
			DM_STRNCPY(val, "2", sizeof(val));
		else
			DM_STRNCPY(val, "0", sizeof(val));

		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "snooping_mode", val);
		break;
	}

	return 0;
}

int get_mcasts_last_mq_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "last_member_query_interval", "10");
	return 0;
}

int set_mcasts_last_mq_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "last_member_query_interval", value);
		break;
	}

	return 0;
}

int get_mcast_snooping_querier(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "querier", "0");
	return 0;
}

int set_mcast_snooping_querier(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "querier", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int get_mcasts_fast_leave(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "fast_leave", "1");
	return 0;
}

int set_mcasts_fast_leave(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "fast_leave", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int get_mcast_snooping_robustness(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "robustness", "2");
	return 0;
}

int set_mcast_snooping_robustness(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "robustness", value);
		break;
	}

	return 0;
}

int get_mcast_snooping_aggregation(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "aggregation", "1");
	return 0;
}

int set_mcast_snooping_aggregation(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "aggregation", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int get_mcast_snooping_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char val[16] = {0}; // taking 16 here is same as that is size of linux names usually supported
	char *val1;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "interface", &val1);

	// The value is linux interface name so it would be br-wan for example, but the network
	// section would be wan, so extract wan from br-wan
	char *tok, *end;

	DM_STRNCPY(val, val1, sizeof(val));
	tok = strtok_r(val, "-", &end);
	if ((tok == NULL) || (end == NULL))
		return 0;

	if (DM_LSTRCMP(tok, "br") != 0)
		return 0;

	// In the dmmap_bridge file, the details related to the instance id etc. associated with this bridge
	// is stored, we now switch our focus to it to extract the necessary information.
	_bbfdm_get_references(ctx, "Device.Bridging.Bridge.*.Port.", "Name", val1, value);
	return 0;
}

int set_mcast_snooping_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dm_reference reference = {0};
	char ifname[16] = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action)	{
	case VALUECHECK:
		if (bbfdm_validate_string_list(ctx, reference.path, -1, -1, 1024, -1, -1, NULL, NULL))
			return FAULT_9007;
		break;
	case VALUESET:
		if (get_mcast_snooping_interface_val(&reference, ifname, sizeof(ifname)) != 0)
			return -1;

		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interface", ifname);
		break;
	}

	return 0;
}


int del_mcastp_filter_obj(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *d_sec = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_filter",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		char *f_inst = NULL, *ip_addr = NULL;
		bool found = false;

		dmuci_get_value_by_section_string(d_sec, "filter_instance", &f_inst);

		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "ipaddr", &ip_addr);
			dmuci_delete_by_section(d_sec, NULL, NULL);
			found = true;
		}

		if (found) {
			dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section, "filter", ip_addr);
			break;
		}
	}

	return 0;
}

int get_mcastp_filter_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *f_sec = NULL;
	char *f_inst, *f_enable = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_filter",
			"section_name", section_name(((struct dm_data *)data)->config_section), f_sec) {
		dmuci_get_value_by_section_string(f_sec, "filter_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(f_sec, "enable", &f_enable);
			break;
		}
	}

	if (f_enable && DM_LSTRCMP(f_enable, "1") == 0) {
		*value = dmstrdup("true");
	} else {
		*value = dmstrdup("false");
	}

	return 0;
}

int set_mcastp_filter_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *f_sec;
	char *f_inst, *ip_addr;
	bool b;
	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_filter",
				"section_name", section_name(((struct dm_data *)data)->config_section), f_sec) {
			dmuci_get_value_by_section_string(f_sec, "filter_instance", &f_inst);
			if (DM_STRCMP(instance, f_inst) == 0) {
				dmuci_get_value_by_section_string(f_sec, "ipaddr", &ip_addr);
				dmuci_set_value_by_section(f_sec, "enable", (b) ? "1" : "0");
				sync_dmmap_bool_to_uci_list(((struct dm_data *)data)->config_section,
						"filter", ip_addr, b);
				break;
			}
		}
		break;
	}

	return 0;
}

int get_mcastp_filter_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *d_sec = NULL;
	char *f_inst;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_filter",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "filter_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "ipaddr", value);
			break;
		}
	}

	return 0;
}


int get_mcast_proxy_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enable", "0");
	return 0;
}
int set_mcast_proxy_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int get_mcast_proxy_robustness(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "robustness", "2");
	return 0;
}

int get_mcastp_query_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "query_interval", "125");
	return 0;
}

int get_mcastp_q_response_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "query_response_interval", "100");
	return 0;
}

int get_mcastp_last_mq_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "last_member_query_interval", "10");
	return 0;
}

int set_mcastp_query_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "query_interval", value);
		break;
	}

	return 0;
}

int set_mcastp_q_response_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "query_response_interval", value);
		break;
	}

	return 0;
}

int set_mcastp_last_mq_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "last_member_query_interval", value);
		break;
	}

	return 0;
}

int set_mcast_proxy_robustness(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "robustness", value);
		break;
	}

	return 0;
}

int get_mcast_proxy_aggregation(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "aggregation", "1");
	return 0;
}

int get_mcast_proxy_fast_leave(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "fast_leave", "1");
	return 0;
}

int set_mcast_proxy_fast_leave(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "fast_leave", (b) ? "1" : "0");
		break;
	}

	return 0;
}

int set_mcast_proxy_aggregation(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "aggregation", (b) ? "1" : "0");
		break;
	}

	return 0;
}

void update_snooping_mode(struct uci_section *s)
{
	// Update snooping mode as per downstream interface
	struct uci_list *v = NULL;
	struct uci_element *e = NULL;
	struct uci_section *itf_sec = NULL;
	char *val = NULL, *s_mode = NULL, *up = NULL;
	bool b;

	dmuci_get_value_by_section_list(s, "downstream_interface", &v);
	if (v != NULL) {
		uci_foreach_element(v, e) {
			val = dmstrdup(e->name);
			uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface", "ifname", val, itf_sec) {
				dmuci_get_value_by_section_string(itf_sec, "upstream", &up);
				string_to_bool(up, &b);
				if (b)
					continue;

				dmuci_get_value_by_section_string(itf_sec, "snooping_mode", &s_mode);
				dmuci_set_value_by_section(s, "snooping_mode", s_mode);
				break;
			}

			// Further action is not required
			break;
		}
	} else {
		dmuci_set_value_by_section(s, "snooping_mode", "0");
	}
	return;
}


int get_mcastp_interface_upstream(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *d_sec = NULL;
	char *f_inst, *up = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "upstream", &up);
			break;
		}
	}

	*value = (up && DM_LSTRCMP(up, "1") == 0) ? "true" : "false";
	return 0;
}

int get_mcastp_iface_snoop_mode(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *d_sec = NULL;
	char *f_inst, *val = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "snooping_mode", &val);
			break;
		}
	}

	if (val && DM_LSTRCMP(val, "1") == 0)
		*value = dmstrdup("Standard");
	else if (val && DM_LSTRCMP(val, "2") == 0)
		*value = dmstrdup("Blocking");
	else
		*value = dmstrdup("Disabled");

	return 0;
}

int set_mcastp_iface_snoop_mode(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *f_inst, *up;
	struct uci_section *d_sec;
	char val[4];
	bool b;

	switch (action) {
	case VALUECHECK:
		if ((DM_LSTRCMP(value, "Standard") != 0)
			&& (DM_LSTRCMP(value, "Blocking") != 0)
			&& (DM_LSTRCMP(value, "Disabled") != 0))
			return FAULT_9007;
		break;
	case VALUESET:
		if (DM_LSTRCMP(value, "Standard") == 0)
			strcpy(val, "1");
		else if (DM_LSTRCMP(value, "Blocking") == 0)
			strcpy(val, "2");
		else
			strcpy(val, "0");

		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
			if (DM_STRCMP(instance, f_inst) == 0) {
				dmuci_get_value_by_section_string(d_sec, "upstream", &up);
				dmuci_set_value_by_section(d_sec, "snooping_mode", val);

				string_to_bool(up, &b);
				if (!b) {
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "snooping_mode", val);
				}
				break;
			}
		}
		break;
	}

	return 0;
}

void sync_proxy_interface_sections(struct uci_section *s, const char *section, const char *value, bool up_iface)
{
	struct uci_list *v = NULL;
	struct uci_element *e = NULL;

	if (!s || !section || !value)
		return;

	dmuci_get_value_by_section_list(s, section, &v);
	// value here is a list of space separated names of interface so,
	// the first task is to tokenise this and then for each interface,
	// update the downstream or upstream interface list.
	char *value_list = dmstrdup(value);

	char *spch = NULL;
	char *pch = strtok_r(value_list, " ", &spch);
	while (pch != NULL) {

		if (v != NULL) {
			// For each pch value check if entry already exists
			// in the qos uci file in the downstream or upstream list

			bool found = 0; // use to avoid duplicate entries

			uci_foreach_element(v, e) {
				if (DM_STRCMP(e->name, pch) == 0) {
					found = 1;
					// Further action is not required
					break;
				}
			}

			// if entry was not found and b is true create entry. Check for
			// found in needed otherwise, duplicate entry maybe created
			if (up_iface && !found) {
				dmuci_add_list_value_by_section(s, section, pch);
			}
		} else {
			// The list of downstream or upstream interfaces in uci file is
			// empty, so just add entries if needed
			if (up_iface) {
				dmuci_add_list_value_by_section(s, section, pch);
			}
		}

		pch = strtok_r(NULL, " ", &spch);
	}
}

void set_iface_val(void *data, char *instance, char *linker, char *interface_linker, bool is_br)
{
	struct uci_section *d_sec = NULL, *device_s = NULL;
	char *f_inst = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
			"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
		if (DM_STRCMP(instance, f_inst) == 0) {
			char *device_name = NULL, *interface_name = NULL, *up = NULL;
			bool b, is_bridge_interface = false;

			// Read the interface name from dmmap before updating it
			dmuci_get_value_by_section_string(d_sec, "ifname", &interface_name);
			dmuci_set_value_by_section(d_sec, "ifname", interface_linker);
			dmuci_get_value_by_section_string(d_sec, "upstream", &up);
			string_to_bool(up, &b);

			// Checking if it is a bridge interface
			// Read the device name of bridge interface
			uci_foreach_sections("network", "device", device_s) {
				char *device_type = NULL;

				dmuci_get_value_by_section_string(device_s, "type", &device_type);
				dmuci_get_value_by_section_string(device_s, "name", &device_name);

				if ((DM_STRCMP(device_type, "bridge") == 0) && (DM_STRCMP(device_name, interface_name) == 0)) {
					is_bridge_interface = true;
					break;
				}
			}

			if (!is_bridge_interface) {
				device_name = interface_name;
			}

			// Delete the previous upstream_interface/downstream_interface from the uci
			// file as it is updated in dmmap file, and will be updated after sync in
			// sync_proxy_interface_sections function
			dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section,
					(b) ? "upstream_interface" : "downstream_interface" , device_name);

			sync_proxy_interface_sections(((struct dm_data *)data)->config_section,
					"downstream_interface", interface_linker, !b);

			// Now update the proxy_interface list
			sync_proxy_interface_sections(((struct dm_data *)data)->config_section,
					"upstream_interface", interface_linker, b);
			update_snooping_mode(((struct dm_data *)data)->config_section);
			break;
		}
	}
}

void get_mcast_iface_key(const char *p_ifname, char *key, size_t key_size)
{
	struct uci_section *s = NULL;

	uci_foreach_sections("network", "interface", s) {
		char *intf_device = NULL, *pch = NULL, *spch = NULL;

		dmuci_get_value_by_section_string(s, "device", &intf_device);

		pch = strtok_r(intf_device, " ", &spch);
		while (pch != NULL) {
			if (DM_STRCMP(pch, p_ifname) == 0) {
				DM_STRNCPY(key, section_name(s), key_size);
				return;
			}
			pch = strtok_r(NULL, " ", &spch);
		}
	}
}

int set_interface_iface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.IP.Interface.", NULL};
	struct dm_reference reference = {0};
	char *interface_linker = NULL;
	char ifname[16] = {0};
	char *if_type = NULL;
	struct uci_section *s = NULL;
	bool is_br = false;


	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_string(ctx, reference.path, -1, 256, NULL, NULL))
			return FAULT_9007;

		if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
			return FAULT_9007;

		break;
	case VALUESET:
		// First check if this is a bridge type interface
		if (get_mcast_snooping_interface_val(&reference, ifname, sizeof(ifname)) == 0) {
			interface_linker = dmstrdup(ifname);
			is_br = true;
		} else {
			if (DM_STRLEN(reference.value)) {
				uci_foreach_sections("network", "interface", s) {

					if (strcmp(section_name(s), reference.value) != 0)
						continue;

					dmuci_get_value_by_section_string(s, "type", &if_type);
					if (if_type && DM_LSTRCMP(if_type, "bridge") == 0) {
						dmasprintf(&interface_linker, "br-%s", reference.value);
						is_br = true;
					} else {
						dmuci_get_value_by_section_string(s, "device", &interface_linker);
					}
					break;
				}
			} else {
				interface_linker = dmstrdup("");
			}
		}

		set_iface_val(data, instance, reference.value, interface_linker, is_br);
		break;
	}

	return 0;
}

int set_interface_upstream(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *f_inst, *ifname;
	struct uci_section *d_sec;
	bool b;

	switch (action) {
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name(((struct dm_data *)data)->config_section), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
			if (DM_STRCMP(instance, f_inst) == 0) {
				// The interface is a part of downstream or upstream list in the
				// uci file based on the value of upstream parameter, hence, when
				// this parameter is updated, need arises to update the lists as well.
				// Reading the interface name to be updated associated with the
				// instance for which upstream parameter is being updated is hence
				// needed. This value is read into variable key.
				char key[1024];
				dmuci_get_value_by_section_string(d_sec, "ifname", &ifname);
				DM_STRNCPY(key, ifname, sizeof(key));

				dmuci_set_value_by_section(d_sec, "upstream", (b) ? "1" : "0");
				sync_proxy_interface_sections(((struct dm_data *)data)->config_section, "downstream_interface", key, !b);
				sync_proxy_interface_sections(((struct dm_data *)data)->config_section, "upstream_interface", key, b);
				update_snooping_mode(((struct dm_data *)data)->config_section);

				break;
			}
		}

		break;
	}

	return 0;
}
