-- 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
--         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 REQ_SEND_ADDR_CCODE<const> = 0x02
local REQ_SEND_DATA_CCODE<const> = 0x07

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

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

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

local input = {}
local ref_chip = {
    Address = 64,
    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
}

TestSmbusM88RT51632 = {}

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

function TestSmbusM88RT51632:test_create_request()
    local s = smbus.new(ref_chip)

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

function TestSmbusM88RT51632:test_smbus_write()
    input = {}
    local addr = 0xfff0
    local data = 0x800000
    local use_pec = false
    local s = smbus.new(ref_chip)
    s.chip_plugin_write = function(self, use_pec, send_data, write_cmd)
        table.insert(input, send_data)
        return true
    end
    local ok, msg = s:smbus_write(addr, data, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(msg, nil)
    lu.assertEquals(input, {
        '\x06\xF0\xFF\x00\x00\x01\x00',
    })

    input = {}
    s.chip_plugin_write = function(self, use_pec, send_data, write_cmd)
        table.insert(input, send_data .. "\x03")
        return true
    end
    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, {
        '\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, {
        '\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)

    s.chip_plugin_read = function(self, use_pec, send_data, write_cmd, read_cmd, read_length)
        if use_pec then  
            read_length = read_length - 1
        end
        table.insert(input, string.char(write_cmd) .. send_data)
        local ret = string.char(read_length - 1) .. string.sub(send_data, 2, -1) .. '\x60\x71\x0D\x06'
        if use_pec then
            ret = ret .. "\xA6"
        end
        return true, ret
    end

    local ok, msg = s:smbus_read_quick(addr, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(input, {'\x02\x02\x4C\x10'})
    lu.assertEquals(msg, '\x06\x4c\x10\x60\x71\x0D\x06')

    input = {}
    use_pec = true
    ok, msg = s:smbus_read_quick(addr, use_pec)
    lu.assertEquals(ok, true)
    lu.assertEquals(input, {'\x82\x02\x4C\x10'})
    lu.assertEquals(msg, '\x06\x4C\x10\x60\x71\x0D\x06\xA6') 
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