-- 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: 眼图测试
local eye_diagrame = {}

local log = require 'mc.logging'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local msg = require 'general_hardware.ipmi.ipmi_message'
local client = require 'general_hardware.client'
local json = require 'cjson'
local context = require 'mc.context'
local cmn = require 'common'

local EYE_DIAGRAME_TYPE_TABLE<const> = {
    [0] = 'PCIe0',
    [1] = 'PCIe1',
    [2] = 'GMAC2',
    [3] = 'GMAC3',
    [4] = 'USB'
}

local EYE_DIAGRAME_OPTIONS_ENUM<const> = {
    START_TEST = 0x0,
    GET_RESULT = 0x1
}

local EYE_DIAGRAME_OPTIONS<const> = {
    [0] = 0x0,
    [1] = 0x1
}

local SCOPE_SEL_TABLE<const> = {
    [0] = 'before AFE sampling',
    [1] = 'after AFE sampling'
}

local EYE_DIAGRAME_RESULT<const> = {
    TEST_WIDTH = 'test_width',
    REAL_WIDTH = 'real_width',
    TEST_HEIGHT = 'test_height',
    REAL_HEIGHT = 'real_height'
}

local EYE_DIAGRAME_DATA_TYPE_TABLE<const> = {
    [0] = EYE_DIAGRAME_RESULT.TEST_WIDTH,
    [1] = EYE_DIAGRAME_RESULT.REAL_WIDTH,
    [2] = EYE_DIAGRAME_RESULT.TEST_HEIGHT,
    [3] = EYE_DIAGRAME_RESULT.REAL_HEIGHT
}

local TASK_STATUS<const> = {
    INIT = 'init',
    STARTED = 'started',
    FINISHED = 'finished'
}

local eye_diagram_result = false
local task_status = TASK_STATUS.INIT

local function start_test(type, scope_sel, compare_value)
    if task_status ~= TASK_STATUS.INIT then
        log:notice('[general_hardware]task started, status: %s', task_status)
        return msg.GetEyeDiagramRsp.new(comp_code.Success, 0, '')
    end

    local ret, ok, row, col, obj
    local manager_id = 1
    ok, obj = pcall(client.GetBmcDfxEyeDiagramObject, client, {ManagerId = manager_id})
    if not ok or not obj then
        log:error('rpc call GetEyeDiagram has error: %s', obj)
        return msg.GetEyeDiagramRsp.new(comp_code.InvalidFieldRequest, 0, '')
    end

    task_status = TASK_STATUS.STARTED
    cmn.skynet.fork(function()
        ok, row, col = obj.pcall:StartTest(context.new(), type, scope_sel)
        log:notice("[general_hardware] start test get eye diagrame, row: %s, col: %s", row, col)
        if not ok then
            log:error('rpc call StartTest has error, error: %s', row)
            task_status = TASK_STATUS.INIT
            return msg.GetEyeDiagramRsp.new(comp_code.InvalidFieldRequest, 0, '')
        end
        ok, ret = obj.pcall:GetResult(context.new(), row, col, compare_value)
        if not ok then
            task_status = TASK_STATUS.INIT
            log:error('rpc call GetResult has error: %s', ret)
            return msg.GetEyeDiagramRsp.new(comp_code.InvalidFieldRequest, 0, '')
        end
        eye_diagram_result = ret
        task_status = TASK_STATUS.FINISHED
    end)

    return msg.GetEyeDiagramRsp.new(comp_code.Success, 0, '')
end

local function get_result(data_type)
    if not data_type or not EYE_DIAGRAME_DATA_TYPE_TABLE[data_type] then
        log:error('[general_hardware]DataType[%s] is nil', data_type)
        return msg.GetEyeDiagramRsp.new(comp_code.ParmOutOfRange, 0,'')
    end

    if task_status == TASK_STATUS.INIT or task_status == TASK_STATUS.STARTED then
        log:notice('[general_hardware]cannot get result, status: %s', task_status)
        return msg.GetEyeDiagramRsp.new(comp_code.DataNotAvailable, 0, '')
    end

    local ok, result_json = pcall(function()
        return json.decode(eye_diagram_result)
    end)
    eye_diagram_result = false
    task_status = TASK_STATUS.INIT
    if not ok or not result_json then
        log:error("[general_hardware]eye diagrame json: json format error, result_json: %s", result_json)
        return msg.GetEyeDiagramRsp.new(comp_code.InvalidFieldRequest, 0, '')
    end

    log:notice("[general_hardware]eye diagrame t_h: %s, r_h: %s, t_w: %s, r_w: %s",
        result_json[EYE_DIAGRAME_RESULT.TEST_HEIGHT], result_json[EYE_DIAGRAME_RESULT.REAL_HEIGHT],
        result_json[EYE_DIAGRAME_RESULT.TEST_WIDTH], result_json[EYE_DIAGRAME_RESULT.REAL_WIDTH])
    local result = tostring(result_json[EYE_DIAGRAME_DATA_TYPE_TABLE[data_type]])
    if not result then
        log:error('[general_hardware]DataType[%s] is nil', data_type)
        return msg.GetEyeDiagramRsp.new(comp_code.InvalidFieldRequest, 0, '')
    end

    local length = #result
    return msg.GetEyeDiagramRsp.new(comp_code.Success, length, result)
end

function eye_diagrame.get_eye_diagrame_result(req, ctx)
    log:notice("[general_hardware] eye diagrame Option: %s, type: %s, ScopeSel: %s, CompareValue: %s",
        req.Option, req.DeviceType, req.ScopeSel, req.CompareValue)

    local ret = msg.GetEyeDiagramRsp.new(comp_code.ParmOutOfRange, 0, '')
    if not req.Option or not EYE_DIAGRAME_OPTIONS[req.Option] then
        log:error('[general_hardware]Option[%s] is illegal', req.Option)
        return ret
    end

    if not req.ScopeSel or not EYE_DIAGRAME_TYPE_TABLE[req.ScopeSel] then
        log:error('[general_hardware]ScopeSel[%s] is illegal', req.ScopeSel)
        return ret
    end

    if not req.DeviceType or not SCOPE_SEL_TABLE[req.DeviceType] then
        log:error('[general_hardware]Type[%s] is illegal', req.DeviceType)
        return ret
    end

    if req.Option == EYE_DIAGRAME_OPTIONS_ENUM.START_TEST then
        ret = start_test(req.DeviceType, req.ScopeSel, req.CompareValue)
    end

    if req.Option == EYE_DIAGRAME_OPTIONS_ENUM.GET_RESULT then
        ret = get_result(req.DataType)
    end

    return ret
end

function eye_diagrame.get_pcie_eye_diagrame(req, ctx)
    return msg.GetPCIeEyeDiagramRsp.new(comp_code.InvalidCommand, 0, 0, 0, 0, 0)
end

return eye_diagrame