-- 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.
--         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 lu = require 'luaunit'
local smbus = require 'protocol.smbus_M88RT51632'
local bs = require 'mc.bitstring'
local log = require 'mc.logging'

local REQUEST_CONTEXT<const> = {
    ['SEND_ADDR'] = {
        pattern = bs.new([[<<
            cmd_code:16/big,
            addr:16/little
        >>]]),
        cmd_code = 514,
        length = 4
    },
    ['SEND_ADDR_ADTA'] = {
        pattern = bs.new([[<<
            cmd_code:16/big,
            addr:16/little,
            data:32/big
        >>]]),
        cmd_code = 1798,
        length = 8
    }
}

local function to_raw(hex_data)
    local i = 0
    return hex_data:gsub('[^ ]+', function (data)
        return string.format('%c', tonumber(data, 16))
    end):gsub('.', function (data)
        i = i + 1
        return (i % 2 == 1) and data or ''
    end)
end

local function to_hex(data)
    return data:gsub('.', function(v)
        return string.format('%02X ', v:byte())
    end)
end


TestSmbusM88RT51632 = {}

function TestSmbusM88RT51632:test_can_create_smbus_M88RT51632()
    local s = smbus.new({})
    lu.assertNotNil(s)
    lu.assertEquals(s.chip, {})
end

local old_getlevel = log.getLevel

local function make_log_level()
    log.getLevel = function()
        return 0xff
    end
end

local function recover_log_level()
    log.getLevel = old_getlevel
end

function TestSmbusM88RT51632:test_create_request()
    make_log_level()

    -- 命令行指令：mdbctl call Chip_Retimer0_010108 bmc.kepler.Chip.BlockIO Write 0 0x40 5 0x82 0x02 0x4c 0x10 212
    local str = smbus:construct_request(0x104c, nil, REQUEST_CONTEXT['SEND_ADDR'], true)
    lu.assertEquals(to_hex(str), '82 02 4C 10 D4 ')
    str = smbus:construct_request(0x104c, nil, REQUEST_CONTEXT['SEND_ADDR'], false)
    lu.assertEquals(to_hex(str), '02 02 4C 10 ')
    str = smbus:construct_request(0x104c, nil, REQUEST_CONTEXT['SEND_ADDR'], false)
    lu.assertEquals(to_hex(str), '02 02 4C 10 ')
    str = smbus:construct_request(0xfff0, 0x3fff0000, REQUEST_CONTEXT['SEND_ADDR_ADTA'], false)
    lu.assertEquals(to_hex(str), '07 06 F0 FF 00 00 FF FC ')
    str = smbus:construct_request(0xfff0, 0x800000, REQUEST_CONTEXT['SEND_ADDR_ADTA'], false)
    lu.assertEquals(to_hex(str), '07 06 F0 FF 00 00 01 00 ')
    str = smbus:construct_request(0xfff0, 0x800000, REQUEST_CONTEXT['SEND_ADDR_ADTA'], true)
    lu.assertEquals(to_hex(str), '87 06 F0 FF 00 00 01 00 03 ')

    recover_log_level()
end

local input = {}
local ref_chip = {
    Write = function(_, _, _, data)
        table.insert(input, data)
        return nil
    end,
    Read = function()
        return '\x07\x4c\x10\x60\x71\x0D\x06'
    end,
    ComboWriteRead = function()
        return '\x07\x4c\x10\x60\x71\x0D\x06'
    end
}

function TestSmbusM88RT51632:test_smbus_write()
    input = {}
    local addr = 0xfff0
    local data = 0x800000
    local use_pec = false
    local s = smbus.new(ref_chip)
    local ok, msg = s:smbus_write(addr, data, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, nil)
    lu.assertEquals(input, {
        '\x07\x06\xF0\xFF\x00\x00\x01\x00',
    })
    input = {}
    local use_pec = true
    local ok, msg = s:smbus_write(addr, data, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, nil)
    lu.assertEquals(input, {
        '\x87\x06\xF0\xFF\x00\x00\x01\x00\x03',
    })

    input = {}
    local ok, msg = s:retimer_write({addr = addr, data = data, use_pec = use_pec})
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, nil)
    lu.assertEquals(input, {
        '\x87\x06\xF0\xFF\x00\x00\x01\x00\x03',
    })
    
    s.smbus_write = function(...)
        return false
    end
    local ok, msg = s:retimer_write({})
    lu.assertEquals(ok, false)
    lu.assertEquals(msg, 'fail to write retimer')
end

function TestSmbusM88RT51632:test_smbus_read_quick()
    input = {}
    local addr = 0x104c
    local use_pec = false
    local s = smbus.new(ref_chip)
    local ok, msg = s:smbus_read_quick(addr, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, '\x07\x4c\x10\x60\x71\x0D\x06')
    lu.assertEquals(input, {
        '\x02\x02\x4C\x10',
    })

    input = {}
    use_pec = true
    s.chip.ComboWriteRead = function()
        return '\x06\x4c\x10\x00\x0D\x71\x60\xA6'
    end
    ok, msg = s:smbus_read_quick(addr, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, '\x06\x4C\x10\x00\x0D\x71\x60') 
end

function TestSmbusM88RT51632:test_retimer_read()
    input = {}
    local args = {
        addr=0x104c,
        use_pec=false,
    }
    local s = smbus.new(ref_chip)
    local ok, msg = s:retimer_read(args)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, 0x60B08E06) -- retimer版本号
    lu.assertEquals(input, {
        '\x02\x02\x4C\x10',
    })
end

function TestSmbusM88RT51632:test_retimer_loop_read()
    local count = 2
    local ref_chip = {
        Write = function()
            return nil
        end,
        ComboWriteRead = function()
            if count ~= 0 then
                count = count - 1
                return '\x07\x18\x0a' .. string.rep('\x00', 4)
            else
                return '\x07\x18\x0a' .. string.rep('\xff', 4)
            end
        end
    }
    local args = {
        addr=0xa18,
        use_pec=false,
    }
    local fun = function(msg)
        return ((msg >> 16) & 0x1) == 1
    end
    local s = smbus.new(ref_chip)
    local ok, msg = s:retimer_loop_read(args, fun)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, 0xffffffff)
end

function TestSmbusM88RT51632:test_parse_response()
    local rawdata = '07 4c 10 60 71 0D 06'
    local ret, data = smbus:parse_response(to_raw(rawdata), 0x104c)
    lu.assertEquals(ret, true)
    lu.assertEquals(data, 0x60B08E06)
    ret, data = smbus:parse_response(to_raw(rawdata), 0xfff0)
    lu.assertEquals(ret, false)
    lu.assertEquals(data, "wrong rsp addr")
end