/*
 * 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: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 */

#include "dmentry.h"
#include "interfacestack.h"

struct interfacestack_data {
	char *lowerlayer;
	char *higherlayer;
	char *loweralias;
	char *higheralias;
};

/*************************************************************
* ENTRY METHOD
**************************************************************/
static char *get_instance_by_section(int mode, char *dmmap_config, char *section, char *option, char *value, char *instance_option, char *alias_option)
{
	struct uci_section *dmmap_section;
	char *instance = "";

	get_dmmap_section_of_config_section_eq(dmmap_config, section, option, value, &dmmap_section);

	if (mode == INSTANCE_MODE_NUMBER)
		dmuci_get_value_by_section_string(dmmap_section, instance_option, &instance);
	else
		dmuci_get_value_by_section_string(dmmap_section, alias_option, &instance);

	return instance;
}

static char *get_instance_by_section_option_condition(int mode, char *dmmap_config, char *section, struct uci_section *s, char *option, char *value, char *instance_option, char *alias_option)
{
	struct uci_section *dmmap_section;
	char *instance = "";

	get_dmmap_section_of_config_section_cont(dmmap_config, section, option, value, &dmmap_section);

	if (mode == INSTANCE_MODE_NUMBER)
		dmuci_get_value_by_section_string(dmmap_section, instance_option, &instance);
	else
		dmuci_get_value_by_section_string(dmmap_section, alias_option, &instance);

	return instance;
}

static char *get_alias_by_section(char *dmmap_config, char *section, struct uci_section *s, char *alias_option)
{
	struct uci_section *dmmap_section;
	char *alias = "";

	get_dmmap_section_of_config_section(dmmap_config, section, section_name(s), &dmmap_section);
	dmuci_get_value_by_section_string(dmmap_section, alias_option, &alias);
	return alias;
}

static char *get_alias_by_section_option_condition(char *dmmap_config, char *section, char *option, char *value, char *alias_option)
{
	struct uci_section *dmmap_section;
	char *alias = "";

	get_dmmap_section_of_config_section_cont(dmmap_config, section, option, value, &dmmap_section);
	dmuci_get_value_by_section_string(dmmap_section, alias_option, &alias);

	return alias;
}
static struct uci_section *create_dmmap_interface_stack_section(char *curr_inst)
{
	struct uci_section *s = NULL;

	uci_path_foreach_option_eq(bbfdm, "dmmap_interface_stack", "interface_stack", "interface_stack_instance", curr_inst, s) {
		return s;
	}

	if (!s) {
		dmuci_add_section_bbfdm("dmmap_interface_stack", "interface_stack", &s);
		dmuci_set_value_by_section_bbfdm(s, "interface_stack_instance", curr_inst);
	}

	return s;
}

int browseInterfaceStackInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct interfacestack_data intf_stack_data = {0};
	struct uci_section *s = NULL, *dmmap_s = NULL;
	char *layer_inst = "", *loweralias = "", *higheralias = "";
	char *inst = NULL, *max_inst = NULL;
	char buf_lowerlayer[128] = {0};
	char buf_higherlayer[128] = {0};
	char buf_higheralias[64] = {0};
	char buf_loweralias[64] = {0};
	char buf_instance[16] = {0};
	int instance = 0;

	/* Higher layers are Device.IP.Interface.{i}. */
	uci_foreach_sections("network", "interface", s) {
		char *proto, *ifname;

		dmuci_get_value_by_section_string(s, "proto", &proto);
		dmuci_get_value_by_section_string(s, "ifname", &ifname);

		if (strcmp(section_name(s), "loopback") == 0 ||
			*proto == '\0' ||
			strchr(ifname, '@'))
			continue;

		// The higher layer is Device.IP.Interface.{i}.
		layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "interface", "section_name", section_name(s), "ip_int_instance", "ip_int_alias");
		if (*layer_inst == '\0')
			continue;
		snprintf(buf_higherlayer, sizeof(buf_higherlayer), "Device.IP.Interface.%s", layer_inst);

		higheralias = get_alias_by_section("dmmap_network", "interface", s, "ip_int_alias");
		if (*higheralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
			else
				snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", higheralias);

		if (strstr(proto, "ppp")) {
			// The lower layer is Device.PPP.Interface.{i}.
			layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "interface", "section_name", section_name(s), "ppp_int_instance", "ppp_int_alias");
			if (*layer_inst == '\0')
				continue;
			snprintf(buf_lowerlayer, sizeof(buf_lowerlayer), "Device.PPP.Interface.%s", layer_inst);
			loweralias = get_alias_by_section("dmmap_network", "interface", s, "ppp_int_alias");
			if (*loweralias == '\0')
				snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", layer_inst);
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);
		} else {
			// The lower layer is Device.Ethernet.VLANTermination.{i}.
			char *value = NULL;
			int found = 0;
			char *device = get_device(section_name(s));
			if (device[0] != '\0') {
				struct uci_section *vlan_sect = NULL;
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cVLANTermination%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
				uci_foreach_option_eq("network", "device", "name", device, vlan_sect) {
					loweralias = get_alias_by_section("dmmap_network", "device", vlan_sect, "vlan_term_alias");
					layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "device", "section_name", section_name(vlan_sect), "vlan_term_instance", "vlan_term_alias");
					break;
				}
				if (value != NULL)
					found = 1;
			}

			if (device[0] != '\0' && found == 0) {
				// The lower layer is Device.Ethernet.Link.{i}.
				char linker[32] = {0};
				strncpy(linker, device, sizeof(linker) - 1);
				char *vid = strchr(linker, '.');
				if (vid) *vid = '\0';
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cLink%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &value);
				loweralias = get_alias_by_section_option_condition("dmmap", "link", "section_name", section_name(s), "link_alias");
				layer_inst = get_instance_by_section_option_condition(dmctx->instance_mode, "dmmap", "link", s, "section_name", section_name(s), "link_instance", "link_alias");
				if (value == NULL)
					value = "";
			}
			snprintf(buf_lowerlayer, sizeof(buf_lowerlayer), "%s", value?value:"");
			if (*loweralias == '\0')
				if (*layer_inst == '\0')
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", layer_inst);
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);
		}

		// fill interface stack data
		intf_stack_data.higherlayer = buf_higherlayer;
		intf_stack_data.lowerlayer = buf_lowerlayer;
		intf_stack_data.higheralias = buf_higheralias;
		intf_stack_data.loweralias = buf_loweralias;

		// create dmmap section
		snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
		dmmap_s = create_dmmap_interface_stack_section(buf_instance);

		// link instance to interface stack data
		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
			   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
			goto end;
	}

	/* Higher layers are Device.PPP.Interface.{i}. */
	uci_foreach_sections("network", "interface", s) {
		char *proto;
		dmuci_get_value_by_section_string(s, "proto", &proto);
		if (!strstr(proto, "ppp"))
			continue;

		// The higher layer is Device.PPP.Interface.{i}.
		layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "interface", "section_name", section_name(s), "ppp_int_instance", "ppp_int_alias");
		if (*layer_inst == '\0')
			continue;
		snprintf(buf_higherlayer, sizeof(buf_higherlayer), "Device.PPP.Interface.%s", layer_inst);

		higheralias = get_alias_by_section("dmmap_network", "interface", s, "ppp_int_alias");
		if (*higheralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
			else
				snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", higheralias);

		char *value = NULL;
		int found = 0;
		// The lower layer is Device.Ethernet.VLANTermination.{i}.
		char *device = get_device(section_name(s));
		if (device[0] != '\0') {
			struct uci_section *vlan_sect = NULL;
			adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cVLANTermination%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
			uci_foreach_option_eq("network", "device", "name", device, vlan_sect) {
				loweralias = get_alias_by_section("dmmap_network", "device", vlan_sect, "vlan_term_alias");
				layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "device", "section_name", section_name(vlan_sect), "vlan_term_instance", "vlan_term_alias");
				break;
			}
			if (value != NULL)
				found = 1;
		}

		if (found == 0) {
			// The lower layer is Device.Ethernet.Link.{i}.
			char linker[32] = {0};
			strncpy(linker, device, sizeof(linker) - 1);
			char *vid = strchr(linker, '.');
			if (vid) *vid = '\0';
			adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cLink%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &value);
			loweralias = get_alias_by_section("dmmap", "link", s, "link_alias");
			layer_inst = get_instance_by_section_option_condition(dmctx->instance_mode, "dmmap", "link", s, "section_name", section_name(s), "link_instance", "link_alias");
			if (value == NULL)
				value = "";
		}

		snprintf(buf_lowerlayer, sizeof(buf_lowerlayer), "%s", value);
		if (*loweralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

		// fill interface stack data
		intf_stack_data.higherlayer = buf_higherlayer;
		intf_stack_data.lowerlayer = buf_lowerlayer;
		intf_stack_data.higheralias = buf_higheralias;
		intf_stack_data.loweralias = buf_loweralias;

		// create dmmap section
		snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
		dmmap_s = create_dmmap_interface_stack_section(buf_instance);

		// link instance to interface stack data
		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
			   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
			goto end;
	}

	/* Higher layers are Device.Ethernet.VLANTermination.{i}. */
	uci_foreach_sections("network", "device", s) {
		char *type, *name, *value = NULL;
		dmuci_get_value_by_section_string(s, "type", &type);
		dmuci_get_value_by_section_string(s, "name", &name);
		if (strcmp(type, "untagged") == 0 || (*name != '\0' && !is_vlan_termination_section(name)))
			continue;

		// The higher layer is Device.Ethernet.VLANTermination.{i}.
		layer_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "device", "section_name", section_name(s), "vlan_term_instance", "vlan_term_alias");
		if (*layer_inst == '\0')
			continue;

		snprintf(buf_higherlayer, sizeof(buf_higherlayer), "Device.Ethernet.VLANTermination.%s", layer_inst);

		higheralias = get_alias_by_section("dmmap_network", "device", s, "vlan_term_alias");
		if (*higheralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
			else
				snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", higheralias);

		// The lower layer is Device.Ethernet.Link.{i}.
		char *vid = strchr(name, '.');
		if (vid) *vid = '\0';
		adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cLink%c", dmroot, dm_delim, dm_delim, dm_delim), name, &value);
		if (value == NULL)
			value = "";

		struct uci_section *link_s = NULL;
		get_dmmap_section_of_config_section_eq("dmmap", "link", "device", name, &link_s);
		dmuci_get_value_by_section_string(link_s, "link_instance", &layer_inst);
		dmuci_get_value_by_section_string(link_s, "link_alias", &loweralias);

		snprintf(buf_lowerlayer, sizeof(buf_lowerlayer), "%s", value);
		if (*loweralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

		// fill interface stack data
		intf_stack_data.higherlayer = buf_higherlayer;
		intf_stack_data.lowerlayer = buf_lowerlayer;
		intf_stack_data.higheralias = buf_higheralias;
		intf_stack_data.loweralias = buf_loweralias;

		// create dmmap section
		snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
		dmmap_s = create_dmmap_interface_stack_section(buf_instance);

		// link instance to interface stack data
		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
			   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
			goto end;
	}

	/* Higher layers are Device.Ethernet.Link.{i}. */
	uci_path_foreach_sections(bbfdm, DMMAP, "link", s) {

		// The higher layer is Device.Ethernet.Link.{i}.
		dmuci_get_value_by_section_string(s, "link_instance", &layer_inst);
		if (*layer_inst == '\0')
			continue;

		snprintf(buf_higherlayer, sizeof(buf_higherlayer), "Device.Ethernet.Link.%s", layer_inst);

		dmuci_get_value_by_section_string(s, "link_alias", &higheralias);
		if (*higheralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
			else
				snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", higheralias);


		char *linker, *value = NULL;
		dmuci_get_value_by_section_string(s, "device", &linker);
		char *bridge = strstr(linker, "br-");
		if (bridge) {
			// The lower layer is Device.Bridging.Bridge.{i}.Port.{i}.
			char *int_name;
			dmuci_get_value_by_section_string(s, "section_name", &int_name);
			struct uci_section *dmmap_section, *port;
			get_dmmap_section_of_config_section("dmmap_network", "interface", int_name, &dmmap_section);
			if (dmmap_section != NULL) {
				char *br_inst, *mg;
				dmuci_get_value_by_section_string(dmmap_section, "bridge_instance", &br_inst);
				uci_path_foreach_option_eq(bbfdm, "dmmap_bridge_port", "bridge_port", "br_inst", br_inst, port) {
					dmuci_get_value_by_section_string(port, "management", &mg);
					if (strcmp(mg, "1") == 0) {
						char *device, linker[512] = "";
						dmuci_get_value_by_section_string(port, "device", &device);
						snprintf(linker, sizeof(linker), "br_%s:%s+%s", br_inst, section_name(port), device);
						adm_entry_get_linker_param(dmctx, dm_print_path("%s%cBridging%cBridge%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &value);
						dmuci_get_value_by_section_string(port, "bridge_port_alias", &loweralias);
						dmuci_get_value_by_section_string(port, "bridge_port_instance", &layer_inst);
						break;
					}
				}
			}
		} else {
			// The lower layer is Device.Ethernet.Interface.{i}.
			char *vid = strchr(linker, '.');
			if (vid) *vid = '\0';
			char *macvlan = strchr(linker, '_');
			if (macvlan)
				*macvlan = '\0';
			struct uci_section *eth_port_sect = NULL, *eth_port_dmms = NULL;
			uci_foreach_option_eq("ports", "ethport", "ifname", linker, eth_port_sect) {
				break;
			}
			if (eth_port_sect != NULL) {
				get_dmmap_section_of_config_section_eq("dmmap_ports", "ethport", "section_name", section_name(eth_port_sect), &eth_port_dmms);
				if (eth_port_dmms) {
					dmuci_get_value_by_section_string(eth_port_dmms, "eth_port_alias", &loweralias);
					dmuci_get_value_by_section_string(eth_port_dmms, "eth_port_instance", &layer_inst);
				}
			}
			adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cInterface%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &value);
		}

		if (value == NULL)
			value = "";

		snprintf(buf_lowerlayer, sizeof(buf_lowerlayer), "%s", value);
		if (*loweralias == '\0')
			if (*layer_inst == '\0')
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", layer_inst);
		else
			snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

		// fill interface stack data
		intf_stack_data.higherlayer = buf_higherlayer;
		intf_stack_data.lowerlayer = buf_lowerlayer;
		intf_stack_data.higheralias = buf_higheralias;
		intf_stack_data.loweralias = buf_loweralias;

		// create dmmap section
		snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
		dmmap_s = create_dmmap_interface_stack_section(buf_instance);

		// link instance to interface stack data
		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
			   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
			goto end;
	}

	/* Higher layers are Device.Bridging.Bridge.{i}.Port.{i}.*/
	uci_foreach_sections("network", "interface", s) {
		char *type;
		dmuci_get_value_by_section_string(s, "type", &type);
		if (strcmp(type, "bridge") != 0)
			continue;

		char *br_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_network", "interface", "section_name", section_name(s), "bridge_instance", "bridge_alias");
		if (*br_inst == '\0')
			continue;

		// The higher layer is Device.Bridging.Bridge.{i}.Port.{i}.
		char *bridge_port_inst, *mg_value = NULL,*value = NULL;
		char buf_mngr[64] = {0};
		struct uci_section *port = NULL;
		uci_path_foreach_option_eq(bbfdm, "dmmap_bridge_port", "bridge_port", "br_inst", br_inst, port) {
			char *mg;
			dmuci_get_value_by_section_string(port, "management", &mg);
			if (strcmp(mg, "1") == 0) {
				char *device, linker[512] = {0};
				dmuci_get_value_by_section_string(port, "device", &device);
				snprintf(linker, sizeof(linker), "br_%s:%s+%s", br_inst, section_name(port), device);
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cBridging%cBridge%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &mg_value);
				dmuci_get_value_by_section_string(port, "bridge_port_alias", &higheralias);
				dmuci_get_value_by_section_string(port, "bridge_port_instance", &bridge_port_inst);
				if (*higheralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_mngr, sizeof(buf_mngr), "%s", "");
					else
						snprintf(buf_mngr, sizeof(buf_mngr), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_mngr, sizeof(buf_mngr), "%s", higheralias);

				if (mg_value == NULL)
					mg_value = "";
				break;
			}
		}

		struct uci_section *sd = NULL;
		uci_path_foreach_option_eq(bbfdm, "dmmap_bridge_port", "bridge_port", "br_inst", br_inst, sd) {
			char *mg;
			dmuci_get_value_by_section_string(sd, "management", &mg);
			if (strcmp(mg, "1") == 0)
				continue;

			char *vb = NULL, *device, linker[512] = {0};
			dmuci_get_value_by_section_string(sd, "device", &device);

			// The lower layer is Device.Bridging.Bridge.{i}.Port.{i}.
			snprintf(linker, sizeof(linker), "br_%s:%s+%s", br_inst, section_name(sd), device);
			adm_entry_get_linker_param(dmctx, dm_print_path("%s%cBridging%cBridge%c", dmroot, dm_delim, dm_delim, dm_delim), linker, &vb);

			if (vb == NULL)
				vb = "";

			dmuci_get_value_by_section_string(sd, "bridge_port_alias", &loweralias);
			dmuci_get_value_by_section_string(sd, "bridge_port_instance", &bridge_port_inst);
			if (*loweralias == '\0')
				if (*bridge_port_inst == '\0')
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", bridge_port_inst);
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

			// fill interface stack data
			intf_stack_data.higherlayer = mg_value;
			intf_stack_data.lowerlayer = vb;
			intf_stack_data.higheralias = buf_mngr;
			intf_stack_data.loweralias = buf_loweralias;

			// create dmmap section
			snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
			dmmap_s = create_dmmap_interface_stack_section(buf_instance);

			// link instance to interface stack data
			inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
				   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
				goto end;

			if (*loweralias == '\0')
				if (*bridge_port_inst == '\0')
					snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
				else
					snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", bridge_port_inst);
			else
				snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", loweralias);

			char package[32] = {0};
			int found = 0;
			// The lower layer is Device.Ethernet.Interface.{i}.
			adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cInterface%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
			if (value != NULL) {
				strncpy(package, "ports", sizeof(package) - 1);
				struct uci_section *port_s = NULL;
				uci_foreach_option_eq("ports", "ethport", "ifname", device, port_s) {
					loweralias = get_alias_by_section("dmmap_ports", "ethport", port_s, "eth_port_alias");
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_ports", "ethport", "section_name", section_name(port_s), "eth_port_instance", "eth_port_alias");
					break;
				}
				found = 1;
			}

			// The lower layer is Device.WiFi.SSID.{i}.
			if (!found && value == NULL)
				adm_entry_get_linker_param(dmctx,dm_print_path("%s%cWiFi%cSSID%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);

			if (!found && value != NULL) {
				strncpy(package, "wireless", sizeof(package) - 1);
				struct uci_section *wl_s = NULL;
				uci_foreach_option_eq("wireless", "wifi-iface", "ifname", device, wl_s) {
					loweralias = get_alias_by_section("dmmap_wireless", "wifi-iface", wl_s, "ssidalias");
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_wireless", "wifi-iface", "section_name", section_name(wl_s), "ssidinstance", "ssidalias");
					break;
				}
				found = 1;
			}

			// The lower layer is Device.ATM.Link.{i}.
			if (!found && value == NULL) {
				char *tag = strchr(device, '.');
				if (tag) *tag = '\0';
				adm_entry_get_linker_param(dmctx,dm_print_path("%s%cATM%cLink%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
			}

			if (!found && value != NULL) {
				strncpy(package, "dsl:atm", sizeof(package) - 1);
				struct uci_section *dsl_s = NULL;
				uci_foreach_option_eq("dsl", "atm-device", "device", device, dsl_s) {
					loweralias = get_alias_by_section("dmmap_dsl", "atm-device", dsl_s, "atmlinkalias");
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_dsl", "atm-device", "section_name", section_name(dsl_s), "atmlinkinstance", "atmlinkalias");
					break;
				}
				found = 1;
			}

			// The lower layer is Device.PTM.Link.{i}.
			if (!found && value == NULL) {
				char *tag = strchr(device, '.');
				if (tag) *tag = '\0';
				adm_entry_get_linker_param(dmctx,dm_print_path("%s%cPTM%cLink%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
			}

			if (!found && value != NULL) {
				strncpy(package, "dsl:ptm", sizeof(package) - 1);
				struct uci_section *dsl_s = NULL;
				uci_foreach_option_eq("dsl", "ptm-device", "device", device, dsl_s) {
					loweralias = get_alias_by_section("dmmap_dsl", "ptm-device", dsl_s, "ptmlinkalias");
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_dsl", "ptm-device", "section_name", section_name(dsl_s), "ptmlinkinstance", "ptmlinkalias");
					break;
				}
				found = 1;
			}

			// The lower layer is Device.Ethernet.Interface.{i}.
			if (!found && value == NULL) {
				char *tag = strchr(device, '.');
				if (tag) *tag = '\0';
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cEthernet%cInterface%c", dmroot, dm_delim, dm_delim, dm_delim), device, &value);
			}

			if (!found && value != NULL) {
				strncpy(package, "ports", sizeof(package) - 1);
				struct uci_section *port_s = NULL;
				uci_foreach_option_eq("ports", "ethport", "ifname", device, port_s) {
					loweralias = get_alias_by_section("dmmap_ports", "ethport", port_s, "eth_port_alias");
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_ports", "ethport", "section_name", section_name(port_s), "eth_port_instance", "eth_port_alias");
					break;
				}
			}

			if (*loweralias == '\0')
				if (*bridge_port_inst == '\0')
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", bridge_port_inst);
			else
				snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

			if (value == NULL)
				value = "";

			// fill interface stack data
			intf_stack_data.higherlayer = vb;
			intf_stack_data.lowerlayer = value;
			intf_stack_data.higheralias = buf_higheralias;
			intf_stack_data.loweralias = buf_loweralias;

			// create dmmap section
			snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
			dmmap_s = create_dmmap_interface_stack_section(buf_instance);
			// link instance to interface stack data
			inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
				   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
				goto end;
			// The lower layer is Device.WiFi.Radio.{i}.
			if(strcmp(package, "wireless") == 0) {
				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
					else
						snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", loweralias);
				struct uci_section *wl_s = NULL;
				char *wl_device;
				uci_foreach_option_eq("wireless", "wifi-iface", "ifname", device, wl_s) {
					dmuci_get_value_by_section_string(wl_s, "device", &wl_device);
					break;
				}

				if (wl_device[0] != '\0') {
					adm_entry_get_linker_param(dmctx, dm_print_path("%s%cWiFi%cRadio%c", dmroot, dm_delim, dm_delim, dm_delim), wl_device, &vb);
					struct uci_section *ss = NULL;
					uci_foreach_sections("wireless", "wifi-device", ss) {
						if(strcmp(section_name(ss), device) == 0) {
							loweralias = get_alias_by_section("dmmap_wireless", "wifi-device", ss, "radioalias");
							bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap_wireless", "wifi-device", "section_name", section_name(ss), "radioinstance", "radioalias");
							break;
						}
					}
				}

				if (vb == NULL)
					vb = "";

				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
					else
						snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

				// fill interface stack data
				intf_stack_data.higherlayer = value;
				intf_stack_data.lowerlayer = vb;
				intf_stack_data.higheralias = buf_higheralias;
				intf_stack_data.loweralias = buf_loweralias;

				// create dmmap section
				snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
				dmmap_s = create_dmmap_interface_stack_section(buf_instance);

				// link instance to interface stack data
				inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
					   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

				if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
					goto end;
			}

			// The lower layer is Device.DSL.Channel.{i}.
			if(strcmp(package, "dsl:atm") == 0) {
				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
					else
						snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", loweralias);

				char *link_channel = "channel_0";
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cDSL%cChannel%c", dmroot, dm_delim, dm_delim, dm_delim), link_channel, &vb);
				if (vb == NULL)
					vb = "";

				struct uci_section *dsl_s = NULL;
				uci_path_foreach_sections(bbfdm, "dmmap", "dsl_channel", dsl_s) {
					dmuci_get_value_by_section_string(dsl_s, "dsl_channel_alias", &loweralias);
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap", "dsl_channel", "section_name", section_name(dsl_s), "dsl_channel_instance", "dsl_channel_alias");
				}

				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
					else
						snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

				// fill interface stack data
				intf_stack_data.higherlayer = value;
				intf_stack_data.lowerlayer = vb;
				intf_stack_data.higheralias = buf_higheralias;
				intf_stack_data.loweralias = buf_loweralias;

				// create dmmap section
				snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
				dmmap_s = create_dmmap_interface_stack_section(buf_instance);

				// link instance to interface stack data
				inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
					   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

				if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
					goto end;
			}

			// The lower layer is Device.DSL.Line.{i}.
			if(strcmp(package, "dsl:ptm") == 0) {
				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", "");
					else
						snprintf(buf_higheralias, sizeof(buf_higheralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_higheralias, sizeof(buf_higheralias), "%s", loweralias);

				char *link_line = "line_0";
				adm_entry_get_linker_param(dmctx, dm_print_path("%s%cDSL%cLine%c", dmroot, dm_delim, dm_delim, dm_delim), link_line, &value);
				if (value == NULL)
					value = "";

				struct uci_section *dsl_s = NULL;
				uci_path_foreach_sections(bbfdm, "dmmap", "dsl_line", dsl_s) {
					dmuci_get_value_by_section_string(dsl_s, "dsl_line_alias", &loweralias);
					bridge_port_inst = get_instance_by_section(dmctx->instance_mode, "dmmap", "dsl_line", "id", "0", "dsl_line_instance", "dsl_line_alias");
				}

				if (*loweralias == '\0')
					if (*bridge_port_inst == '\0')
						snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", "");
					else
						snprintf(buf_loweralias, sizeof(buf_loweralias), "cpe-%s", bridge_port_inst);
				else
					snprintf(buf_loweralias, sizeof(buf_loweralias), "%s", loweralias);

				// fill interface stack data
				intf_stack_data.higherlayer = vb;
				intf_stack_data.lowerlayer = value;
				intf_stack_data.higheralias = buf_higheralias;
				intf_stack_data.loweralias = buf_loweralias;

				// create dmmap section
				snprintf(buf_instance, sizeof(buf_instance), "%d", ++instance);
				dmmap_s = create_dmmap_interface_stack_section(buf_instance);

				// link instance to interface stack data
				inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
					   dmmap_s, "interface_stack_instance", "interface_stack_alias", "dmmap_interface_stack", "interface_stack");

				if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&intf_stack_data, inst) == DM_STOP)
					goto end;
			}
		}
	}

end:
	return 0;
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
int get_Device_InterfaceStackNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	int cnt = 0;

	uci_path_foreach_sections(bbfdm, "dmmap_interface_stack", "interface_stack", s) {
		cnt++;
	}
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_InterfaceStack_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	uci_path_foreach_option_eq(bbfdm, "dmmap_interface_stack", "interface_stack", "interface_stack_instance", instance, s) {
		dmuci_get_value_by_section_string(s, "interface_stack_alias", value);
		if ((*value)[0] == '\0')
			dmasprintf(value, "cpe-%s", instance);
	}
	return 0;
}

static int set_InterfaceStack_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *s = NULL;

	switch (action)	{
		case VALUECHECK:
			if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
				return FAULT_9007;
			break;
		case VALUESET:
			uci_path_foreach_option_eq(bbfdm, "dmmap_interface_stack", "interface_stack", "interface_stack_instance", instance, s)
				dmuci_set_value_by_section(s, "interface_stack_alias", value);
			break;
	}
	return 0;
}

static int get_InterfaceStack_HigherLayer(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(((struct interfacestack_data *)data)->higherlayer);
	return 0;
}

static int get_InterfaceStack_LowerLayer(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(((struct interfacestack_data *)data)->lowerlayer);
	return 0;
}

static int get_InterfaceStack_HigherAlias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(((struct interfacestack_data *)data)->higheralias);
	return 0;
}

static int get_InterfaceStack_LowerAlias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(((struct interfacestack_data *)data)->loweralias);
	return 0;
}

/* *** Device.InterfaceStack.{i}. *** */
DMLEAF tInterfaceStackParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"Alias", &DMWRITE, DMT_STRING, get_InterfaceStack_Alias, set_InterfaceStack_Alias, BBFDM_BOTH},
{"HigherLayer", &DMREAD, DMT_STRING, get_InterfaceStack_HigherLayer, NULL, BBFDM_BOTH},
{"LowerLayer", &DMREAD, DMT_STRING, get_InterfaceStack_LowerLayer, NULL, BBFDM_BOTH},
{"HigherAlias", &DMREAD, DMT_STRING, get_InterfaceStack_HigherAlias, NULL, BBFDM_BOTH},
{"LowerAlias", &DMREAD, DMT_STRING, get_InterfaceStack_LowerAlias, NULL, BBFDM_BOTH},
{0}
};
