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

-- Description: common sdr interfaces for sensor application.
local bs = require 'mc.bitstring'
local cc = require 'sdr.sdr_const'
local utils = require 'sensor_utils'

local sdr_common = {}
sdr_common.__index = sdr_common

local function generate_sdr_common(obj)
    local sdr_body = {}
    sdr_body[#sdr_body+1] = utils.toc(obj.OwnerId, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.OwnerLun, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.SensorNumber, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityId, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityInstance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Initialization, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Capabilities, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.SensorType, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.ReadingType, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.AssertMask, utils.BITS_16)
    sdr_body[#sdr_body+1] = utils.toc(obj.DeassertMask, utils.BITS_16)
    return sdr_body
end

function sdr_common.generate_sdr_header(id)
    local sdr_header = {}
    sdr_header.valid_flag = 1
    sdr_header.repo_type = cc.LOCAL_SDR
    sdr_header.device_id = id or 0
    return sdr_header
end

function sdr_common.generate_threshold_sdr(obj)
    local sdr_body = generate_sdr_common(obj)
    sdr_body[#sdr_body+1] = utils.toc(obj.ReadingMask, utils.BITS_16)
    sdr_body[#sdr_body+1] = utils.toc(obj.Unit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.BaseUnit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.ModifierUnit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Linearization, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.M, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.MT, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.B, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.BA, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Accuracy, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.RBExp, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Analog, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.NominalReading, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.NormalMaximum, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.NormalMinimum, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.MaximumReading, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.MinimumReading, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.UpperNonrecoverable, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.UpperCritical, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.UpperNoncritical, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.LowerNonrecoverable, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.LowerCritical, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.LowerNoncritical, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.PositiveHysteresis, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.NegativeHysteresis, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- oem
    sdr_body[#sdr_body+1] = utils.toc(0xC0 + #obj.SensorName, utils.BITS_8)
    sdr_body[#sdr_body+1] = obj.SensorName
    return sdr_body
end

function sdr_common.generate_threshold(obj, record_id)
    local sdr_record = {}
    sdr_record[#sdr_record+1] = utils.toc(record_id, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(0, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_VERSION, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_FULL, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_FULL_LEN + #obj.SensorName, utils.BITS_8)
    sdr_record[#sdr_record+1] = table.concat(sdr_common.generate_threshold_sdr(obj), '')
    sdr_record = table.concat(sdr_record, '')

    return sdr_common.generate_sdr_header(), sdr_record
end

function sdr_common.generate_discrete_sdr(obj)
    local sdr_body = generate_sdr_common(obj)
    sdr_body[#sdr_body+1] = utils.toc(obj.DiscreteMask, utils.BITS_16)
    sdr_body[#sdr_body+1] = utils.toc(obj.Unit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.BaseUnit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.ModifierUnit, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.RecordSharing, utils.BITS_16)
    sdr_body[#sdr_body+1] = utils.toc(obj.PositiveHysteresis, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.NegativeHysteresis, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- oem
    sdr_body[#sdr_body+1] = utils.toc(0xC0 + #obj.SensorName, utils.BITS_8)
    sdr_body[#sdr_body+1] = obj.SensorName
    return sdr_body
end

function sdr_common.generate_discrete(obj, record_id)
    local sdr_record = {}
    sdr_record[#sdr_record+1] = utils.toc(record_id, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(0, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_VERSION, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_COMPACT, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_COMPACT_LEN + #obj.SensorName, utils.BITS_8)
    sdr_record[#sdr_record+1] = table.concat(sdr_common.generate_discrete_sdr(obj), '')
    sdr_record = table.concat(sdr_record, '')

    return sdr_common.generate_sdr_header(), sdr_record
end

function sdr_common.generate_mcdl_sdr(obj)
    local sdr_body = {}
    sdr_body[#sdr_body+1] = utils.toc(obj.SlaveAddr, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.PowerStateInitialization, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Capabilities, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- reserved
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityId, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityInstance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8) -- oem
    sdr_body[#sdr_body+1] = utils.toc(0xC0 + #obj.DeviceName, utils.BITS_8)
    sdr_body[#sdr_body+1] = obj.DeviceName
    return sdr_body
end

function sdr_common.generate_mcdl(obj, record_id)
    local sdr_record = {}
    sdr_record[#sdr_record+1] = utils.toc(record_id, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(0, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_VERSION, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_MCDL, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_MCDL_LEN + #obj.DeviceName, utils.BITS_8)
    sdr_record[#sdr_record+1] = table.concat(sdr_common.generate_mcdl_sdr(obj), '')
    sdr_record = table.concat(sdr_record, '')

    return sdr_common.generate_sdr_header(), sdr_record
end

function sdr_common.generate_dea_sdr(obj)
    local sdr_body = {}
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityId, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.EntityInstance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Address, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Flags, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Address1, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel1, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity1Id, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity1Instance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Address2, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel2, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity2Id, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity2Instance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Address3, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel3, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity3Id, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity3Instance, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Address4, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Channel4, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity4Id, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(obj.Entity4Instance, utils.BITS_8)
    return sdr_body
end

function sdr_common.generate_dea(obj, record_id)
    local sdr_record = {}
    sdr_record[#sdr_record+1] = utils.toc(record_id, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(0, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_VERSION, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_DEA, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_DEA_LEN, utils.BITS_8)
    sdr_record[#sdr_record+1] = table.concat(sdr_common.generate_dea_sdr(obj), '')
    sdr_record = table.concat(sdr_record, '')

    return sdr_common.generate_sdr_header(), sdr_record
end

local bs_logical_device = bs.new('<<bus:3,lun:2,reserved:2,device:1>>')

function sdr_common.generate_fru_sdr(id, name)
    local sdr_body = {}
    local logical_device = string.unpack('I1', bs_logical_device:pack({ bus = 0, lun = 0, reserved = 0, device = 1 }))

    sdr_body[#sdr_body+1] = utils.toc(0x20, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(id, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(logical_device, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0x10, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0x02, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(0, utils.BITS_8)
    sdr_body[#sdr_body+1] = utils.toc(16, utils.BITS_8)
    sdr_body[#sdr_body+1] = name
    return sdr_body
end

function sdr_common.generate_fru(id, name, record_id)
    local sdr_record = {}
    sdr_record[#sdr_record+1] = utils.toc(record_id, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(0, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_VERSION, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_FDL, utils.BITS_8)
    sdr_record[#sdr_record+1] = utils.toc(cc.SDR_TYPE_FDL_LEN + #name, utils.BITS_8)
    sdr_record[#sdr_record+1] = table.concat(sdr_common.generate_fru_sdr(id, name), '')
    sdr_record = table.concat(sdr_record, '')

    return sdr_common.generate_sdr_header(id), sdr_record
end

function sdr_common.get_sdr_oper_support()
    local support = cc.SDR_OP_SDR_MODAL
    support = support | cc.SDR_OP_PARTIAL_ADD_SDR
    support = support | cc.SDR_OP_RESERVE_SDR_REPOSITORY
    support = support | cc.SDR_OP_GET_SDR_REPOSITORY_ALLOCATION
    return support
end


return sdr_common