-- 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 c_exu = require 'unit_manager.class.unit.exu.exu'
local cmn = require 'common'
local log = require 'mc.logging'
local smc = require 'protocol.smc'
local bs = require "mc.bitstring"

---@class ExpBoard: EXU @扩展板
local c_exp_board = class(c_exu)

local UNAVAILABLE<const> = 'N/A'

local function logic_version_id_to_version(logic_version_id)
    logic_version_id = logic_version_id & 0xff
    return string.format('%u.%02u', logic_version_id >> 4, logic_version_id & 0xf) -- 高4位和低4位拼装成版本号
end

function c_exp_board:update_logic_version(multi_logic_version)
    if not multi_logic_version.CPLD1 then
        local ok, version_number = pcall(function()
            return self:get_prop('LogicVersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local cpld1_version_str = logic_version_id_to_version(version_number)
            self:set_prop('LogicVersion', cpld1_version_str)
            multi_logic_version.CPLD1 = cpld1_version_str
        end
    end

    if not multi_logic_version.CPLD2 then
        local ok, version_number = pcall(function()
            return self:get_prop('CPLD2VersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local cpld2_version_str = logic_version_id_to_version(version_number)
            multi_logic_version.CPLD2 = cpld2_version_str
        end
    end

    if not multi_logic_version.FPGA then
        local ok, version_number = pcall(function()
            return self:get_prop('FPGAVersionID')
        end)
        if ok and version_number and version_number ~= 0 then
            local fpga_version_str = logic_version_id_to_version(version_number)
            multi_logic_version.FPGA = fpga_version_str
        end
    end
end

function c_exp_board:task_update_logic_version()
    if self:get_prop('LogicVersion') == UNAVAILABLE then
        return
    end
    cmn.skynet.fork(function()
        local multi_logic_version = {}
        local timeout = 120
        local mds_obj = self:get_mds_obj()
        repeat
            self:update_logic_version(multi_logic_version)
            self:set_prop('MultiLogicVersion', multi_logic_version)
            cmn.skynet.sleep(500)
            timeout = timeout - 5
        until (multi_logic_version.CPLD1 and multi_logic_version.CPLD2 and multi_logic_version.FPGA) or timeout < 0
        if mds_obj then
            log:notice("Get %s%s CPLD1Version = %s, CPLD2Version = %s, FPGAVersion = %s, timeout = %s",
                mds_obj.BoardType, mds_obj.Slot, multi_logic_version.CPLD1,
                multi_logic_version.CPLD2, multi_logic_version.FPGA, timeout)
        end
    end)
end

local rtc_req = bs.new([[<<
    year:8,
    month:8,
    wek:8,
    day:8,
    hour:8,
    minute:8,
    seconds:8,
    verify:8
>>]])
local weekMap = {1, 2, 4, 8, 16, 32, 64}  -- 周日到周六
-- func=0x0Fh, command=0x10h, ms=1, rw=0, param=0, data_len=8
local SET_EXP_TIME_CMD <const> = 0x3c004200
function c_exp_board:task_update_exp_board_time()
    local utc_time = os.time() - os.difftime(os.time(), os.time(os.date('!*t', os.time())))
    -- 年 月 日 时 分 秒 需要转换成BCD码值 计算公式 (dec+(dec/10)*6)
    local data = rtc_req:pack({
        year = os.date('%y', utc_time) + os.date('%y', utc_time) // 10 * 6,
        month = os.date('%m', utc_time) + os.date('%m', utc_time) // 10 * 6,
        wek = weekMap[os.date('%w', utc_time) + 1] or 1,
        day = os.date('%d', utc_time) + os.date('%d', utc_time) // 10 * 6,
        hour = os.date('%H', utc_time) + os.date('%H', utc_time) // 10 * 6,
        minute = os.date('%M', utc_time) + os.date('%M', utc_time) // 10 * 6,
        seconds = os.date('%S', utc_time) + os.date('%S', utc_time) // 10 * 6,
        verify = 90 -- 0x5a 校验位
    })

    log:debug('update %s time, year: %d, month: %d, day: %d, wek: %d, hour: %d, minute: %d, seconds: %d',
        self:get_prop('DeviceName'), os.date('%y', utc_time), os.date('%m', utc_time), os.date('%d', utc_time),
        os.date('%w', utc_time), os.date('%H', utc_time), os.date('%M', utc_time), os.date('%S', utc_time))

    local chip = self:get_prop('RefSMCChip')
    local ok, err = pcall(smc.chip_blkwrite, chip, SET_EXP_TIME_CMD, data)
    if not ok then
        log:info('update %s time failed, error : %s', self:get_prop('DeviceName'), err)
        return
    end
end

return c_exp_board