/*
 * 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 "ethernet.h"
#include "dmlayer.h"

#ifdef ETHMNGR_MACVLAN_EXTENSION
/*************************************************************
* ENTRY METHOD
**************************************************************/
int browseEthernetMACVLANInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);
	char *inst = NULL;

	synchronize_specific_config_sections_with_dmmap_eq("network", "device", "dmmap_network", "type", "macvlan", &dup_list);
	list_for_each_entry(p, &dup_list, list) {

		inst = handle_instance(dmctx, parent_node, p->dmmap_section, "mac_vlan_instance", "mac_vlan_alias");

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, inst) == DM_STOP)
			break;
	}
	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
int addObjEthernetMACVLAN(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *s = NULL, *dmmap_network = NULL;
	char device_name[32];

	snprintf(device_name, sizeof(device_name), "mac_vlan_%s", *instance);

	// Add device section
	dmuci_add_section("network", "device", &s);
	dmuci_rename_section_by_section(s, device_name);
	dmuci_set_value_by_section(s, "type", "macvlan");

	// Add device section in dmmap_network file
	dmuci_add_section_bbfdm("dmmap_network", "device", &dmmap_network);
	dmuci_set_value_by_section(dmmap_network, "section_name", device_name);
	dmuci_set_value_by_section(dmmap_network, "mac_vlan_instance", *instance);
	return 0;
}

int delObjEthernetMACVLAN(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	char *name = NULL;

	switch (del_action) {
	case DEL_INST:
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);

		// Update Ethernet MAC VLAN Top Layers
		ethernet___Update_MAC_VLAN_Top_Layers(refparam, name, "");

		// Remove device section
		dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);

		// Remove device section in dmmap_network file
		dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
		break;
	case DEL_ALL:
		break;
	}
	return 0;
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_EthernetMACVLAN_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, "enabled", "1");
	return 0;
}

static int set_EthernetMACVLAN_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, "enabled", b ? "1" : "0");
			break;
	}
	return 0;
}

static int get_EthernetMACVLAN_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *device = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &device);
	return get_net_device_status(device, value);
}

static int get_EthernetMACVLAN_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "mac_vlan_alias", instance, value);
}

static int set_EthernetMACVLAN_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "mac_vlan_alias", instance, value);
}

static int get_EthernetMACVLAN_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", value);
	return 0;
}

static int get_EthernetMACVLAN_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *linker = NULL;
	char buf[4096] = {0};

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "ifname", &linker);
	if (!linker || *linker == '\0')
		return 0;

	bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.VLANTermination.", "Name", linker, buf, sizeof(buf));
	bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.Link.", "Name", linker, buf, sizeof(buf));

	// Store LowerLayers value
	*value = dmstrdup(buf);

	return 0;
}

static int set_EthernetMACVLAN_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {
			"Device.Ethernet.VLANTermination.",
			"Device.Ethernet.Link.",
			NULL};
	struct dm_reference reference = {0};
	char *curr_name = NULL;
	char name[32] = {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;

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

			break;
		case VALUESET:
			// Store LowerLayers value under dmmap section
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", reference.path);

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &curr_name);

			// Update ifname option
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ifname", reference.value);

			if (DM_STRLEN(reference.value)) {
				char *dev_name = ethernet___get_ethernet_interface_name(reference.value);

				snprintf(name, sizeof(name), "%s_%s", dev_name, instance);
			}

			// Update name option
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "name", name);

			// Update Ethernet MAC VLAN Top Layers
			ethernet___Update_MAC_VLAN_Top_Layers(refparam, curr_name, name);
			break;
	}
	return 0;
}

static int get_EthernetMACVLAN_MACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "macaddr", value);
	return 0;
}

static int set_EthernetMACVLAN_MACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 17, NULL, MACAddress))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "macaddr", value);
			break;
	}
	return 0;
}

static int eth_macvlan_sysfs(const struct uci_section *data, const char *name, char **value)
{
	char *device;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &device);
	return get_net_device_sysfs(device, name, value);
}

static int get_EthernetMACVLANStats_BytesSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/tx_bytes", value);
}

static int get_EthernetMACVLANStats_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/rx_bytes", value);
}

static int get_EthernetMACVLANStats_PacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/tx_packets", value);
}

static int get_EthernetMACVLANStats_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/rx_packets", value);
}

static int get_EthernetMACVLANStats_ErrorsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/tx_errors", value);
}

static int get_EthernetMACVLANStats_ErrorsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/rx_errors", value);
}

static int get_EthernetMACVLANStats_DiscardPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/tx_dropped", value);
}

static int get_EthernetMACVLANStats_DiscardPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/rx_dropped", value);
}

static int get_EthernetMACVLANStats_MulticastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_macvlan_sysfs(data, "statistics/multicast", value);
}

int get_Ethernet_macVlanNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseEthernetMACVLANInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
DMLEAF tEthernetMACVLANParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_EthernetMACVLAN_Enable, set_EthernetMACVLAN_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_EthernetMACVLAN_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_EthernetMACVLAN_Alias, set_EthernetMACVLAN_Alias, BBFDM_BOTH},
{"Name", &DMREAD, DMT_STRING, get_EthernetMACVLAN_Name, NULL, BBFDM_BOTH, DM_FLAG_LINKER},
{"LowerLayers", &DMWRITE, DMT_STRING, get_EthernetMACVLAN_LowerLayers, set_EthernetMACVLAN_LowerLayers, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"MACAddress", &DMWRITE, DMT_STRING, get_EthernetMACVLAN_MACAddress, set_EthernetMACVLAN_MACAddress, BBFDM_BOTH},
{0}
};

DMOBJ tEthernetMACVLANObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tEthernetMACVLANStatsParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tEthernetMACVLANStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"BytesSent", &DMREAD, DMT_UNLONG, get_EthernetMACVLANStats_BytesSent, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNLONG, get_EthernetMACVLANStats_BytesReceived, NULL, BBFDM_BOTH},
{"PacketsSent", &DMREAD, DMT_UNLONG, get_EthernetMACVLANStats_PacketsSent, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetMACVLANStats_PacketsReceived, NULL, BBFDM_BOTH},
{"ErrorsSent", &DMREAD, DMT_UNINT, get_EthernetMACVLANStats_ErrorsSent, NULL, BBFDM_BOTH},
{"ErrorsReceived", &DMREAD, DMT_UNINT, get_EthernetMACVLANStats_ErrorsReceived, NULL, BBFDM_BOTH},
{"DiscardPacketsSent", &DMREAD, DMT_UNINT, get_EthernetMACVLANStats_DiscardPacketsSent, NULL, BBFDM_BOTH},
{"DiscardPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetMACVLANStats_DiscardPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetMACVLANStats_MulticastPacketsReceived, NULL, BBFDM_BOTH},
{0}
};

#endif
