-- 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 log = require 'mc.logging'
local fw_def = require 'device_tree.adapters.power_mgmt.protocol.upgrade.fw_def'
local dev_object_manage = require 'mc.mdb.device_tree.dev_object_manage'
local signal = require 'signal'
local file_sec = require 'utils.file'
local utils = require 'device_tree.adapters.power_mgmt.utils'
local c_tasks = require 'mc.orm.tasks'
local crc16 = require 'mc.crc16'

local E_OK<const> = nil -- 函数执行成功返回nil
local E_FAILED<const> = '' -- 空错误信息
local FRAME_DATA_LEN <const> = 0x08
local canbus_upgrade = class()

function canbus_upgrade:ctor(call_back, para_tab)
    self.call_back = call_back
    self.para_tab = para_tab
end

-- 从bin包获取固件信息
function canbus_upgrade:read_canbus_bin_file(file_path)
    if #file_path > fw_def.PATH_MAX then
        log:error('file path is invalid.')
        return nil, nil, E_FAILED
    end
    local file = file_sec.open_s(file_path, 'a+')
    if not file then
        log:error('open bin file failed.')
        return nil, nil, E_FAILED
    end
    return utils.safe_close_file(file, function()
        local data = file:read('a')
        local len = file:seek('end')
        return data, len, E_OK
    end)
end

-- 下发bin文件
function canbus_upgrade:start_canbus_download(file_path)
    -- 读取bin文件
    local data, len, ret = self:read_canbus_bin_file(file_path)
    if ret ~= E_OK then
        log:error("get soft load info from file failed")
        return E_FAILED
    end
    local total_crc = self:calc_crc16_table(data)
    local total_frame_num = (len + FRAME_DATA_LEN - 1) // FRAME_DATA_LEN -- 向上取整
    local recv_data = self:send_download_start_frame(total_crc, total_frame_num)
    if recv_data == nil then
        log:error('send_download_start_frame failed')
        return E_FAILED
    end
    c_tasks.get_instance():sleep_ms(20)  -- 延时20ms
    ret = self:load_canbus_soft_bin_data_to_chip(data, len)
    if ret ~= E_OK then
        log:error('load_canbus_soft_bin_data_to_chip failed')
        return E_FAILED
    end
    recv_data = self:send_download_end_frame(total_crc, total_frame_num)
    if recv_data == nil then
        log:error('send_download_end_frame failed')
        return E_FAILED
    end
    return E_OK
end

function canbus_upgrade:cal_percent_by_count(app_count)
    local app_percent = 0
    if app_count == 0 then
        log:warn('module num is zero!')
    else
        app_percent = 1 / app_count
        log:debug('app upgrade progress : %d.', app_percent)
    end
    return app_percent
end

function canbus_upgrade:process(upgrade_path)
    local ret
    local app_percent = self:cal_percent_by_count(self:canbus_get_upgrade_num())
    local file_path
    local canbus_upgrade_bin_name = self:canbus_get_upgrade_bin_table()
    for _, file_name in ipairs(canbus_upgrade_bin_name) do
        local cur_percent = self.call_back:get_comp_percent()
        file_path = upgrade_path .. file_name
        ret = self:canbus_upload_file_check(file_path, file_name)
        if ret ~= E_OK then 
            ret = E_OK --避免文件不存在或者是不需要加载多元化厂家导致加载失败
            goto continue
        end
        log:notice('start_canbus_download file_path %s', file_path)
        for retry = 1, 3 do  -- 重试3次
            ret = self:start_canbus_download(file_path)
            if ret == E_OK then
                log:notice('start_canbus_download successed')
                break
            end
            log:notice('start_canbus_download failed, retry %d', retry)
        end
        if ret ~= E_OK then
            log:notice('upgrade_by_canbus failed')
            break
        end
        self.call_back:set_comp_percent(cur_percent + app_percent, self.para_tab)
        ::continue::
    end
    self.call_back:set_comp_percent(self.call_back:get_step_progress(), self.para_tab)
    if ret ~= E_OK then
        error(fw_def.SUPPLY_1)
    end
    return fw_def.SUPPLY_0
end

return function (protocol_obj)
    return setmetatable(canbus_upgrade, {__index = protocol_obj})
end