#
# Copyright (c) 2018-2020 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

from libnmstate.error import NmstateNotImplementedError
from libnmstate.error import NmstateValueError
from libnmstate.ifaces.bond import BondIface
from libnmstate.schema import Bond
from libnmstate.state import merge_dict
from .common import NM


BOND_TYPE = "bond"

SYSFS_EMPTY_VALUE = ""

NM_SUPPORTED_BOND_OPTIONS = NM.SettingBond.get_valid_options(
    NM.SettingBond.new()
)

SYSFS_BOND_OPTION_FOLDER_FMT = "/sys/class/net/{ifname}/bonding"

BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC = "00:00:00:00:00:00"


def create_setting(iface, wired_setting, base_con_profile):
    bond_setting = NM.SettingBond.new()
    options = iface.original_dict.get(Bond.CONFIG_SUBTREE, {}).get(
        Bond.OPTIONS_SUBTREE
    )
    mode = iface.bond_mode
    if options != {}:
        if options is None:
            options = {}
        if not iface.is_bond_mode_changed:
            old_bond_options = {}
            if base_con_profile:
                old_bond_options = _get_bond_options_from_profiles(
                    base_con_profile.get_setting_bond()
                )
            merge_dict(options, old_bond_options)
    if mode:
        bond_setting.add_option("mode", mode)
    for option_name, option_value in options.items():
        if wired_setting and BondIface.is_mac_restricted_mode(mode, options):
            # When in MAC restricted mode, MAC address should be unset.
            wired_setting.props.cloned_mac_address = None
        if (
            option_name == "ad_actor_system"
            and option_value == BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC
        ):
            # The all zero ad_actor_system is the kernel default value
            # And it is invalid to set as all zero
            continue
        if option_value != SYSFS_EMPTY_VALUE:
            option_value = _nm_fix_bond_options(option_name, option_value)
            success = bond_setting.add_option(option_name, option_value)
            if not success:
                raise NmstateValueError(
                    "Invalid bond option: '{}'='{}'".format(
                        option_name, option_value
                    )
                )

    return bond_setting


def _nm_fix_bond_options(option_name, option_value):
    if option_name == "all_slaves_active":
        if option_value == "delivered":
            option_value = 1
        elif option_value == "dropped":
            option_value = 0
        else:
            raise NmstateNotImplementedError(
                "Unsupported bond option: '{}'='{}'".format(
                    option_name, option_value
                )
            )
    elif option_name == "use_carrier":
        option_value = 1 if option_value else 0

    return str(option_value)


def _get_bond_options_from_profiles(bond_setting):
    ret = {}
    if bond_setting:
        for i in range(0, bond_setting.get_num_options()):
            name, value = bond_setting.get_option(i)[1:3]
            if name != "mode":
                ret[name] = value
    return ret
