/*******************************************************************************
 * (c) Copyright 2015 Microsemi Corporation.  All rights reserved.
 *
 *
 *
 * SVN $Revision: $
 * SVN $Date: $
 */

#include <api_oam_sdk/common/error_codes/inc/mr_error_codes.h>
#include <api_oam_sdk/maintenance_domain/src/md_types.h>
#include <api_oam_sdk/metroam_hal/inc/mr_hal.h>
#include <string.h>
#include <syslog.h>

#include "egl_assert.h"
#include "egl_log.h"
#include "osal.h"
#include "network_settings.h"

static mdList_t		mdList					= {{NULL,NULL},1,0};
static mdEntry_t	mdArray[MAX_NUM_MD];
static uint16_t		log_handle				= 0;		// handle used to log messages for this module
osMutex_t			mdListLock				= NULL;			// TODO: change back to Static after debug
static bool			mdInitialised			= false;

#define LOCK_MD_LIST(timeout)	;//osRecursiveMutexTake(mdListLock,timeout)
#define UNLOCK_MD_LIST()		;//osRecursiveMutexGive(mdListLock)

bool deleteAll;
/*
 * @brief	This sets the MDEntry values to their default
 *
 * In most cases this should only be done with mdLock enabled
 * to ensure thread safe operation
 */
static void setMdDefaults(mdEntry_t *mdEntry)
{
    memset(mdEntry,0,sizeof(mdEntry_t));
    mdEntry->mdCtxt.mdNum 				= INVALID_MD_NUM;
    mdEntry->mdMibInfo.type				= CHARACTERSTRING;
    memcpy(&mdEntry->mdMibInfo.name	,"DEFAULT",7);
    mdEntry->mdMibInfo.mhfCreation		= md_defMHFnone;
    mdEntry->mdMibInfo.mhfIdPermission	= md_sendIdNone;
    mdEntry->mdMibInfo.maNextMibIdx		= 1;
}

/*
 * @brief Clears the MD list
 *
 * This should be called to initialise the MD list.
 * Any MDs on the list will be lost.
 */
static void clearMdList(void)
{
    uint16_t	k;

    // Initialise the mdList
    TAILQ_INIT(&mdList.llMdHead);
    mdList.mdNextMibIdx = 1;
    mdList.mdCount		= 0;

    // Initialise all entries in the MD store to defaults
    for (k = 0; k < MAX_NUM_MD; k++)
    {
        setMdDefaults(&mdArray[k]);
    }
    DO_LOG_DEBUG(log_handle,"Initialised the MD List");
}

/*
 * @brief	Called at device startup to initialise MD module
 *
 * Safe to call repeatedly.
 */
void mdInit(void)
{
    log_init();	// Initialises the logging system if it's not already initialised

    // Get a log handle if we don't already have one
    if (log_handle == 0)
        log_handle = log_register("MD");

    // Create a mutex to protect the MD list if we don't already have one
    if (mdListLock == NULL)
    {
        mdListLock = osRecursiveMutexCreate();
        //VRts_printf("mdListLock = osRecursiveMutexCreate() = %d\n\r",mdListLock);
        ASSERT_NOT_NULL(mdListLock);
        //VRts_printf("mdListLock = osRecursiveMutexCreate()\n\r");
    }
    //VRts_printf("Preparing the MD List for first use1\n\r");
    // If not already initialised clear the MD list for first use.
    if (!mdInitialised)
    {
        //VRts_printf("Preparing the MD List for first use2\n\r");
        // Set the default logging level to NOTICE
        setModuleLogLevel(log_handle, eLOG_LEVEL_NOTICE);

        DO_LOG_NOTICE(log_handle,"Preparing the MD List for first use");
        LOCK_MD_LIST(0);
        clearMdList();
        // TODO: Add these:
        // defaultMDInit();
        // mipInit();
        UNLOCK_MD_LIST();
        mdInitialised = true;
    }
}

/*
 * @brief Convert an mdNum into a MD MIB index
 */
mdIndex_t _mdNumToMibIndex(mdNum_t mdNum)
{
    mdIndex_t mdIndex  = 0;

    LOCK_MD_LIST(0);
    if (mdArray[mdNum].mdCtxt.inUse)
    {
        mdIndex = mdArray[mdNum].mdCtxt.mibIdx;
    }
    UNLOCK_MD_LIST();
    return (mdIndex);
}


/*
 * @brief Convert a MD MIB index into an mdNum
 */
static mdNum_t	mibIndexToMdNumNoLocking(mdIndex_t mdIndex)
{
    mdEntry_t	*mdEntryPtr;
    mdNum_t		mdNum = -1;

    // Start searching valid MDs at the head of the list
    mdEntryPtr = TAILQ_FIRST(&mdList.llMdHead);
    while (mdEntryPtr != NULL)
    {
        if (mdEntryPtr->mdCtxt.mibIdx == mdIndex)
        {
            mdNum = mdEntryPtr->mdCtxt.mdNum;
            return (mdNum);	//found it
        }
        mdEntryPtr = TAILQ_NEXT(mdEntryPtr,ll);
    }
    return -1;	// mdIndex not found
}

/*
 * @brief Convert a MD MIB index into an mdNum
 */
mdNum_t	_mdMibIndexToMdNum(mdIndex_t mdIndex)
{
    mdNum_t mdNum;

    LOCK_MD_LIST(0);
    mdNum = mibIndexToMdNumNoLocking(mdIndex);
    UNLOCK_MD_LIST();
    return mdNum;
}

/*
 * @brief Read the next entry in the MD List
 *
 * @param[in] mdIndex of previous entry or 0
 * @return	mdIndex of the next entry or 0 if no more entries
 *
 * Read the next entry in the MD List. If mdIndex is 0 it
 * returns the mdIndex of the first MD entry or 0 if there are
 * no MD entries. If a valid mdIndex is provided it returns
 * the mdIndex of the next MD in the list.
 */
mdIndex_t mdReadList(mdIndex_t prevMdIdx)
{
    mdNum_t 	nextMdNum;
    mdIndex_t	theMdMibIdx;
    LOCK_MD_LIST(0);
    if (prevMdIdx == 0)
    {
        if (!TAILQ_EMPTY(&mdList.llMdHead))
        {
            theMdMibIdx = TAILQ_FIRST(&mdList.llMdHead)->mdCtxt.mibIdx;
            UNLOCK_MD_LIST();
            return theMdMibIdx;
        }
    }
    else
    {
        nextMdNum = mibIndexToMdNumNoLocking(prevMdIdx);
        if (nextMdNum != -1)
        {
            if (TAILQ_NEXT(&mdArray[nextMdNum],ll) != NULL)
            {
                theMdMibIdx = TAILQ_NEXT(&mdArray[nextMdNum],ll)->mdCtxt.mibIdx;
                UNLOCK_MD_LIST();
                return theMdMibIdx;
            }
        }
    }
    UNLOCK_MD_LIST();
    return 0;
}

/*
 * @brief	Find the next free MD Entry
 */
static mdEntry_t* getNextFreeMdEntry(void)
{
    mdNum_t	k;

    //LOCK_MD_LIST(0);
    for (k = 0; k < MAX_NUM_MD; k++)
    {
        if (!mdArray[k].mdCtxt.inUse)
        {
            mdArray[k].mdCtxt.mdNum = k;
            mdArray[k].mdCtxt.inUse = true;
            //UNLOCK_MD_LIST();
            return (&mdArray[k]);
        }
    }
    return NULL;
}

// -----------------------------------------------------------------------------------------------------
// verifyMDNFL
// This function tests that the formatting of the supplied MD name is valid.
// If the name, specified as a byte array, is not correctly formated to match
// the mdType and length specifiers then this function will return an error.
// -----------------------------------------------------------------------------------------------------
static bool isMDNFLInvalid(mdName_t name,mdNameType_t mdType)
{
    int     k;

    switch(mdType)
    {
    case md_none:
    {
        break;
    }
    case md_dnsLikeName:
    {
        //This is basically a standard string. DNS name verification is too diverse to test
        if ((strlen(name) > 43) || (strlen(name) == 0))
        {
            DO_LOG_DEBUG(log_handle,"The MD format is dnsLikeName (%d), but length is > 43",mdType);
            return(true);
        }
        break;
    }
    case md_macaddressAndUInt:
    {
        // The MAC address is 6 bytes and the integer is 2 bytes
        // No real way to verify!
        break;
    }
    case md_characterString:
    {
        // The restriction here is that the string must be less than 45 bytes and should not contain ascii characters 0-31
        if ((strlen(name) > 43) || (strlen(name) == 0))
        {
            DO_LOG_DEBUG(log_handle,"The MD format is characterString (%d), but length (%d) is not >1 and <= 43",mdType, strlen(name));
            return(true);
        }

        for (k=0;k<strlen(name);k++)
        {
            if (name[k] < 32)
            {
                DO_LOG_DEBUG(log_handle,"The MD format is characterString (%d), but the string contains illegal characters",mdType);
                return(true);
            }
        }
        break;
    }
#if 0
    case md_y1731:
    {
        if ((strlen(name) > 13) || (strlen(name) < 8))
        {
            DO_LOG_DEBUG(log_handle,"The MD format is Y1731 (ICC based) (%d), but length (%d) is not >= 8 and <= 13",mdType, strlen(name));
            return(true);
        }
        break;
    }
#endif
    default:
    {
        DO_LOG_DEBUG(log_handle,"Unknown MD Name type (%d) provided",mdType);
        return(true);
    }
    }
    return(false);
}

/*
 * @brief test is the MD name already in use
 */
static bool isMDNameInUse(mdName_t mdName, mdNameType_t type)
{
    mdEntry_t	*mdEntryPtr;
    bool		compareAsStr;
    uint8_t		compareLen;

    // Start searching valid MDs from the head of the list
    mdEntryPtr = TAILQ_FIRST(&mdList.llMdHead);
    while (mdEntryPtr != NULL)
    {
        if (mdEntryPtr->mdMibInfo.type == type)
        {
            switch(type)
            {
            case md_none:
                return false;	// If MD type is 'none' then we must allow duplicates (this is used for Y1731 mode primarily)
                break;
            case md_dnsLikeName:
                compareLen = 0;
                compareAsStr = true;
                break;
            case md_macaddressAndUInt:
                compareLen = 8;
                compareAsStr = false;
                break;
            case md_characterString:
                compareLen = 0;
                compareAsStr = true;
                break;
#if 0
            case md_y1731:
                compareLen = 0;
                compareAsStr = true;
                break;
#endif
            default:
                compareLen = 0;
                compareAsStr = true;
            }
            // The types match check the data
            if (compareAsStr)
            {
                if (strcmp(mdEntryPtr->mdMibInfo.name,mdName) == 0)
                {
                    return true;
                }
            }
            else
            {
                if (memcmp(mdEntryPtr->mdMibInfo.name,mdName,compareLen) == 0)
                {
                    return true;
                }
            }
        }
        mdEntryPtr = TAILQ_NEXT(mdEntryPtr,ll);
    }
    return false;	// MD name not in use
}

/*
 * @brief	Add a new mdEntry into the MD List
 */
static void addMdToList(mdEntry_t *mdEntry)
{
    ASSERT_NOT_NULL(mdEntry);

    TAILQ_INSERT_TAIL(&mdList.llMdHead,mdEntry,ll);
    mdList.mdCount++;

    // The MD index can be specified by the user. So
    // it is necessary to keep the mdNextMibIdx aligned
    if (mdList.mdNextMibIdx <= mdEntry->mdCtxt.mibIdx)
    {
        mdList.mdNextMibIdx = mdEntry->mdCtxt.mibIdx + 1;
    }
}

/*
 * @brief	Remove the MD entry, pointed to by mdEntry, from the MD List
 */
static void removeMdFromList(mdEntry_t *mdEntry)
{
    ASSERT_NOT_NULL(mdEntry);

    TAILQ_REMOVE(&mdList.llMdHead,mdEntry,ll);
    setMdDefaults(mdEntry);
    mdList.mdCount--;
}

/*
 * @brief	Create a new Maintenance Domain
 *
 * @param[in]	mdIndex - the MIB index to use for this MD
 * @param[in]	mdName - Character array the holds the MD name
 * @param[in]	type - the MD name type
 * @return values:
 *  1: Operation rejected due to invalid Maintenance Domain Name
 *  2: Operation rejected because Maintenance Domain Name already exists
 *  3: Operation rejected due to invalid MD Level
 *  4: Operation accepted
 * -1: Operation rejected due to invalid MD Index
 * -2: Operation rejected due to insufficient resources
 */
int mdCreate(mdIndex_t mdIndex, mdName_t mdName, mdNameType_t type, mdLevel_t mdLevel)
{
    mdEntry_t	*mdEntryPtr;
    //LOCK_MD_LIST(0);

    // Validate the mdIndex value provided
    if ((mdIndex == 0) || (mibIndexToMdNumNoLocking(mdIndex) != -1)) //|| (mdIndex > mdList.mdNextMibIdx))
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid MD Index (%lu)",mdIndex);
        ts_printf("Operation rejected due to invalid MD Index (%lu)",mdIndex);
        return (-1);
    }
    //Begin testing the fields of the entry...
    // Test the MD level
    if ((mdLevel > level7) || (mdLevel < level0))
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid MD Level (%d)",mdLevel);
        ts_printf("Operation rejected due to invalid MD Level (%d)",mdLevel);
        return(3); //Operation rejected due to invalid MD Level
    }
    // Test the MD name format
    if (isMDNFLInvalid(mdName,type))
    {
        //UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid Maintenance Domain Name");
        return(1);
    }
    // Check that the MD Name is unique
    if (isMDNameInUse(mdName, type))
    {
        //UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected because Maintenance Domain Name already exists");
        ts_printf("Operation rejected because Maintenance Domain Name already exists");
        return(2);
    }
    mdEntryPtr = getNextFreeMdEntry();
    if (mdEntryPtr != NULL)
    {
        // Set the mibIdx to the one provided and update the other values
        mdEntryPtr->mdCtxt.mibIdx = mdIndex;
        mdEntryPtr->mdMibInfo.level = mdLevel;
        mdEntryPtr->mdMibInfo.type = type;
        memcpy(mdEntryPtr->mdMibInfo.name, mdName, sizeof(mdName_t));
        TAILQ_INIT(&mdEntryPtr->llMaHead);
        // add the new MD to the list
        addMdToList(mdEntryPtr);
    }
    else
    {
        //UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to insufficient resources");
        ts_printf("Operation rejected due to insufficient resources");
        return (-2);
    }
    //UNLOCK_MD_LIST();
    //DO_LOG_NOTICE(log_handle,"Created MD with index = %lu",mdIndex);
#ifdef DEBUG_PRINT_L2
    ts_printf("Created MD with index = %d",mdIndex);
#endif
    return (4); // Operation accepted.
}

/*
 * @brief Test if the supplied MD Index is valid
 */
static bool isMdMibIdxValid(mdIndex_t mdIndex, mdNum_t *mdNum)
{
    mdNum_t	theMdNum;

    if ((mdIndex == 0) || (mdIndex >= mdList.mdNextMibIdx))
    {
        MD_MIB_IDX_INVALID:
        DO_LOG_DEBUG(log_handle,"The MD MIB Index (%lu) was invalid",mdIndex);
        return false;
    }

    // Test is the mdIndex known and get it's associated mdNum
    theMdNum = mibIndexToMdNumNoLocking(mdIndex);
    if (theMdNum != -1)		// If the MD index is found
    {
        if (mdNum != NULL)
        {
            *mdNum = theMdNum;
        }
        return(true);
    }
    goto MD_MIB_IDX_INVALID;
}

/*
 * @brief Test if the supplied MD Index is valid
 */
bool mdIsMdMibIdxValid(mdIndex_t mdIndex)
{
    bool retVal;

    LOCK_MD_LIST(0);
    retVal = isMdMibIdxValid(mdIndex, NULL);
    UNLOCK_MD_LIST();
    return(retVal);
}

/*
 * @brief	Delete the MD with the supplied MIB index
 */
int mdDelete(mdIndex_t mdIndex)
{
    mdNum_t		mdNum;
    maIndex_t	maIndex, maIndexToDelete;

    LOCK_MD_LIST(0);
    // Test is the mdIndex known and get it's associated mdNum
    if (!isMdMibIdxValid(mdIndex, &mdNum))
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid or nonexistent Maintenance Domain (index = %lu)",mdIndex);
        return (1);	//Operation rejected due to invalid or nonexistent Maintenance Domain
    }

    // find and delete all MAs associated with this MD
    maIndex = maReadList(mdIndex,0);
    while (maIndex != 0)
    {
        maIndexToDelete = maIndex;
        // Get the next MA Index. BEFORE deleting the current one
        maIndex = maReadList(mdIndex,maIndex);
        if (maDelete(mdIndex,maIndexToDelete) != 2)
        {
            UNLOCK_MD_LIST();
            // TODO: see if this can happen and if it can should the error be higher severity?
            DO_LOG_CRITICAL(log_handle,"Failed to delete MA with index = %lu",maIndexToDelete);
            return (3); // Not all child objects were deleted
        }

    }

    removeMdFromList(&mdArray[mdNum]);
    UNLOCK_MD_LIST();
    DO_LOG_NOTICE(log_handle,"Deleted MD with index = %lu",mdIndex);
    return (2); // Operation accepted
}

/*
 * @brief	Delete all MDs on the device
 */
int mdDeleteMdList(void)
{
    mdEntry_t	*mdEntryPtr;
    int			retVal;
    //uint32_t	data;

    LOCK_MD_LIST(0);

#if 0
    // Disable all CFM interrupts
    mrHalRegRead(INT_MASK_REG_ADDR,&data);	// TODO: decide if this is to be left in or not!
    data &= ~(CONN_INT_MASK | XCONN_INT_MASK | RMEP_ERR_INT_MASK | SA_CNG_INT_MASK | RDI_CNG_INT_MASK | ANY_TLV_CNG_INT_MASK);
    mrHalRegWrite(INT_MASK_REG_ADDR,data);
#endif

    //used to prevent PM timer from saving stats to the flash log
    deleteAll=true;

    // Point to the first MD on the list
    mdEntryPtr = TAILQ_FIRST(&mdList.llMdHead);
    while (mdEntryPtr != NULL)
    {
        // Remove the next MD in the list
        retVal = mdDelete(mdEntryPtr->mdCtxt.mibIdx);
        //ASSERT(retVal == 2,1);		// if we fail to delete any MDs there is a problem!

        if ( retVal != 2)
        {
            // TODO: revise this later - is the better course of action to do a initialisation of the MIB lists?
            DO_LOG_CRITICAL(log_handle,"Not all MDs were deleted, MD with index %lu was not removed",mdEntryPtr->mdCtxt.mibIdx);
        }
        // We are deleting from the head of the list so get the new head...
        mdEntryPtr = TAILQ_FIRST(&mdList.llMdHead);
    }

#if 0
    // re-enable all CFM interrupts
    mrHalRegRead(INT_MASK_REG_ADDR,&data);	// TODO: decide if this is to be left in or not!
    data |= (CONN_INT_MASK | XCONN_INT_MASK | RMEP_ERR_INT_MASK | SA_CNG_INT_MASK | RDI_CNG_INT_MASK | ANY_TLV_CNG_INT_MASK);
    mrHalRegWrite(INT_MASK_REG_ADDR,data);
#endif

    UNLOCK_MD_LIST();
    deleteAll=false;
    DO_LOG_NOTICE(log_handle,"All MDs were deleted");
    return (2); // Operation accepted
}

/*
 * @brief	Read the information for the MD	selected by mdIndex
 */
int mdRead(mdIndex_t mdIndex, mdMibInfo_t *mdMibInfo, networkAddress_t *faultAlarmNetAddr)
{
    mdNum_t		mdNum;

    LOCK_MD_LIST(0);
    // Test is the mdIndex known and get it's associated mdNum
    if (!isMdMibIdxValid(mdIndex, &mdNum))
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid or nonexistent Maintenance Domain (%lu)",mdIndex);
        return (1);
    }

    if (mdMibInfo != NULL)
    {
        memcpy(mdMibInfo, &mdArray[mdNum].mdMibInfo,sizeof(mdMibInfo_t));
    }

    if (faultAlarmNetAddr != NULL)
    {
        *faultAlarmNetAddr = mdArray[mdNum].alarmNetAddress;
    }
    UNLOCK_MD_LIST();
    DO_LOG_DEBUG(log_handle,"Read info for Maintenance Domain (%lu)",mdIndex);
    return (2); // Operation accepted
}

/*
 * @brief	Write a new value to the selected MD parameter
 */
int mdWrite(mdIndex_t mdIndex, mdWrParam_t mdParam, void *paramValue)
{
    mdNum_t		mdNum;
    char		buf[20];

    LOCK_MD_LIST(0);
    // Test is the mdIndex known and get it's associated mdNum
    if (!isMdMibIdxValid(mdIndex, &mdNum))
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to invalid or nonexistent Maintenance Domain (index = %lu)",mdIndex);
        return (1);	//Operation rejected due to invalid or nonexistent Maintenance Domain
    }

    if (paramValue == NULL)
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to NULL value");
        return (3);
    }

    switch(mdParam)
    {
    case mdParam_mdMhfCreation:
    {
        mdMhfCreation_t		*mhfCreationPtr = (mdMhfCreation_t*)paramValue;
        if ((*mhfCreationPtr < md_defMHFnone) || (*mhfCreationPtr > md_defMHFexplicit))
        {
            UNLOCK_MD_LIST();
            DO_LOG_INFO(log_handle,"Operation rejected due to invalid value (%d) for mhfCreation",*mhfCreationPtr);
            return (3);
        }
        if (mdArray[mdNum].mdMibInfo.mhfCreation != *mhfCreationPtr)
        {
            // Write the new value into the MD
            mdArray[mdNum].mdMibInfo.mhfCreation = *mhfCreationPtr;
            DO_LOG_NOTICE(log_handle,"On MD with index %lu the mhfCreation value was changed to %d",mdIndex,*mhfCreationPtr);
        }
        break;
    }
    case mdParam_mdMhfIdPermission:
    {
        mdMhfIdPermission_t		*mhfIdPermissionPtr = (mdMhfIdPermission_t*)paramValue;
        if ((*mhfIdPermissionPtr < md_sendIdNone) || (*mhfIdPermissionPtr > md_sendIdChassisManage))
        {
            UNLOCK_MD_LIST();
            DO_LOG_INFO(log_handle,"Operation rejected due to invalid value (%d) for mhfIdPermission",*mhfIdPermissionPtr);
            return (3);
        }
        if (mdArray[mdNum].mdMibInfo.mhfIdPermission != *mhfIdPermissionPtr)
        {
            // Write the new value into the MD
            mdArray[mdNum].mdMibInfo.mhfIdPermission = *mhfIdPermissionPtr;
            DO_LOG_NOTICE(log_handle,"On MD with index %lu the mhfIdPermission value was changed to %d",mdIndex,*mhfIdPermissionPtr);
        }
        break;
    }
    case mdParam_mdFaultAlarmNetAddress:
    {
        networkAddress_t *networkAddress = (networkAddress_t*)paramValue;
        // TODO: There is no easy way to validate the network address so don't for now
        if (mdArray[mdNum].alarmNetAddress != *networkAddress)
        {
            mdArray[mdNum].alarmNetAddress = *networkAddress;
            DO_LOG_NOTICE(log_handle,"On MD with index %lu the alarmNetAddress was changed to %s",mdIndex,printfIPAddr(*networkAddress,buf));
        }
        break;
    }
    default:
    {
        UNLOCK_MD_LIST();
        DO_LOG_INFO(log_handle,"Operation rejected due to the selected managed object being Read-Only");
        return (2);
        break;
    }
    }
    UNLOCK_MD_LIST();
    return (4); // Operation accepted
}

struct llMaHead_s * _getMaListHead(mdIndex_t mdIndex)
{
    mdNum_t	mdNum;
    struct llMaHead_s *maListHeadPtr;

    LOCK_MD_LIST(0);
    // Test is the mdIndex known and get it's associated mdNum
    if (!isMdMibIdxValid(mdIndex, &mdNum))
    {
        UNLOCK_MD_LIST();
        DO_LOG_DEBUG(log_handle,"Operation rejected due to invalid or nonexistent Maintenance Domain (index = %lu)",mdIndex);
        return (NULL);	//Operation rejected due to invalid or nonexistent Maintenance Domain
    }

    maListHeadPtr = &mdArray[mdNum].llMaHead;
    UNLOCK_MD_LIST();
    return(maListHeadPtr);
}

void _mdIncNextMaIdx(mdIndex_t mdIndex, maIndex_t maIndex)
{
    mdNum_t	mdNum;

    LOCK_MD_LIST(0);
    // Test is the mdIndex known and get it's associated mdNum
    if (isMdMibIdxValid(mdIndex, &mdNum) == false)
    {
        // do not modify the maNextMibIdx
        UNLOCK_MD_LIST();
        return;
    }
    //ASSERT(isMdMibIdxValid(mdIndex, &mdNum),ERR_INVALID_MD_HNDL);


    // The MA index can be specified by the user. So
    // it is necessary to keep the mdMibInfo.maNextMibIdx aligned
    if (mdArray[mdNum].mdMibInfo.maNextMibIdx <= maIndex)
    {
        mdArray[mdNum].mdMibInfo.maNextMibIdx = maIndex + 1;
    }
    UNLOCK_MD_LIST();
}

mdIndex_t mdListReadNextMdIndex(void)
{
    return (mdList.mdNextMibIdx);
}

/*
 * @brief Add/update/remove a TLV on the CCM packet for all MEPs it the MA - e.g. Port/Interface Status
 */
void _mdUpdateCCMTLVs(mdIndex_t mdIndex, iFace_t iFace, tlvType_t type, uint8_t length,void *value)
{
    maIndex_t maIndex;

    // Adjust the TLV for all MAs associated with this MD
    maIndex = maReadList(mdIndex,0);
    while(maIndex != 0)
    {
        _maUpdateCCMTLVs(mdIndex, maIndex, iFace, type, length, value);
        maIndex = maReadList(mdIndex, maIndex);
    }
}

/*
 * This function is used to change the lowest level severity to log for
 * this module. Values of eLOG_LEVEL_DEBUG (lowest) to eLOG_LEVEL_ERROR
 * can be selected. Levels with greater severity than eLOG_LEVEL_ERROR
 * will default to eLOG_LEVEL_ERROR since severity of eLOG_LEVEL_ERROR
 * and higher are non maskable.
 */
void setMdLogLevel(log_level_t logLevel)
{
    setModuleLogLevel(log_handle,logLevel);
}

void lockMDBranch(mdIndex_t mdIndex, uint32_t timeout)
{
#if 0
    mdNum_t	mdNum;
    mdNum = _mdMibIndexToMdNum(mdIndex);

    if (mdNum == -1)
    {
        return(-1);
    }
    osRecursiveMutexTake(mdArray[mdNum].lock,timeout);
    return (0);
#endif
    LOCK_MD_LIST(timeout);
}

void unlockMDBranch(mdIndex_t mdIndex)
{
    //UNLOCK_MD_BRANCH(_mdMibIndexToMdNum(mdIndex),timeout);
    UNLOCK_MD_LIST();
}
