-- Copyright (c) 2025 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 log = require 'mc.logging'
local context = require 'mc.context'
local skynet = require 'skynet'
local hypercard_chip = {}
hypercard_chip.__index = hypercard_chip
function hypercard_chip.new()
    return setmetatable({}, hypercard_chip)
end

local function string_to_number(str)
    local numbers = {}
    local byte
    if not str then
        return numbers
    end

    for i = 1, #str do
        byte = string.byte(str, i)
        numbers[#numbers + 1] = tonumber(string.format("%d", byte))
    end

    return numbers
end

local function chip_read(chip, read_offset, read_len)
    local retry_count = 3
    for _ = 1, retry_count do
        local ok, recv_data = pcall(function()
            return chip:Read(context.get_context_or_default(), read_offset, read_len)
        end)
        if ok then
            return string_to_number(recv_data)
        end
    end
    return false
end

local function chip_write(chip, write_offset, data)
    local retry_count = 3
    local ok, err
    for _ = 1, retry_count do
        ok, err = pcall(function ()
            return chip:Write(context.get_context_or_default(), write_offset, data)
        end)
        if ok then
            return true
        else
            log:debug('chip write error: %s', err)
        end
    end
    return false
end

local function parse_temp(data)
    local temp
    -- 0xfe表示温度不在位，NA状态；0xfd表示温度在位，但获取失败
    if data == 0xfe or data == 0xfd then
        return data
    end

    -- 最高位为符号位，0代表正数，1代表负数
    if data & 0x80 == 0 then
        temp = data
    else
        temp = 0 - (data & 0x7f)
    end
    -- 根据厂商要求，温度高于127则保持127不变，低于-127则保持-127不变
    if temp > 127 then
        temp = 127
    elseif temp < -127 then
        temp = -127
    end

    return temp
end

-- input为二维数组，input[i]代表从第i个offset读出来的数据表，input[i][j]代表从第i个offset读出来数据表的第j位值
local hypercard_cfg = {
    CPLDProtocolVersion = {
        chip = 'CPLDChip',
        offset = {0x01},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            return {string.format('%s', input[1][1])}
        end
    },
    BOM = {
        chip = 'CPLDChip',
        offset = {0x05},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- BOM的有效位为高4bit
            log:debug('BOM origin data is %s', input[1][1])
            return string.format('0x%x', input[1][1] >> 4)
        end
    },
    LogicVersion = {
        chip = 'CPLDChip',
        offset = {0x06},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- CPLD版本号需转换为V1、V2、V3格式
            log:debug('LogicVersion origin data is %s', input[1][1])
            return string.format('V%s', input[1][1])
        end
    },
    ManagerFirmwareVersion = {
        chip = 'BMCChip',
        offset = {0x23},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- 固件版本号，低6bit为minor version，高2bit为major version
            log:debug('ManagerFirmwareVersion origin data is %s', input[1][1])
            return string.format('%s.%s', input[1][1] >> 6, input[1][1] & 0x3F)
        end
    },
    BIOSVersion = {
        chip = 'BMCChip',
        offset = {0x25},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- 固件版本号，低6bit为minor version，高2bit为major version
            log:debug('BIOSVersion origin data is %s', input[1][1])
            return string.format('%s.%s', input[1][1] >> 6, input[1][1] & 0x3F)
        end
    },
    IMUVersion = {
        chip = 'BMCChip',
        offset = {0x26},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- 固件版本号，低6bit为minor version，高2bit为major version
            log:debug('IMUVersion origin data is %s', input[1][1])
            return string.format('%s.%s', input[1][1] >> 6, input[1][1] & 0x3F)
        end
    },
    PowerState = {
        chip = 'CPLDChip',
        offset = {0x18},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            log:debug('PowerState origin data is %s', input[1][1])
            return input[1][1]
        end
    },
    SetPowerStatus = {
        chip = 'CPLDChip',
        offset = {0x18},
        length = {0x01},
        type = 'Write',
        data = nil,
        handle_func = function(input)
            return input
        end
    },
    MemorySizeGiB = {
        chip = 'BMCChip',
        offset = {0x06},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            return input[1][1]
        end
    },
    DiskCapacityGiB = {
        chip = 'BMCChip',
        offset = {0x07, 0x08},
        length = {0x01, 0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- 偏移7为H，偏移8为L
            return ((input[1][1] << 8) | input[2][1])
        end
    },
    SerialDirection = {
        chip = 'CPLDChip',
        offset = {0x80},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            return input[1][1]
        end
    },
    SetSerialDirection = {
        chip = 'CPLDChip',
        offset = {0x80},
        length = {0x01},
        type = 'Write',
        data = nil,
        handle_func = function(input)
            return input
        end
    },
    PowerWatts = {
        chip = 'BMCChip',
        offset = {0x30},
        length = {0x01},
        type = 'Read',
        data = nil,
        default = 0,
        handle_func = function(input)
            return input[1][1]
        end
    },
    AmbientTemperatureCelsius = {
        chip = 'BMCChip',
        offset = {0x31},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            -- 温度需进行转换
            return parse_temp(input[1][1])
        end
    },
    NetworkAdapterChipTemperatureCelsius = {
        chip = 'BMCChip',
        offset = {0x32},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            return parse_temp(input[1][1])
        end
    },
    NICSFPMaxTemperatureCelsius = {
        chip = 'BMCChip',
        offset = {0x33, 0x34, 0x35, 0x36, 0x37, 0x38},
        length = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            local max_temp = 0xfe
            for _, temp in ipairs(input) do
                -- 仅全部不在位时，返回默认不在位状态
                if parse_temp(temp[1]) == 0xfe then
                    goto continue
                end
                -- 当前最大温度是不在位状态时，修改为异常状态，已记录过正常温度时，不记录异常
                if parse_temp(temp[1]) == 0xfd then
                    if max_temp == 0xfe then
                        max_temp = 0xfd
                    end
                    goto continue
                end
                -- 当前最大温度是不在位状态或者异常时，或者当前温度高于最大温度时，更新最大温度
                if max_temp == 0xfe or max_temp == 0xfd or parse_temp(temp[1]) > max_temp then
                    max_temp = parse_temp(temp[1])
                end
                ::continue::
            end
            return max_temp
        end
    },
    CPLDCheckWrite = {
        chip = 'CPLDChip',
        offset = {0x07},
        length = {0x01},
        type = 'Write',
        data = nil,
        handle_func = function(input)
            return input
        end
    },
    CPLDCheckRead = {
        chip = 'CPLDChip',
        offset = {0x07},
        length = {0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            return input[1][1]
        end
    },
    BMCAlertStatus = {
        chip = 'BMCChip',
        offset = {0x60, 0x61},
        length = {0x01, 0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            local regs = {}
            for i, _ in ipairs(input) do
                table.insert(regs, input[i][1])
            end
            return regs
        end
    },
    DPUAlertStatus = {
        chip = 'BMCChip',
        offset = {0x62, 0x63},
        length = {0x01, 0x01},
        type = 'Read',
        data = nil,
        handle_func = function(input)
            local regs = {}
            for i, _ in ipairs(input) do
                table.insert(regs, input[i][1])
            end
            return regs
        end
    }
}

-- 外部需传入DPUCard对象、待获取/设置的属性、写入数据(如需写入)
function hypercard_chip:CardInfo(obj, property, data)
    log:debug('start to update CardInfo, pro=%s', property)
    if not property or not hypercard_cfg[property] then
        error('the property is not supported')
    end
    local chip = obj[hypercard_cfg[property].chip]
    if not chip or not chip.Read or not chip.Write then
        error(string.format('the chip(%s) is invalid', hypercard_cfg[property].chip))
    end
    local operation = hypercard_cfg[property].type
    local offset = hypercard_cfg[property].offset
    local length = hypercard_cfg[property].length
    if operation == 'Read' then
        local chip_data
        local input = {}
        for i, _ in ipairs(offset) do
            chip_data = chip_read(chip, offset[i], length[i])
            if not chip_data or #chip_data ~= length[i] then
                error(string.format('chip read failed[chip:%s,offset:%s,len:%s]',
                    hypercard_cfg[property].chip, offset[i], length[i]))
            end
            table.insert(input, chip_data)
        end
        return hypercard_cfg[property].handle_func(input)
    elseif operation == 'Write' then
        local ok
        local write_data
        for i, _ in ipairs(offset) do
            if not data or not data[i] then
                error(string.format('chip[%s] write failed, write data is nil', hypercard_cfg[property].chip))
            end
            write_data = data[i]
            ok = chip_write(chip, offset[i], write_data)
            if not ok then
                error(string.format('chip write failed[chip:%s,offset:%s,data:%s]',
                    hypercard_cfg[property].chip, offset[i], data[i]))
            end
            skynet.sleep(1)
        end
        return hypercard_cfg[property].handle_func(ok)
    else
        error('invalid property operation type')
    end
end

return hypercard_chip