#!/usr/bin/env python

########################################################################
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Modules' information which are available in the platform
#
########################################################################


try:
    import os
    from sonic_platform_base.module_base import ModuleBase
    from sonic_platform.sfp import Sfp
    from sonic_platform.component import Component
    from sonic_platform.eeprom import Eeprom
except ImportError as e:
    raise ImportError(str(e) + "- required module not found")


class Module(ModuleBase):
    """DellEMC Platform-specific Module class"""

    HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/"
    HWMON_NODE = os.listdir(HWMON_DIR)[0]
    MAILBOX_DIR = HWMON_DIR + HWMON_NODE

    IOM_I2C_MAPPING = { 1: 14, 2: 16, 3: 15, 4: 17 }
    EEPROM_I2C_MAPPING = {
          # IOM 1
           0: [6, 66],  1: [6, 67],  2: [6, 68],  3: [6, 69],
           4: [6, 70],  5: [6, 71],  6: [6, 72],  7: [6, 73],
           8: [6, 74],  9: [6, 75], 10: [6, 76], 11: [6, 77],
          12: [6, 78], 13: [6, 79], 14: [6, 80], 15: [6, 81],
          # IOM 2
          16: [8, 34], 17: [8, 35], 18: [8, 36], 19: [8, 37],
          20: [8, 38], 21: [8, 39], 22: [8, 40], 23: [8, 41],
          24: [8, 42], 25: [8, 43], 26: [8, 44], 27: [8, 45],
          28: [8, 46], 29: [8, 47], 30: [8, 48], 31: [8, 49],
          # IOM 3
          32: [7, 50], 33: [7, 51], 34: [7, 52], 35: [7, 53],
          36: [7, 54], 37: [7, 55], 38: [7, 56], 39: [7, 57],
          40: [7, 58], 41: [7, 59], 42: [7, 60], 43: [7, 61],
          44: [7, 62], 45: [7, 63], 46: [7, 64], 47: [7, 65],
          # IOM 4
          48: [9, 18], 49: [9, 19], 50: [9, 20], 51: [9, 21],
          52: [9, 22], 53: [9, 23], 54: [9, 24], 55: [9, 25],
          56: [9, 26], 57: [9, 27], 58: [9, 28], 59: [9, 29],
          60: [9, 30], 61: [9, 31], 62: [9, 32], 63: [9, 33]
      }

    def __init__(self, module_index):
        ModuleBase.__init__(self)
        # Modules are 1-based in DellEMC platforms
        self.index = module_index + 1
        self.port_start = (self.index - 1) * 16
        self.port_end = (self.index * 16) - 1
        self.port_i2c_line = self.IOM_I2C_MAPPING[self.index]
        self._eeprom = Eeprom(iom_eeprom=True, i2c_line=self.port_i2c_line)

        self.iom_status_reg = "iom_status"
        self.iom_presence_reg = "iom_presence"

        component = Component(is_module=True, iom_index=self.index,
                              i2c_line=self.port_i2c_line, dependency=self)
        self._component_list.append(component)

        eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom"
        sfp_ctrl_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-003e/"

        # sfp.py will read eeprom contents and retrive the eeprom data.
        # It will also provide support sfp controls like reset and setting
        # low power mode.
        for index in range(self.port_start, self.port_end + 1):
            eeprom_path = eeprom_base.format(self.EEPROM_I2C_MAPPING[index][0],
                                             self.EEPROM_I2C_MAPPING[index][1])
            sfp_control = sfp_ctrl_base.format(self.port_i2c_line)
            sfp_node = Sfp(index, 'QSFP', eeprom_path, sfp_control, index)
            self._sfp_list.append(sfp_node)

    def _get_pmc_register(self, reg_name):
        # On successful read, returns the value read from given
        # reg_name and on failure returns 'ERR'
        rv = 'ERR'
        mb_reg_file = self.MAILBOX_DIR + '/' + reg_name

        if (not os.path.isfile(mb_reg_file)):
            return rv

        try:
            with open(mb_reg_file, 'r') as fd:
                rv = fd.read()
        except Exception as error:
            rv = 'ERR'

        rv = rv.rstrip('\r\n')
        rv = rv.lstrip(" ")
        return rv

    def get_name(self):
        """
        Retrieves the name of the device

        Returns:
            string: The name of the device
        """
        return "IOM{}: {}".format(self.index, self._eeprom.modelstr())

    def get_presence(self):
        """
        Retrieves the presence of the Module

        Returns:
            bool: True if Module is present, False if not
        """
        status = False
        iom_presence = self._get_pmc_register(self.iom_presence_reg)
        if (iom_presence != 'ERR'):
            iom_presence = int(iom_presence,16)
            if (~iom_presence & (1 << (self.index - 1))):
                status = True

        return status

    def get_model(self):
        """
        Retrieves the part number of the module

        Returns:
            string: part number of module
        """
        return self._eeprom.part_number_str()

    def get_serial(self):
        """
        Retrieves the serial number of the module

        Returns:
            string: Serial number of module
        """
        return self._eeprom.serial_str()

    def get_status(self):
        """
        Retrieves the operational status of the Module

        Returns:
            bool: True if Module is operating properly, False if not
        """
        status = False
        iom_status = self._get_pmc_register(self.iom_status_reg)
        if (iom_status != 'ERR'):
            iom_status = int(iom_status,16)
            if (~iom_status & (1 << (self.index - 1))):
                status = True

        return status

    def get_position_in_parent(self):
        """
        Retrieves 1-based relative physical position in parent device.
        Returns:
            integer: The 1-based relative physical position in parent
            device or -1 if cannot determine the position
        """
        return self.index

    def is_replaceable(self):
        """
        Indicate whether Module is replaceable.
        Returns:
            bool: True if it is replaceable.
        """
        return True

    def get_base_mac(self):
        """
        Retrieves the base MAC address for the module

        Returns:
            A string containing the MAC address in the format
            'XX:XX:XX:XX:XX:XX'
        """
        # In S6100, individual modules doesn't have MAC address
        return '00:00:00:00:00:00'

    def get_system_eeprom_info(self):
        """
        Retrieves the full content of system EEPROM information for the module

        Returns:
            A dictionary where keys are the type code defined in
            OCP ONIE TlvInfo EEPROM format and values are their corresponding
            values.
            Ex. { ‘0x21’:’AG9064’, ‘0x22’:’V1.0’, ‘0x23’:’AG9064-0109867821’,
                  ‘0x24’:’001c0f000fcd0a’, ‘0x25’:’02/03/2018 16:22:00’,
                  ‘0x26’:’01’, ‘0x27’:’REV01’, ‘0x28’:’AG9064-C2358-16G’}
        """
        return self._eeprom.system_eeprom_info()

    def get_description(self):
        """
        Retrieves the platform vendor's product description of the module

        Returns:
            A string, providing the vendor's product description of the module.
        """
        return self._eeprom.modelstr()

    def get_slot(self):
        """
        Retrieves the platform vendor's slot number of the module

        Returns:
            An integer, indicating the slot number in the chassis
        """
        return self.index

    def get_oper_status(self):
        """
        Retrieves the operational status of the module

        Returns:
            A string, the operational status of the module from one of the
            predefined status values: MODULE_STATUS_EMPTY, MODULE_STATUS_OFFLINE,
            MODULE_STATUS_FAULT, MODULE_STATUS_PRESENT or MODULE_STATUS_ONLINE
        """
        if self.get_presence():
            if self.get_status():
                return self.MODULE_STATUS_ONLINE
            else:
                return self.MODULE_STATUS_PRESENT
        else:
            return self.MODULE_STATUS_EMPTY

    def get_maximum_consumed_power(self):
        """
        Retrives the maximum power drawn by this module

        Returns:
            A float, with value of the maximum consumable power of the
            module.
        """
        return 97.23
