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

local class = require 'mc.class'
local debug = require 'domain.debug.debug'
local defs = require 'domain.defs'
local log = require 'mc.logging'
local monitor = require 'domain.debug.monitor'
local custom_messages = require 'messages.custom'

local rpc = class()

function rpc:register_methods(app_base)
    app_base:ImplLanSwitchMockLanSwitchMockRegRead(function(obj, ctx, ...)
        return self:read_debug(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockRegWrite(function(obj, ctx, ...)
        return self:write_debug(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockGetPortInfo(function(obj, ctx, ...)
        return self:get_port_info(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockSetPortEnable(function(obj, ctx, ...)
        return self:set_port_enable(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockSetPortStpState(function(obj, ctx, ...)
        return self:set_port_stp_state(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockGetSwitchErrorCode(function(obj, ctx, ...)
        return self:get_switch_error_code(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockSetPortMacAddr(function(obj, ctx, ...)
        return self:set_port_mac_addr(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockFlushPortAddr(function(obj, ctx, ...)
        return self:flush_port_addr(...)
    end)
    app_base:ImplLanSwitchMockLanSwitchMockGetVlanInfo(function(obj, ctx, ...)
        return self:get_vlan_info(...)
    end)
    app_base:ImplLanSwitchLanSwitchLswReset(function(obj, ctx, ...)
        return self:reset_lan_switch(ctx, ...)
    end)
    app_base:ImplLanSwitchLanSwitchGetConfigInfo(function(obj, ctx, ...)
        return self:get_config_info()
    end)
end

function rpc:call_lsw(name, ...)
    local mgr = debug.get_instance()
    local ok, res = pcall(mgr.op_reg, mgr, name, ...)
    if not ok or not res then
        log:error('[lsw]call %s fail, err:%s', name, res)
        error(res)
    end
    return res
end

local PORT_TYPE<const> = 0
function rpc:read_debug(unit, type, port, addr)
    if type == PORT_TYPE then
        return self:call_lsw(defs.DEBUG_FUNC.PORT_REG_READ, unit, port, addr)
    end
    return self:call_lsw(defs.DEBUG_FUNC.CHIP_REG_READ, unit, addr)
end

function rpc:write_debug(unit, type, port, addr, value)
    if type == PORT_TYPE then
        return self:call_lsw(defs.DEBUG_FUNC.PORT_REG_WRITE, unit, port, addr, value)
    end
    return self:call_lsw(defs.DEBUG_FUNC.CHIP_REG_WRITE, unit, addr, value)
end

function rpc:get_port_dynamic_info()
    local switch_collections = self.mgt.switch_collection
    for _, switch in pairs(switch_collections) do
        return switch:get_port_dynamic_info()
    end
end

function rpc:get_port_info(unit, port)
    local enable, state, mac_info, l2_addr, msg
    local ok, err = pcall(function()
        enable = self:call_lsw(defs.DEBUG_FUNC.GET_PORT_ENABLE, unit, port)
        state = self:call_lsw(defs.DEBUG_FUNC.GET_PORT_STP_STATE, unit, port)
        mac_info = table.concat(self:call_lsw(defs.DEBUG_FUNC.GET_PORT_MAC_INFO, unit, port))
        l2_addr = table.concat(self:call_lsw(defs.DEBUG_FUNC.GET_PORT_L2_ADDR, unit, port))
        msg = string.format('Port enable:%s, PortState:%s, DynamicInfo:%s, MacInfo:%s, L2Addr:%s',
            enable, state, self:get_port_dynamic_info(), mac_info, l2_addr)
    end)

    if not ok then
        log:error('[lsw] get port info failed. err: %s', err)
    end

    return msg
end

function rpc:set_port_enable(unit, port, enable)
    return self:call_lsw(defs.DEBUG_FUNC.SET_PORT_ENABLE, unit, port, enable)
end

function rpc:set_port_stp_state(unit, port, state)
    return self:call_lsw(defs.DEBUG_FUNC.SET_PORT_STP_STATE, unit, port, state)
end

function rpc:get_switch_error_code(unit, utp_num)
    return self:call_lsw(defs.DEBUG_FUNC.GET_SWITCH_ERROR_CODE, unit, utp_num)
end

function rpc:set_port_mac_addr(unit, port, mac_str)
    return self:call_lsw(defs.DEBUG_FUNC.SET_PORT_MAC_ADDR, unit, port, mac_str)
end

function rpc:flush_port_addr(unit, port)
    return self:call_lsw(defs.DEBUG_FUNC.FLUSH_PORT_ADDR, unit, port)
end

function rpc:get_vlan_info(unit)
    local switch = self.mgt.switch_collection[unit]
    if not switch then
        log:error("[lsw] Unit(%u) not exist.")
        error(custom_messages.ValueOutOfRange('unit'))
    end

    return switch:get_vlans_info()
end

function rpc:reset_lan_switch(ctx, device, type)
    local switch_collections = self.mgt.switch_collection
    local ok, _ = pcall(function()
        for _, switch in pairs(switch_collections) do
            if type == defs.RESET_TYPE.HARD_RESET then
                switch:hard_reset_with_device(device)
            elseif type == defs.RESET_TYPE.SOFT_RESET then
                switch:soft_reset_with_device(device)
            end
        end
    end)

    if ok then
        log:operation(ctx:get_initiator(), 'LSW', "Reset LSW successfully")
    else
        log:operation(ctx:get_initiator(), 'LSW', "Reset LSW failed")
    end
end

function rpc:get_config_info()
    local mtr = monitor.get_instance()
    return mtr:get_config_state()
end

function rpc:ctor(app_base, mgt)
    self.app_base = app_base
    self.mgt = mgt
end

function rpc:init()
    self:register_methods(self.app_base)
end

return rpc