-- 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 = require 'protocol_open.common'
local hw_communicator = require 'protocol_open.transport.hw_communicator'

local protocol_open = {}

-- 空对象，用于返回不存在的属性函数
local empty_retrieve_obj<const> = {
    value = function()
    end,
    on_data_change = {
        on = function()
        end
    },
    on_error = {
        on = function()
        end
    },
    start = function()
    end,
    update_params = function()
    end,
    set_period = function()
    end,
    deconstruct = function()
    end
}

local function get_protocol(protocol_name)
    return require('protocol_open.protocol.' .. protocol_name)
end

-- config_request 配置表中的request表，用于存储常量信息：opcode等
-- runtime_request 运行时传入的request表，用于存储动态信息：port_id，data等
-- config_request会覆盖runtime_request，保证request常量不被更改
local function combine_request(config_request, runtime_request)
    local result = {}

    if runtime_request ~= nil then
        for key, v in pairs(runtime_request) do
            result[key] = v
        end
    end

    for key, v in pairs(config_request) do
        result[key] = v
    end

    return result
end

local function create_obj_func(name, props, protocol_dependencies, hwc)
    if not props.action or not hw_communicator[props.action] then
        log:raise('unsupported action: %s', props.action)
    end
    if props.action == 'on_schedule' and props.period_in_sec < 0 then
        log:raise('cannot have on_schedule task with period_in_sec < 0')
    end
    if not hwc.protocols[props.protocol] then
        local p = get_protocol(props.protocol)
        hwc.protocols[props.protocol] = p.new(protocol_dependencies[props.protocol])
    end
    if not hwc.protocols[props.protocol]:validate_request_params(props.request) then
        log:raise('unable to validate request template')
    end

    log:debug('creating obj func for %s', name)
    return function(obj, request_data)
        local protocol = props.protocol
        if request_data ~= nil and
            not obj.hwc.protocols[protocol]:validate_request_params(request_data) then
            log:error('unable to validate request_data when accessing property: %s', name)
            return nil
        end
        local request = combine_request(props.request, request_data)
        local params = {
            name = name,
            protocol = protocol,
            request = request,
            period_in_sec = props.period_in_sec
        }
        return obj.hwc[props.action](obj.hwc, params, props.response)
    end
end

local function validate_chip_config(chip_config)
    if not chip_config or not next(chip_config) then
        return false, 'unable to create device spec parser with empty config'
    end
    if not chip_config.properties or not chip_config.protocol_dependencies then
        return false,
            'unable to create device spec parser with no properties or no protocol dependencies'
    end
    return true
end

function protocol_open.device_spec_parser(chip_config)
    local ok, msg = validate_chip_config(chip_config)
    if not ok then
        log:raise(msg)
        return
    end
    local obj = {}
    local hwc = hw_communicator.new()
    local return_obj = setmetatable({hwc = hwc}, {
        __index = function(_, name)
            if obj[name] then
                return obj[name]
            end
            return function()
                return empty_retrieve_obj
            end
        end
    })

    for k, v in pairs(chip_config.properties) do
        if not chip_config.protocol_dependencies[v.protocol] then
            log:raise('no protocol info in the config file. protocol: %s', v.protocol)
        elseif obj[k] ~= nil then
            log:warn('duplicated config for property %s. will not override', k)
        else
            obj[k] = create_obj_func(k, v, chip_config.protocol_dependencies, hwc)
        end
    end
    return return_obj
end

function protocol_open.create_array_parser(obj_meta, obj_length)
    if not obj_meta or #obj_meta == 0 then
        log:raise('cannot create parser without obj_meta')
    end

    if obj_length <= 0 then
        log:raise('cannot create parser with obj_length <= 0')
    end

    return function(data)
        if #data < obj_length then
            log:error('response length(%s) is less than template length(%s)', #data, obj_length)
            return nil
        end
        local rsp_template = bs.new(obj_meta, protocol_open.common_bs_helper)
        local parse_time = #data / obj_length
        local result = {}
        for i = 1, parse_time do
            local start_offset = 1 + obj_length * (i - 1)
            local end_offset = obj_length * i
            result[#result + 1] = rsp_template:unpack(data:sub(start_offset, end_offset), true)
        end
        return result
    end
end

-- 将common里面的函数对外暴露
for k, v in pairs(common) do
    protocol_open[k] = v
end

return protocol_open
