#
# Copyright (c) 2004-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# LicenseRef-NvidiaProprietary
#
# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
# property and proprietary rights in and to this material, related
# documentation and any modifications thereto. Any use, reproduction,
# disclosure or distribution of this material and related documentation
# without an express license agreement from NVIDIA CORPORATION or
# its affiliates is strictly prohibited
#

import abc


class ParserBase():
    """ Represents the base class for all operational info sections.
    """

    def __init__(self):
        self.NAME = None
        self.exists = False
        self.attr_names = []
        self.attr_dict = {}

    def find_attr_line(self, attr, mlxlink_output):
        for line in mlxlink_output:
            # An additional check to avoid false detection in case two attrs has the same prefix
            splitted_attr = attr.split(' ')
            splitted_line = line.split(':')[0].strip().split(' ')
            if attr in line and (len(splitted_attr) == len(splitted_line) or 'attenuation' in attr.lower()):
                return line
        return ""

    def parse(self, mlxlink_output):
        start = 0
        while self.NAME is not None and self.NAME not in mlxlink_output[start] and start < len(mlxlink_output):
            start += 1
        if start == len(mlxlink_output):
            return
        self.exists = True
        self.attr_dict = {name: self.find_attr_line(name, mlxlink_output) for name in self.attr_names}

    def print_title(self, suffix=''):
        print(self.NAME + suffix + '\n' + '-' * (len(self.NAME)))

    def is_relevant_section(self):
        return self.exists

    def get_attr(self, attr_name):
        return self.attr_dict.get(attr_name)

    def get_all_attrs_names(self):
        return self.attr_names


##################################################
#            Operational info section            #
##################################################

class OperationalInfo(ParserBase):
    """ Represents the operational info section in mlxlink output(show PDDR).
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Operational Info"
        self.attr_names = ["State", "Physical state", "Speed", "Width", "FEC", "Loopback Mode", "Auto Negotiation"]


class SupportedInfo(ParserBase):
    """ Represents the supported info section in mlxlink output (show PDDR).
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Supported Info"
        self.attr_names = ["Enabled Link Speed", "Supported Cable Speed"]


class TroubleshootingInfo(ParserBase):
    """ Represents the troubleshooting info section in mlxlink output (show PDDR).
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Troubleshooting Info"
        self.attr_names = ["Status Opcode", "Group Opcode", "Recommendation", "Time to Link Up"]


class ToolInfo(ParserBase):
    """ Represents the tool information section in mlxlink output (show PDDR).
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Tool Information"
        self.attr_names = ["Firmware Version", "amBER Version", "MFT Version"]


##################################################
#            Show module info section            #
##################################################

class ModuleInfo(ParserBase):
    """ Represents the module info section in mlxlink output (show module)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Module Info"
        self.attr_names = ["Temperature [C]", "Voltage [mV]", "Bias Current [mA]", "Rx Power Current [dBm]", "Tx Power Current [dBm]", "Identifier", "Compliance", "Cable Technology", "Cable Type", "OUI", "Vendor Name", "Vendor Part Number", "Vendor Serial Number", "Rev", "Wavelength [nm]", "Transfer Distance [m]", "Attenuation", "FW Version", "Digital Diagnostic Monitoring", "Power Class", "CDR RX", "CDR TX", "LOS Alarm", "Intra-ASIC Latency [ns]", "Module Datapath Latency [ns]", "Round Trip Latency [ns]", "SNR Media Lanes [dB]", "SNR Host Lanes [dB]", "IB Cable Width", "Memory Map Revision", "Linear Direct Drive", "Cable Breakout", "SMF Length", "MAX Power", "Cable Rx AMP", "Cable Rx Emphasis", "Cable Rx Post Emphasis", "Cable Tx Equalization", "Wavelength Tolerance", "Module State", "DataPath state [per lane]", "Rx Output Valid [per lane]", "Nominal bit rate", "Rx Power Type", "Manufacturing Date", "Active Set Host Compliance Code", "Active Set Media Compliance Code", "Error Code Response", "Module FW Fault", "DataPath FW Fault", "Tx Fault [per lane]", "Tx LOS [per lane]", "Tx CDR LOL [per lane]", "Rx LOS [per lane]", "Rx CDR LOL [per lane]", "Tx Adaptive EQ Fault [per lane]", "Laser Status", "Laser Restriction", "Production Test Revision"]


##################################################
#             Show counters section              #
##################################################

class CountersInfo(ParserBase):
    """ Represents the counters info section in mlxlink output (show counters)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Physical Counters and BER Info"
        self.attr_names = ["Time Since Last Clear [Min]", "Symbol Errors", "Symbol BER", "Effective Physical Errors", "Effective Physical BER", "Raw Physical Errors Per Lane", "Raw Physical BER Per Lane", "Raw Physical BER", "Link Down Counter", "Link Error Recovery Counter"]


##################################################
#          Show eye opening info section         #
##################################################

class EyeOpeningInfo(ParserBase):
    """ Represents the eye opening info section in mlxlink output (show eye)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "EYE Opening Info"
        self.attr_names = ["FOM Mode", "Lane", "Initial FOM", "Last FOM", "Upper Grades", "Mid Grades", "Lower Grades"]


##################################################
#          Show FEC capabilities section         #
##################################################

class FecCapabilitiesInfo(ParserBase):
    """ Represents the show FEC capabilities info section in mlxlink output (show fec)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "FEC Capability Info"
        self.attr_names = ["FEC Capability XDR", "FEC Capability NDR", "FEC Capability HDR", "FEC Capability EDR", "FEC Capability FDR", "FEC Capability FDR10", "FEC Capability 10G", "FEC Capability 25G", "FEC Capability 40G", "FEC Capability 50G", "FEC Capability 56G", "FEC Capability 100G", "FEC Capability 100G_4X", "FEC Capability 50G_2X", "FEC Capability 50G_1X", "FEC Capability 100G_2X", "FEC Capability 200G_4X", "FEC Capability 400G_8X", "FEC Capability 100G_1X", "FEC Capability 200G_2X", "FEC Capability 400G_4X", "FEC Capability 800G_8X"]


##################################################
#            Show serdes tx info section         #
##################################################

class SerdesTxInfo(ParserBase):
    """ Represents the show serdes tx info section in mlxlink output (show serdes tx)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Serdes Tuning Transmitter Info"
        self.attr_names = ["Serdes TX parameters", "Lane 0"]


##################################################
#         Show general device info section       #
##################################################

class GeneralDeviceInfo(ParserBase):
    """ Represents the general device info section in mlxlink output (show device)
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Device Info"
        self.attr_names = ["Part Number", "Part Name", "Serial Number", "Revision", "FW Version"]


##################################################
#            Show FEC histogram section          #
##################################################

class ShowFecHistogram(ParserBase):
    """ Represents the show FEC histogram section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Histogram of FEC Errors"
        self.attr_names = ["Header", "Bin 0", "Bin 1", "Bin 2", "Bin 3", "Bin 4", "Bin 5", "Bin 6", "Bin 7", "Bin 8", "Bin 9", "Bin 10", "Bin 11", "Bin 12", "Bin 13", "Bin 14", "Bin 15"]


##################################################
#                Test mode section               #
##################################################

class TestModeInfo(ParserBase):
    """ Represents the test mode info section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Test Mode Info"
        self.attr_names = ["RX PRBS Mode", "TX PRBS Mode", "RX Lane Rate", "TX Lane Rate", "Tuning Status", "Lock Status"]


class UniqueCommands(ParserBase):
    """ Represents the relevant unique commands section in mlxlink output.
    """
    def __init__(self):
        super().__init__()
        self.attr_names = [
            "Configuring Port State (Down)", "Configuring Port State (Up)",
            "Configuring Port Speeds",
            "Configuring Port Loopback",
            "Configuring Port FEC",
            "Clearing Histogram Counters",
            "Clearing Counters",
            "Configuring Port Transmitter Parameters",
            "Configuring Port to Physical Test Mode", "Performing Tuning", "Configuring Port to Regular Operation"
        ]


##################################################
#          Configure port state section          #
##################################################

class ConfigurePortState(ParserBase):
    """ Represents the configure port state section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Configuring Port State (Down)", "Configuring Port State (Up)"]


##################################################
#          Configure port speed section          #
##################################################

class ConfigurePortSpeed(ParserBase):
    """ Represents the configure port speed section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Configuring Port Speeds"]


##################################################
#        Configure loopback mode section         #
##################################################

class ConfigureLoopbackMode(ParserBase):
    """ Represents the configure loppback mode section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Configuring Port Loopback"]


##################################################
#             Configure FEC section              #
##################################################

class ConfigureFec(ParserBase):
    """ Represents the configure FEC mode section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Configuring Port FEC"]


##################################################
#             Clear counters section             #
##################################################

class ClearCounters(ParserBase):
    """ Represents the clear counters section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Clearing Counters"]


##################################################
#          Configure serdes tx section           #
##################################################

class ConfigureSerdesTx(ParserBase):
    """ Represents the configure serdes tx section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.attr_names = ["Configuring Port Transmitter Parameters"]


##################################################
#               Show PLR section                 #
##################################################

class ShowPlr(ParserBase):
    """ Represents the show PLR section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.NAME = "PLR Info"
        self.attr_names = ["PLR Reject Mode", "PLR Margin Threshold"]


##################################################
#              Show KR section                  #
##################################################

class ShowKr(ParserBase):
    """ Represents the show KR section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.NAME = "KR Info"
        self.attr_names = ["Support Non-Standard Training Flow", "Number of Iterations", "Time for Iteration", "BER Target"]


##################################################
#              Cable read section                #
##################################################

class CableRead(ParserBase):
    """ Represents the cable read section in mlxlink output.
    """

    def __init__(self):
        super().__init__()
        self.NAME = "Cable Read Output"
        self.data_lst = []

    def get_data_lst(self):
        return self.data_lst

    def parse(self, mlxlink_output):
        start = 0
        while self.NAME is not None and self.NAME not in mlxlink_output[start] and start < len(mlxlink_output):
            start += 1
        if start == len(mlxlink_output):
            return
        self.exists = True
        start += 2
        while start < len(mlxlink_output):
            self.data_lst.append(mlxlink_output[start])
            start += 1


##################################################
#                 Util section                   #
##################################################

COLOR_DICT = {'red': '31m', 'green': '32m', 'yellow': '33m'}


def add_color_to_str(str_to_color, color):
    color_symbol = COLOR_DICT.get(color)
    if color_symbol:
        return '\x1b[' + color_symbol + str_to_color + '\x1b[0m'
    return None


class BaseHandler():
    def __init__(self, color):
        self.msg_lst = []
        self.color = color

    def add_msg(self, msg):
        self.msg_lst.append(msg)

    @abc.abstractclassmethod
    def print_title(self):
        pass

    def display_msg(self):
        if self.msg_lst:
            self.print_title()
            for msg in self.msg_lst:
                print(add_color_to_str(msg, self.color))
        print()


class WarningsHandler(BaseHandler):
    """ This class was created to handle all warning msgs.
    """

    def print_title(self):
        print('Warnings\n' + '-' * 8)


class ErrorHandler(BaseHandler):
    """ This class was created to handle special error msgs.
    """

    def print_title(self):
        print('Errors\n' + '-' * 8)
