-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local c_handler_base = require 'bma.handles.handler_base'
local c_handler_eth = require 'bma.handles.handler_eth'
local c_vlan = require 'device.class.vlan'
local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local skynet = require 'skynet'

local get_oem_huawei = c_handler_base.get_oem_huawei
local prepare_ipv4_address = c_handler_base.prepare_ipv4_address
local prepare_ipv6_address = c_handler_base.prepare_ipv6_address

-- 没有文档显示 1-7 的取值，所以只实现 0、1 两种
-- VLANPriority有以下取值：0、1、2、3、4、5、6、7。未启用时为0
local function get_vlan_priority(v)
    if not v or v == 'Disable' then
        return 0
    end
    return 1
end

local function get_vlan_is_onboot(v)
    if v == 'yes' then
        return true
    elseif v == 'no' then
        return false
    else
        log:error('get vlan is onboot failed')
        return false
    end
end

local c_handler_vlan = class(c_handler_base)

function c_handler_vlan:ctor()
end

function c_handler_vlan:init()
    self:regist_odata_type('VLanNetworkInterface')

    self:regist_class_type(c_vlan)

    c_handler_eth.get_instance().on_need_update:on(function(object, path)
        self:match_resource(object, path)
    end)
end

function c_handler_vlan:find_object(path, data)
    local port_bma_id = path:match('^(.+)/VLANs/.+$')
    local port = c_handler_eth.get_instance():get_object(port_bma_id)
    if not port then
        return nil
    end
    local obj = c_vlan.collection:find({
        PortID = port.PortID,
        NetworkAdapterId = port.NetworkAdapterId,
        VLANId = data.VLANId
    })
    if not obj then
        local oem_huawei = get_oem_huawei(data) or {}
        c_vlan.insert_or_update(port.PortID, port.NetworkAdapterId, port.na_object_name,
            port.np_object_name, {
                VLANId = data.VLANId,
                VLANEnable = data.VLANEnable,
                VLANPriority = get_vlan_priority(oem_huawei.VLANPriority),
                AutoConnectOnBoot = get_vlan_is_onboot(oem_huawei.OnBoot)
            }):save()

        -- 等待vlan创建成功
        skynet.sleep(10)
        obj = c_vlan.collection:find({
            PortID = port.PortID,
            NetworkAdapterId = port.NetworkAdapterId,
            VLANId = data.VLANId
        })
    end
    return obj
end

function c_handler_vlan:update_vlan(obj, data, oem_huawei)
    -- 更新OS侧网口IPv4信息
    if data.IPv4Addresses ~= nil then
        obj:update_ipv4_addresses(prepare_ipv4_address(data.IPv4Addresses))
    end

    -- 更新OS侧网口IPv6信息
    if data.IPv6Addresses ~= nil then
        obj:update_ipv6_addresses(prepare_ipv6_address(data.IPv6Addresses))
    end

    if oem_huawei.VLANPriority ~= nil then
        obj.VLANPriority = get_vlan_priority(oem_huawei.VLANPriority)
    end

    if oem_huawei.OnBoot ~= nil then
        obj.AutoConnectOnBoot = get_vlan_is_onboot(oem_huawei.OnBoot)
    end

    if data.VLANEnable ~= nil then
        obj.VLANEnabled = data.VLANEnable
    end
end

function c_handler_vlan:add(path, data, object)
    local oem_huawei = get_oem_huawei(data) or {}
    self:update_vlan(path, object, data, oem_huawei)
end

function c_handler_vlan:update(path, data, object)
    local oem_huawei = get_oem_huawei(data) or {}
    self:update_vlan(object, data, oem_huawei)
end

function c_handler_vlan:reset()
    for object, _ in pairs(self.objects) do
        if object.__class_name == c_vlan.__class_name then
            object:reset()
            c_vlan.collection:del_object(object)
        end
    end
    self.objects = {}
    self.resources = {}
end

function c_handler_vlan:delete(_, _, object)
    object:reset()
    c_vlan.collection:del_object(object)
    self.objects[object] = nil
end

return singleton(c_handler_vlan)
