-- Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
-- 
-- this file licensed under the 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 bs = require 'mc.bitstring'
local log = require 'mc.logging'
local common_bs_helper = require 'protocol_open.common.bs_helper'

local lldpdu_tlv_packet_max_len<const> = 1500
local lldp_max_tlv_num<const> = 64
local manufacture_id<const> = 0x07DB
local rsp_cmd_type<const> = 0x0
local rsp_sub_command<const> = 0x0

local response_bs<const> = bs.new([[<< 
    manufacture_id:32/big,
    cmd_type:8,
    sub_command:8,
    _:8,
    port_id:8,
    dest_addr:1/MAC_ADDRESS,
    src_addr:1/MAC_ADDRESS,
    ether_type:16/big,
    data/string
>>]], common_bs_helper)

local tlv_header_bs<const> = bs.new([[<<
    info_string_high:1,
    type:7,
    info_string_low:8
>>]])
local tlv_header_len<const> = 2

local tlv_type_tlv_length_map<const> = {
    [0] = {min = 0, max = 0},
    [1] = {min = 2, max = 256},
    [2] = {min = 2, max = 256},
    [3] = {min = 2, max = 2},
    [4] = {min = 0, max = 255},
    [5] = {min = 0, max = 255},
    [6] = {min = 0, max = 255},
    [7] = {min = 4, max = 4},
    [8] = {min = 9, max = 167},
    [127] = {min = 4, max = 511}
}

local function check_tlv_type_and_tlv_length(tlv_type, tlv_length)

    local check = tlv_type_tlv_length_map[tlv_type]
    if check then
        return tlv_length >= check.min and tlv_length <= check.max
    else
        return tlv_type >= 9 and tlv_type <= 126 -- 9到126是reserved字段，暂时不判断
    end
end

local function validate_rsp_data(rsp)
    if not rsp or not rsp.data or #rsp.data == 0 then
        error('empty data received')
    end

    if rsp.manufacture_id ~= manufacture_id or rsp.cmd_type ~= rsp_cmd_type or rsp.sub_command ~=
        rsp_sub_command then
        error('response header mismatch')
    end

    if #rsp.data > lldpdu_tlv_packet_max_len then
        error(string.format('received data exceeds maximum data len! max: %d, actual: %d',
            lldpdu_tlv_packet_max_len, #rsp.data))
    end
end

local function vdpci_lldp_parser(rsp_bin)
    local rsp = response_bs:unpack(rsp_bin, true)
    validate_rsp_data(rsp)

    local rsp_data_len = #rsp.data
    -- LLDPDU 总长度不应该超过1500 octets
    local tlvs = {}
    local offset = 1
    local is_complete = false
    repeat
        if #tlvs >= lldp_max_tlv_num then
            error(string.format('received tlvs exceeds maximum tlvs count! max: %d',
                lldp_max_tlv_num))
        end

        if offset >= rsp_data_len then
            error('did not receive end tlv')
        end

        local tlv_header = tlv_header_bs:unpack(rsp.data:sub(offset, offset + tlv_header_len - 1),
            true)
        if not tlv_header then
            error('tlv header is nil')
        end
        local tlv_info_string_len = tlv_header.info_string_high << 8 | tlv_header.info_string_low

        if not check_tlv_type_and_tlv_length(tlv_header.type, tlv_info_string_len) then
            error(string.format('invalid tlv_type: %d and tlv_length: %d', tlv_header.type,
                tlv_info_string_len))
        end
        offset = offset + tlv_header_len
        local tlv = {
            tlv_type = tlv_header.type,
            tlv_info_string_len = tlv_info_string_len,
            tlv_info_string = tlv_info_string_len > 0 and
                rsp.data:sub(offset, offset + tlv_info_string_len - 1) or ''
        }
        tlvs[#tlvs + 1] = tlv
        offset = offset + tlv_info_string_len
        local index = #tlvs -- 计算当前tlv是第几个，判断tlv_type是否合理
        -- 前三个LLDPDU TLV必须是type 1, 2, 3, 代表chassis_id, port_id, time_to_live
        if index <= 3 and tlv.tlv_type ~= index then
            error('invalid tlv response: wrong tlv type in the first 3 tlvs')
        end
        -- 最后一个TLV必须是End LLDPDU TLV: tlv_type == 0
        is_complete = tlv.tlv_type == 0
    until is_complete

    return {
        port_id = rsp.port_id,
        dest_addr = rsp.dest_addr.mac_address,
        src_addr = rsp.src_addr.mac_address,
        ether_type = rsp.ether_type,
        tlvs = tlvs
    }
end

return vdpci_lldp_parser
