-- 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.

-- Description: 解析hex文件
local file_sec = require 'utils.file'
local log = require 'mc.logging'

local HEX_FILE_PARSE_ERR = -1
local HEX_FILE_PARSE_CONTINUE = 1
local HEX_FILE_PARSE_NORMALEND = 0
local HEX_RECORD_TYPE_DATA = 0x00
local HEX_RECORD_TYPE_END = 0x01
local HEX_RECORD_LINEARITY_ADDR = 0x04

local function parse_two_hex_chars(fp)
    local ch1 = fp:read(1)
    local ch2 = fp:read(1)
    if ch1 and ch2 then
        return tonumber(ch1:lower(), 16) * 16 + tonumber(ch2:lower(), 16)
    end
    return 0
end

local function hexlinedata_parse(fp, data_table, buf_len)
    local exit_status = HEX_FILE_PARSE_CONTINUE
    local record_data_len = parse_two_hex_chars(fp) -- 前两个字节代表长度
    local check_sum = record_data_len

    local high_byte = parse_two_hex_chars(fp)
    local low_byte = parse_two_hex_chars(fp)
    local addr = (high_byte << 8) + low_byte -- 当前的地址，lua从1开始
    check_sum = check_sum + high_byte + low_byte

    local record_type = parse_two_hex_chars(fp) -- 数据的类型
    check_sum = check_sum + record_type

    if record_type == HEX_RECORD_TYPE_END then
        for _ = 0, record_data_len - 1 do
            check_sum = check_sum + parse_two_hex_chars(fp)
        end
        exit_status = HEX_FILE_PARSE_NORMALEND
    end
    if record_type == HEX_RECORD_TYPE_DATA then
        for _ = 0, record_data_len - 1 do
            local data = parse_two_hex_chars(fp)
            if addr > buf_len then
                exit_status = HEX_FILE_PARSE_ERR
                break
            end
            data_table[addr] = data
            check_sum = check_sum + data
            addr = addr + 1
        end
    end
    if record_type == HEX_RECORD_LINEARITY_ADDR then
        for _ = 0, record_data_len - 1 do
            check_sum = check_sum + parse_two_hex_chars(fp)
        end
    end

    -- 手动截断一个字节
    check_sum = check_sum % 256
    check_sum = (~check_sum + 1) % 256
    if check_sum ~= parse_two_hex_chars(fp) then
        exit_status = HEX_FILE_PARSE_ERR
    end
    return exit_status
end

return function(file, buf_len)
    local fp, err = file_sec.open_s(file, 'rb')
    if not fp then
        log:error('open file failed, err: %s', err)
        return { ret = HEX_FILE_PARSE_ERR }
    end
    local data_table = {}
    local ch
    local ret

    while true do
        while true do
            ch = fp:read(1)
            if ch == ':' then
                break
            end
        end
        ret = hexlinedata_parse(fp, data_table, buf_len)
        if ret ~= HEX_FILE_PARSE_CONTINUE then
            break
        end
    end
    fp:close()

    -- 将表转换为二进制字符串
    local buffer_str
    local byte_array = {}
    for i = 0, #data_table do
        table.insert(byte_array, string.char(data_table[i]))
    end
    buffer_str = table.concat(byte_array)

    return {
        ret = ret,
        real_len = #buffer_str,
        buffer = buffer_str
    }
end