-- 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 log = require 'mc.logging'
local bs = require 'mc.bitstring'

local card_asset_data = {}

local BUSINESSPORT_CARD_TYPE_VIR<const> = 6
local BUSINESSPORT_CARD_TYPE_TEAM<const> = 7
local BUSINESSPORT_CARD_TYPE_BRIDGE<const> = 8

local function is_virtual_port(type)
    if type == BUSINESSPORT_CARD_TYPE_VIR or
       type == BUSINESSPORT_CARD_TYPE_BRIDGE or
       type == BUSINESSPORT_CARD_TYPE_TEAM then
        return true
    end
    return false
end

local function register_asset_listen_callback(orm_obj)
    orm_obj:listen('Name', function(_, value)
        orm_obj.AssetName = value
    end)

    orm_obj:listen('SerialNumber', function(_, value)
        orm_obj.InventorySerialNumber = value
    end)

    orm_obj:listen('FirmwareVersion', function(_, value)
        orm_obj.InventoryFirmwareVersion = value
    end)

    orm_obj:listen('PCBVersion', function(_, value)
        orm_obj.InventoryPCBVersion = value
    end)

    orm_obj:listen('Manufacturer', function(_, value)
        orm_obj.InventoryManufacturer = value
    end)

    orm_obj:listen('SlotNumber', function(_, value)
        orm_obj.Slot = tostring(value)
    end)
end

-- 十六进制字符转ascii字符
local function hex_to_ascii(mac)
    local hex = string.gsub(mac, ":", "")
    -- 非法mac地址置零
    if string.find(hex, "[^%x]") then
        return "\0\0\0\0\0\0"
    end
    local str = {}
    local byte
    -- 十六进制字符转换
    for i = 1, #hex, 2 do
        byte = string.sub(hex, i, i + 1)
        table.insert(str, string.char(tonumber(byte, 16)))
    end
    return table.concat(str)
end

local function get_port_mac_hex(port)
    local str = "\0\0\0\0\0\0"
    if not port then
        return str
    end

    local mac = port.PermanentMACAddress
    -- 非法mac地址置零
    if mac and #mac == 17 then
        str = hex_to_ascii(mac)
    end
    return str
end

-- ascii字符转十六进制字符
local function str_to_hex(str)
    local hex = {}
    for i = 1, #str do
        table.insert(hex,string.format("%02X", string.byte(str, i)))
    end
    return table.concat(hex)
end

local function regenerate_uuid_str(pack_string)
    local partten_mac = bs.new([[<<
        custom_a:48/big,
        custom_b:4,
        custom_c:4,
        custom_d:4,
        custom_e:4,
        custom_f:56/big,
        custom_g:8/big>>]])
    local uuid_part = partten_mac:unpack(pack_string)
    local uuid_param = {
        custom_a = uuid_part.custom_a,
        custom_b = uuid_part.custom_c,
        ver = 8,
        custom_c = uuid_part.custom_e,
        custom_d = uuid_part.custom_b,
        custom_e = uuid_part.custom_d,
        var = 8,
        custom_f = uuid_part.custom_f
    }
    local partten_uuid = bs.new([[<<
        custom_a:48/big,
        custom_b:4,
        ver:4,
        custom_c:4,
        custom_d:4,
        custom_e:4,
        var:4,
        custom_f:56/big>>]])
    return partten_uuid:pack(uuid_param)
end

local function get_uuid_pack_str(ports)
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    local mac1 = get_port_mac_hex(ports[1])
    local mac2 = get_port_mac_hex(ports[2])
    local mac3 = get_port_mac_hex(ports[3])
    local mac4 = get_port_mac_hex(ports[4])
    -- 网口个数小于等于2个时，取两个网口的mac共96位，剩余32位用0补齐
    -- 网口个数大于2个，取第一个网口的mac（48位），后三个网口的低24位共120位，剩余8位用0补齐
    if #ports <= 2 then
        return string.pack("<c6c6c4", mac1, mac2, string.rep('\0', 4))
    end
    return string.pack("<c6c3c3c3c1", mac1, string.sub(mac2, 4, 6),
                string.sub(mac3, 4, 6), string.sub(mac4, 4, 6), '\0')
end

-- 通过mac生成网卡资产信息的uuid
local function get_uuid_by_mac(orm_obj)
    local ports = orm_obj.children
    if ports == nil or #ports == 0 then
        return ""
    end
    local uuid_pack_str = get_uuid_pack_str(ports)
    local uuid_str = regenerate_uuid_str(uuid_pack_str)
    local uuid = str_to_hex(uuid_str)
    return uuid
end

function card_asset_data.init_asset_data_info(orm_obj)
    if is_virtual_port(orm_obj.Type) then
        return
    end
    register_asset_listen_callback(orm_obj)
    log:notice("start init asset data info")
    orm_obj.AssetType = 'NetworkAdapter'
    orm_obj.AssetName = orm_obj.Name
    orm_obj.InventorySerialNumber = orm_obj.SerialNumber
    orm_obj.InventoryFirmwareVersion = orm_obj.FirmwareVersion
    orm_obj.InventoryPCBVersion = orm_obj.PCBVersion
    orm_obj.InventoryManufacturer = orm_obj.Manufacturer
    orm_obj.AssetTag = 'N/A'
    orm_obj.PartNumber = 'N/A'
    orm_obj.ManufactureDate = 'N/A'
    orm_obj.Slot = tostring(orm_obj.SlotNumber)
    orm_obj.UUID = ''
end

function card_asset_data.update_asset_uuid_info(orm_obj)
    if is_virtual_port(orm_obj.Type) then
        return
    end
    orm_obj.UUID = get_uuid_by_mac(orm_obj)
end

return card_asset_data