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

-- eeprom 模型
-- 支持offsetwidth: 1, 2, 4
-- 数据大小：任意
-- 支持生成指定大小的数据文件，默认size为1024
local Eeprom = {
    offset_width = 1,
    size = 1024
}

local log = require "log"
local cjson = require "cjson"
local CommonI2c = require "protocol.CommonI2c"
local BinParser = require "data.BinParser"
local eeprom_data = {
    [0xb8] = {
        [0x20] = { 0x14, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x21] = { 0xcc, 0xc4, 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x31] = { 225, 16, 145, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x78] = { 0x00, 236, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x79] = { 0x00, 16, 236, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7a] = { 0x00, 58, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7b] = { 0x00, 81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7c] = { 0x00, 71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7d] = { 0x00, 44, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7e] = { 0x00, 145, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x80] = { 240, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x81] = { 0x00, 186, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x88] = { 203, 249, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x89] = { 0x39, 185, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x8b] = { 243, 196, 202, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x8c] = { 0x2a, 0xf0, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x96] = { 5, 242, 187, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x97] = { 50, 242, 63, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x98] = { 34, 204, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x99] = { 6, 72, 85, 65, 87, 69, 73, 175, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x9a] = { 12, 80, 65, 67, 57, 48, 48, 83, 49, 50, 45, 66, 50, 122, 0xff, 0xff, 0xff },
        [0x9b] = { 1, 65, 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x9e] = { 20, 50, 49, 48, 50, 51, 49, 50, 88, 87, 75, 49, 48, 78, 56, 49, 49, 50, 56, 54, 54, 227, 0xff },
        [0xde] = { 8, 48, 50, 51, 49, 50, 88, 87, 75, 7, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xdf] = { 1, 0, 2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xce] = { 0, 64, 161, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xe4] = { 8, 1, 137, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xe7] = { 8, 1, 161, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
    },
    [0xba] = {
        [0x20] = { 0x14, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x21] = { 204, 196, 129, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x31] = { 225, 16, 131, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x78] = { 0x00, 234, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x79] = { 0x00, 16, 254, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7a] = { 0x00, 60, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7b] = { 0x00, 87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7c] = { 0x00, 65, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7d] = { 0x00, 42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x7e] = { 0x00, 151, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x80] = { 240, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x81] = { 0x00, 188, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x88] = { 189, 249, 220, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x89] = { 0x47, 185, 198, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x8b] = { 13, 197, 29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x8c] = { 0x28, 0xf0, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x96] = { 5, 242, 232, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x97] = { 50, 242, 127, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x98] = { 34, 204, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x99] = { 6, 72, 85, 65, 87, 69, 73, 89, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x9a] = { 12, 80, 65, 67, 57, 48, 48, 83, 49, 50, 45, 66, 50, 179, 0xff, 0xff, 0xff },
        [0x9b] = { 1, 65, 19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0x9e] = { 20, 50, 49, 48, 50, 51, 49, 50, 88, 87, 75, 49, 48, 78, 55, 49, 50, 51, 55, 51, 54, 68, 0xff },
        [0xde] = { 8, 48, 50, 51, 49, 50, 88, 87, 75, 109, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xdf] = { 1, 0, 16, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xce] = { 0, 64, 179, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xe4] = { 8, 1, 137, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
        [0xe7] = { 8, 1, 179, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
    }
}

for i = 1, 0xff do
    if not eeprom_data[0xb8][i] then
        eeprom_data[0xb8][i] = {0xff,0xff,0xff,0xff,0xff}
    end
    if not eeprom_data[0xba][i] then
        eeprom_data[0xba][i] = {0xff,0xff,0xff,0xff,0xff}
    end
end

local CRC8Lookup = {
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
}

local function crc(bytes)
    local crc8 = 0x00
    for i = 1, #bytes do
        local index = string.byte(bytes, i, i) ~ crc8  -- 这个就是异或运算
        crc8 = CRC8Lookup[index + 1]  -- lua的table下标寻址是从1 开始的。
    end
    return crc8
end

function Eeprom:new(o, offset_width, size)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    self.offset_width = offset_width
    self.size = size
    return o
end

local EepromParser = {
    prot_parser = {},
    data_parser = {},
}

function EepromParser:new(o, prot_parser, data_parser)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    self.prot_parser = prot_parser
    self.data_parser = data_parser
    return o
end

function EepromParser:read_data(length)
    local offset = self.prot_parser:parse_offset()
    return self.data_parser:read_data(offset, length)
end

function EepromParser:write_data()
    local offset = self.prot_parser:parse_offset()
    local data = self.prot_parser:parse_data()

    log:print(LOG_ERROR, "eeprom write_data offset is %d", offset)
    log:print(LOG_ERROR, "eeprom write_data data is %s", cjson.encode(data))

    self.data_parser:write_data(offset, data)
end

local function write_data(filename, data)
    local file = io.open(filename, "w+")
    if file == nil then
        print("write_file open file fail")
        return
    end
    file:write(data)
    file:close()
end

local function refresh_data(filename, address)
    local file = io.open(filename, "r")
    if file == nil then
        write_data(filename, cjson.encode(eeprom_data[address]))
    else
        local str = file:read("*a")
        eeprom_data[address] = cjson.decode(str)
        file:close()
    end
end

function Eeprom:read(tx_buffer, length, filename)
    refresh_data(filename .. '.json', tx_buffer[1])
    local _read_data = {}
    for i = 1, length do
        table.insert(_read_data, eeprom_data[tx_buffer[1]][tx_buffer[2]][i])
    end
    return _read_data
end

function Eeprom:write(tx_buffer, filename)
    refresh_data(filename .. '.json', tx_buffer[1])
    local check_buf = table.concat({
        string.char(tx_buffer[1]), string.char(tx_buffer[2]),
        string.char(tx_buffer[1] | 0x01),
        string.pack(string.rep('B', #tx_buffer - 3),
        table.unpack(tx_buffer, 3, #tx_buffer - 1))
    })
    tx_buffer[#tx_buffer] = crc(check_buf)

    for i = 3, #tx_buffer do
        eeprom_data[tx_buffer[1]][tx_buffer[2]][i - 2] = tx_buffer[i]
    end
    write_data(filename .. '.json', cjson.encode(eeprom_data[tx_buffer[1]]))
end

return Eeprom
