-- 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 mcu_object_base = require 'mcu.mcu_object'
local subcomp = require 'mcu.upgrade.sub_component'
local MCU_ENUMS = require 'mcu.enum.mcu_enums'
local cmn = require 'common'
local file_sec = require 'utils.file'
local utils = require 'mc.utils'
local json = require 'cjson'

local smc_mcu_object = class(mcu_object_base)

local VRD_DUMP_FILE_PATH<const> = '/data/var/log/vrd/'

function smc_mcu_object:init()
    self.LibMaxSize = 0
    self.BinMaxSize = 0
    self.vrd_dump = {}
    self:get_vrd_log_in_init()
end

function smc_mcu_object:set_lib_max_size(lib_max_size)
    self.LibMaxSize = lib_max_size
end

function smc_mcu_object:set_bin_max_size(bin_max_size)
    self.BinMaxSize = bin_max_size
end

function smc_mcu_object:get_lib_max_size()
    return self.LibMaxSize
end

function smc_mcu_object:get_bin_max_size()
    return self.BinMaxSize
end

function smc_mcu_object:get_firmeware_info(index)
    return self.interface:get_upgrade_detail(index)
end

local function get_per_vrd_dump_data(mcu_id, vrd_dump, total_count)
    local vrd_data = {}
    for i = 1, #vrd_dump // total_count do
        local vrd_num = 'Vrd' .. string.format("%-3s", i)
        local value = {}
        for j = 1, total_count do
            value[j] = ' ' .. string.format('%02X', vrd_dump[(i - 1) * total_count + j])
        end
        local data
        local ok,err = pcall(function()
            data = string.format('%-20s | %-4s | %s\n', mcu_id, vrd_num, table.concat(value))
        end)

        if not ok then
            log:error('string.format error: %s', err)
            data = ''
        end
        vrd_data[i] = data
    end
    vrd_data = table.concat(vrd_data)
    return vrd_data
end

function smc_mcu_object:get_vrd_log_in_init()
    local ok, file_data = pcall(function()
        return self:get_vrd_log()
    end)
    if not ok then
        log:error('get vrd log error: %s', file_data)
    end
    if file_data then
        for _, data in pairs(file_data) do
            table.insert(self.vrd_dump, data)
        end
    end
end

function smc_mcu_object:get_vrd_log()
    local file_name = VRD_DUMP_FILE_PATH .. self.mcu.Id
    local file, err = file_sec.open_s(file_name, "r")
    if not file then
        log:error('open %s failed, err: %s', file_name, err)
        return
    end
    local print_data = utils.close(file, pcall(file.read, file, 'a'))
    if print_data == '' then
        log:error('failed to read data from %s', file_name)
        return
    else
        log:info('Data read from file: %s', print_data)
    end

    return json.decode(print_data)
end

local MAX_DUMP_NUM = 10
function smc_mcu_object:get_vrd_dump_info(mcu_id)
    local vrd_dump_per_time = ''
    local sec = os.time()
    local time = os.date('log time:%Y-%m-%d %H:%M:%S', sec)

    vrd_dump_per_time = vrd_dump_per_time .. time .. '\n'
    local head
    head = string.format('%-20s | %-4s | %s\n', 'Id', 'VrdNum', 'Value')
    vrd_dump_per_time = vrd_dump_per_time .. head
    local total_dump_info , total_count = self.interface:get_vrd_dump()
    if total_dump_info and #total_dump_info ~= 0 then
        vrd_dump_per_time = vrd_dump_per_time .. get_per_vrd_dump_data(mcu_id, total_dump_info, total_count) .. '\n'
    end
    
    if self.vrd_dump and #self.vrd_dump == MAX_DUMP_NUM then
        table.remove(self.vrd_dump,1)
    end
    if not self.vrd_dump then 
        self.vrd_dump = {}
    end
    table.insert(self.vrd_dump, vrd_dump_per_time)

    utils.mkdir_with_parents(VRD_DUMP_FILE_PATH, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)
    local file_name = VRD_DUMP_FILE_PATH .. self.mcu.Id
    local file, err = file_sec.open_s(file_name, 'wb+')
    if not file then
        log:error('open vrd dump file failed, err: %s', err)
        return
    end
    utils.close(file, pcall(file.write, file, json.encode(self.vrd_dump)))
    return
end

function smc_mcu_object:register_firmware_inventory(bus)
    -- 查询子组件数量
    local cnt, lib_max_size, bin_max_size = self.interface:get_upgrade_cnt()
    log:notice('[McuUpgrade]get smc firmware cnt: %s', cnt)
    if not cnt or not lib_max_size or not bin_max_size then
        log:warn('get SMC sub-component count fail')
    end

    self:set_lib_max_size(lib_max_size)
    self:set_bin_max_size(bin_max_size)
    if cnt then
        -- 索引从0开始,查询子件详细信息并向FirmwareInventory注册, 不包括MCU和VRD子组件
        self.sub_component_info_list = {}
        for i = 0, cnt - 1 do
            local detail = self.interface:get_upgrade_detail(i)
            if not detail then
                goto continue
            end
            table.insert(self.sub_component_info_list, detail)
            local component = subcomp.new(i, detail)

            if not component or next(component) == nil then
                goto continue
            end

            component:set_ref_mcu(self)
            self.super.insert_subcomp_detail(self, component)
            -- 将支持的MCU子件对象注册到Firmware
            if component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_MCU and
                component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_VRD and
                MCU_ENUMS.SUB_COMPONENT_TYPE_TABLE[component.Type] then
                component:register_component_firmware_info(bus)
            end

            ::continue::
        end
        self.vrd_info_changed:emit(self.sub_component_info_list)
    end
    -- 将vrd子件对象注册到Firmware
    subcomp.register_vrd_firmware_info(self, bus)

    local component = self.mcu.SubCompList[1]
    if component and next(component) ~= nil then
        if component.Type ~= MCU_ENUMS.SUB_COMPONENT_TYPE.T_MCU or component.No ~= 0 then
            log:warn('the first sub-component is not MCU')
        else
            self.super.set_mcu_major_version(self, component.MajorVersion)
            self.super.set_mcu_minor_version(self, component.MinorVersion)
            self.super.set_mcu_revision(self, component.Revision)
            self.super.register_mcu_firmware_inventory(self, bus)
        end
    end
    self.vrd_load = true
    log:notice('[McuUpgrade] (%s)register SMC version to FirmwareInventory done', self.mcu.BoardType)
end

return smc_mcu_object