/**
 * Adds a new definition of transaction to txn-database
 */
ClRcT clTxnDbNewTxnDefnAdd(
        CL_IN   ClTxnDbHandleT      txnDb,
        CL_IN   ClTxnDefnT          *pNewTxn,
        CL_IN   ClTxnTransactionIdT *pTxnId)
{
    ClRcT       rc  = CL_OK;
    ClTxnDefnT  *pTxnDef;
    CL_FUNC_ENTER();

    rc = clCntDataForKeyGet(txnDb, (ClCntKeyHandleT) pTxnId, 
                            (ClCntDataHandleT *) &pTxnDef);
    if (CL_OK != rc)
    {
        rc = clCntNodeAdd(txnDb, 
                          (ClCntKeyHandleT) pTxnId, 
                          (ClCntDataHandleT) pNewTxn, 0);
    }
    else
    {
        rc = CL_ERR_DUPLICATE;
    }

    CL_FUNC_EXIT();
    return (CL_GET_ERROR_CODE(rc));
}
/**
 *  Get Node Name given MoId.
 *
 *  API to get Logical NodeName given MOId. 
 *
 *
 *  @param pMoId      MOId of the Blade.
 *  @param nodeName   Node name corresponding to blade MOId
 *
 *  @returns 
 *    CL_OK on success <br/>
 *    CL_COR_SET_RC(CL_COR_ERR_NOT_EXIST) If an entry is not present for given MOId<br/>
 *    CL_COR_SET_RC(CL_COR_ERR_NULL_PTR) if moId is NULL <br/>
 */
ClRcT _clCorMoIdToNodeNameGet(ClCorMOIdPtrT pMoId, ClNameT** nodeName)
{
    ClRcT rc;
    ClUint16T OriginalDepth;
    ClCorMOServiceIdT  svcId;
    ClCorMOClassPathT moclassPath;
    CORMOClass_h moClassH;

	if(pMoId == NULL)
		return CL_COR_SET_RC(CL_COR_ERR_NULL_PTR);

    /* First check if the MOId passed by the user is valid or not*/

    /* The minimum condition that must be satisfied is */
    /* taht the MO class should exist*/
    rc = clCorMoIdToMoClassPathGet(pMoId, &moclassPath);

    if(CL_OK != rc)
    	CL_COR_RETURN_ERROR(CL_DEBUG_ERROR, "\nCould not get MO class path from MOID", rc);

    /* Check if the MO class exists or not */
    rc = corMOClassHandleGet(&moclassPath, &moClassH);
    
    if (rc != CL_OK)
    	{
    	/* The mo class does not exist. Return*/
    	CL_COR_RETURN_ERROR(CL_DEBUG_ERROR, "\nIncorrect path specified. MO class does not exist\n", rc);
    	}

    /* The user has passed MOId, Since we are going to work on MOId */
    /* Passed by the user, let's store the original depth and svc Id */

    OriginalDepth = pMoId->depth;
    svcId = pMoId->svcId;

    pMoId->svcId = CL_COR_INVALID_SVC_ID;

    for(; pMoId->depth>=1; pMoId->depth--)
    	{
    	/* Find if the MOId is present in the Hash Table */
    	rc = clCntDataForKeyGet(moIdToNodeNameTableHandle, (ClCntKeyHandleT) pMoId, (ClCntDataHandleT *) nodeName);
    	if(CL_OK == rc && *nodeName !=NULL )
    		{
    		/* Found the node name for given MOId. Return.*/
    		/* Before returning restore svc Id and depth */
    		pMoId->depth = OriginalDepth;
    		pMoId->svcId = svcId;
    		return CL_OK;
    		}
    	}

    /* If we are here then the node Name could not be found*/
    /* Restore original depth and svcId and return */

    pMoId->depth = OriginalDepth;
    pMoId->svcId = svcId;

    CL_COR_RETURN_ERROR(CL_DEBUG_TRACE, "\n Node Name not found for given MOId.\n", CL_COR_SET_RC(CL_COR_ERR_NOT_EXIST));

}
/**
 * Retrieve txn-definition using txn-id as index
 */
ClRcT clTxnDbTxnDefnGet(
        CL_IN   ClTxnDbHandleT      txnDb, 
        CL_IN   ClTxnTransactionIdT txnId, 
        CL_OUT  ClTxnDefnT          **pTxnDefn)
{
    ClRcT   rc  = CL_OK;
    CL_FUNC_ENTER();

    if (NULL == pTxnDefn)
    {
        CL_FUNC_EXIT();
        return (CL_ERR_NULL_POINTER);
    }

    rc = clCntDataForKeyGet(txnDb, (ClCntKeyHandleT) &txnId, 
                            (ClCntDataHandleT *) pTxnDefn);

    CL_FUNC_EXIT();
    return (CL_GET_ERROR_CODE(rc));
}
ClRcT clAlarmPayloadCntDataGet(ClAlarmProbableCauseT probCause, 
                                    ClAlarmSpecificProblemT specificProblem,
                                    ClCorMOIdPtrT pMoId, 
                                    ClAlarmPayloadCntT **payloadInfo)
{
	ClRcT rc = CL_OK;
	ClCntNodeHandleT nodeH;
    ClAlarmPayloadCntKeyT *pCntKey = clHeapAllocate(sizeof(ClAlarmPayloadCntKeyT));
    if(NULL == pCntKey)
    {
          CL_DEBUG_PRINT (CL_DEBUG_CRITICAL,("Memory allocation failed with rc 0x%x ", rc));
          return (rc);
    }    
    pCntKey->probCause = probCause;
    pCntKey->specificProblem = specificProblem;
    pCntKey->moId = *pMoId;

    clOsalMutexLock(gClAlarmPayloadCntMutex);        
    rc = clCntNodeFind(gPayloadCntHandle,(ClCntKeyHandleT)pCntKey,&nodeH);
	if(CL_OK == rc)
	{
        rc = clCntDataForKeyGet(gPayloadCntHandle, (ClCntKeyHandleT)pCntKey,
									(ClCntDataHandleT*)payloadInfo);
        if (CL_OK != rc)
        {
            CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("clCntDataForKeyGet failed with rc 0x%x\n", rc));
        }
    }
	else
	{
		CL_DEBUG_PRINT(CL_DEBUG_INFO, ("Node does not exist with rc:0x%x\n", rc));
	}
    clHeapFree(pCntKey);
    clOsalMutexUnlock(gClAlarmPayloadCntMutex);    
	return rc;
}
ClRcT clCkptMasterPeerUpdateNoLock(ClIocPortT        portId, 
                                   ClUint32T         flag, 
                                   ClIocNodeAddressT localAddr,
                                   ClUint8T          credential) 
{
    ClRcT              rc           = CL_OK;
    CkptPeerInfoT      *pPeerInfo   = NULL;
    CkptNodeListInfoT  *pPeerInfoDH = NULL;
    ClCntNodeHandleT   nodeHdl      = 0;
    ClCntNodeHandleT   tempHdl      = 0;
    ClHandleT         *pMasterHandle  = NULL;
    
    /*
     * Check whether node/component is coming up or going down.
     */
    if(flag == CL_CKPT_SERVER_UP)
    {
        /*
         * Checkpoint server up scenario.
         */
         clLogDebug(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_ANNOUNCE,
                   "Received welcome message from master, updating the peerlist for [%d]",
                    localAddr);

         /* Reset the replica list for peer being welcomed without knowing the peer is available or not */
         if(localAddr != gCkptSvr->localAddr)
         {
             clLogNotice("PEER", "UPDATE",
                         "Resetting the replica list for the peer [%#x] being welcomed", localAddr);
             clCkptMasterReplicaListUpdateNoLock(localAddr);
         }

        /* 
         * Add an entry to the peer list if not existing.
         * Mark the node as "available" i.e. available for checkpoint 
         * operations like storing replicas etc..
         */
        rc = clCntDataForKeyGet( gCkptSvr->masterInfo.peerList,
                                 (ClPtrT)(ClWordT)localAddr,
                                 (ClCntDataHandleT *)&pPeerInfo);
        if( rc == CL_OK && pPeerInfo != NULL)
        {
            CL_ASSERT(pPeerInfo->ckptList != 0);
            pPeerInfo->credential = credential;
            pPeerInfo->available  = CL_CKPT_NODE_AVAIL;

            if(localAddr != gCkptSvr->localAddr)
            {
                pPeerInfo->replicaCount = 0;
            }
        }
        else
        {
            if( CL_OK !=( rc = _ckptMasterPeerListInfoCreate(localAddr, 
                            credential,0)))
            {
                return rc;
            }
        }        
    }
    else
    {
        /*
         * Node/component down scenario.
         */
        clLogDebug(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_DOWN, 
                   "Updating the peerAddr [%d] for down notification",
                   localAddr);
        /* 
         * Find the corresponding entry from the peer list.
         */
        if( CL_OK != (rc = clCntDataForKeyGet(gCkptSvr->masterInfo.peerList,
                                             (ClCntKeyHandleT)(ClWordT)localAddr,
                                             (ClCntDataHandleT *) &pPeerInfo)))
        {
            rc = CL_OK;
            goto exitOnError;
        }

        if( flag != CL_CKPT_COMP_DOWN)
        {
            clLogDebug(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_DOWN,
                       "Either ckpt server or node down, "
                       "changing active address");
                    
            clCntFirstNodeGet(pPeerInfo->mastHdlList,&nodeHdl);
            tempHdl = 0;
            while(nodeHdl != 0)
            {
                rc = clCntNodeUserKeyGet(pPeerInfo->mastHdlList,nodeHdl,
                                    (ClCntKeyHandleT *)&pMasterHandle);
                if( CL_OK != rc )
                {
                    clLogError(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_DOWN, 
                            "Not able get the data for node handle rc[0x %x]",
                            rc);
                    goto exitOnError;
                }
                rc = clCntNextNodeGet(pPeerInfo->mastHdlList, nodeHdl, 
                                      &tempHdl);
                /*
                 * Update the active address and inform the clients.
                 */
                if( CL_OK != (rc = _clCkpMastertReplicaAddressUpdate(*pMasterHandle, 
                                                                 localAddr)))
                {
                    return rc;
                }
                nodeHdl = tempHdl;
                tempHdl = 0;
            }
        }
        
        if (flag != CL_CKPT_SVR_DOWN)
        {
            /* 
             * Component down/ node down case.
             * In case of component down close the associated client Hdl.
             * Incase of node down close all client Hdl.
             * Delete the ckpt Hdls from the client handle List.
             */
            clLogDebug(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_DOWN, 
                  "Closing the opened handles from this slot id [%d]...", 
                   localAddr);
            clCntFirstNodeGet(pPeerInfo->ckptList,&nodeHdl);
            while(nodeHdl != 0)
            {
                rc = clCntNodeUserDataGet(pPeerInfo->ckptList,nodeHdl,
                        (ClCntDataHandleT *)&pPeerInfoDH);
                if( CL_OK != rc )
                {
                    clLogError(CL_CKPT_AREA_MAS_DEP, CL_CKPT_CTX_PEER_DOWN, 
                            "Not able get the data for node handle rc[0x %x]",
                            rc);
                    goto exitOnError;
                }
                clCntNextNodeGet(pPeerInfo->ckptList,nodeHdl,&tempHdl);
                if ( (flag == CL_CKPT_COMP_DOWN && 
                     pPeerInfoDH->appPortNum == portId) || 
                     (flag == CL_CKPT_NODE_DOWN) )
                {
                    /*
                     * Close the checkpoint hdl but dont delete the entry from
                     * masterHdl list.
                     */
                    if(gCkptSvr->masterInfo.masterAddr == 
                                    gCkptSvr->localAddr) 
                    {                                    
                        clLogInfo(CL_CKPT_AREA_MAS_DEP,
                                  CL_CKPT_CTX_PEER_DOWN, 
                                  "Closing the handle [%#llX]...", 
                                  pPeerInfoDH->clientHdl);
                        _clCkptMasterCloseNoLock(pPeerInfoDH->clientHdl, 
                        localAddr, !CL_CKPT_MASTER_HDL); 
                    }    
                }
                nodeHdl = tempHdl;
                tempHdl = 0; 
            }
        }
        else if (flag == CL_CKPT_SVR_DOWN)
        {
            /*
             * Mark the availability of checkpoint server as UNAVAILABLE.
             */
            if(pPeerInfo->credential == CL_CKPT_CREDENTIAL_POSITIVE)
                gCkptSvr->masterInfo.availPeerCount--;
            pPeerInfo->available = CL_CKPT_NODE_UNAVAIL;
        }   

        if(flag == CL_CKPT_NODE_DOWN
           ||
           flag == CL_CKPT_SVR_DOWN)
        {
            
            /*
             * Node down case, delete the entry from master's peer list.
             */
            rc = clCntAllNodesForKeyDelete(gCkptSvr->masterInfo.peerList,
                                (ClPtrT)(ClWordT)localAddr);
             CKPT_ERR_CHECK(CL_CKPT_SVR,CL_LOG_SEV_ERROR,
             (" MasterPeerUpdate failed rc[0x %x]\n",rc),
             rc);

        }
        
        if( flag != CL_CKPT_COMP_DOWN)
        {
            /*
             * Find other nodes to store the replicas of checkpoints for whom
             * this node was storing the replicas.
             */
             if(gCkptSvr->masterInfo.masterAddr == gCkptSvr->localAddr)
             {
                 _ckptCheckpointLoadBalancing();
             }
        }
    }
exitOnError:
    {
        return rc;
    }
}  
/**
 * API to un-register a service with transaction-agent
 */
ClRcT clTxnAgentServiceUnRegister(CL_IN ClTxnAgentServiceHandleT tHandle)
{
    ClRcT                       rc = CL_OK;
    ClUint8T                    twoPC = 0;
    ClTxnAgentCompServiceInfoT  *pCompService = NULL;


    CL_FUNC_ENTER();

    if (tHandle == 0x0)
    {
        CL_FUNC_EXIT();
        return CL_TXN_RC(CL_ERR_INVALID_HANDLE);
    }
    /*
       This is a request from service hosted in the component to unregister
       from the transaction-management.
       Remove the entry from the data-structure and invalidate the handle

       FIXME: Before actually deleting it, check to see if this service is 
              part of any active txn or not
    */

    rc = clCntDataForKeyGet(clTxnAgntCfg->compServiceMap, 
                            (ClCntKeyHandleT) &(((ClTxnAgentCompServiceInfoT *)tHandle)->serviceType),
                            (ClCntDataHandleT *)&pCompService);
    if (CL_OK == rc)
    {
        ClUint32T   srvCount;
        if (pCompService->serviceCapability == CL_TXN_AGENT_SERVICE_1PC)
        {
            twoPC = 0;
            clTxnAgntCfg->agentCapability &= ~(CL_TXN_AGENT_SERVICE_1PC);
        }
        else if(pCompService->serviceCapability == CL_TXN_AGENT_SERVICE_2PC)
        {
            twoPC = 1;
        }

        rc = clCntAllNodesForKeyDelete(clTxnAgntCfg->compServiceMap,
                                      (ClCntKeyHandleT) &(((ClTxnAgentCompServiceInfoT *)tHandle)->serviceType));
        if(CL_OK != rc)
        {
            clLogError("AGT", NULL,
                    "Failed to delete node from compServiceMap corresponding to service[%d]", 
                    ((ClTxnAgentCompServiceInfoT *)tHandle)->serviceType);
            return rc;
        }
                                        
        /* Reset agent capability, if necessary */
        rc = clCntSizeGet(clTxnAgntCfg->compServiceMap, &srvCount);
        if ( (CL_OK == rc) && (srvCount == 0x0) )
        {
            clTxnAgntCfg->agentCapability = CL_TXN_AGENT_NO_SERVICE_REGD;
        } 
        else if ( (CL_OK == rc) && (srvCount == 0x1) && 
                  ( (clTxnAgntCfg->agentCapability & CL_TXN_AGENT_SERVICE_1PC) == CL_TXN_AGENT_SERVICE_1PC) )
        {
            clTxnAgntCfg->agentCapability = CL_TXN_AGENT_SERVICE_1PC;
        }
        else if ( CL_OK != rc )
        {
            CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Error while reading number of service registered. rc:0x%x", rc));
        }
    }
    if(CL_OK == rc)
        clLogNotice("AGT", "FIN",
                "Unregistering [%s] service successfull", twoPC ? "2PC":"READ"); 
    CL_TXN_RETURN_RC(rc, ("Failed to unregister component-service rc:0x%x\n", rc));
}
/**
 * API to register a service with transaction agent
 */
ClRcT clTxnAgentServiceRegister(
        CL_IN   ClInt32T                    serviceId, 
        CL_IN   ClTxnAgentCallbacksT        tCallback, 
        CL_OUT  ClTxnAgentServiceHandleT    *pHandle)
{
    /*
       This is registration request from a service hosted in this component.
       (There could be multiple such services).
       Store these callbacks in an appropriate data-structure indexed with 
       service-id (identication of service under considered).
    */
    ClRcT                       rc                  = CL_OK;
    ClUint8T                    serviceCapability   = 0xFF;
    ClTxnAgentCompServiceInfoT  *pNewCompService    = NULL;

    CL_FUNC_ENTER();

    CL_TXN_NULL_CHECK_RETURN(pHandle, CL_ERR_NULL_POINTER, ("Invalid handle\n"));
    if (NULL == clTxnAgntCfg)
    {
        clLogWrite(CL_LOG_HANDLE_APP, CL_LOG_ERROR, CL_TXN_AGENT_LIB,
                   CL_LOG_MESSAGE_0_COMPONENT_UNINITIALIZED);
        CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Agent library is not initialized. clTxnAgntCfg is NULL\n"));
        CL_FUNC_EXIT();
        return CL_TXN_RC(CL_ERR_NOT_INITIALIZED);
    }

    /* Validate callback function */
    if( (tCallback.fpTxnAgentJobPrepare == NULL) && 
        (tCallback.fpTxnAgentJobRollback == NULL) &&
        (tCallback.fpTxnAgentJobCommit != NULL) )
    {
        serviceCapability = CL_TXN_AGENT_SERVICE_1PC;
        clTxnMutexLock(clTxnAgntCfg->actMtx);
        /* Check if this is not the first one to declare as 1-PC Capable service */
        if ( (clTxnAgntCfg->agentCapability & 
                CL_TXN_AGENT_SERVICE_1PC) == CL_TXN_AGENT_SERVICE_1PC)
        {
            CL_DEBUG_PRINT(CL_DEBUG_ERROR, 
                    ("Txn-Agent does not allow more than one service of 1-PC Type"));
            CL_FUNC_EXIT();
            clTxnMutexUnlock(clTxnAgntCfg->actMtx);
            return CL_TXN_RC(CL_ERR_INVALID_PARAMETER);
        }
        clTxnMutexUnlock(clTxnAgntCfg->actMtx);
    }
    else if ( (tCallback.fpTxnAgentJobPrepare != NULL) &&
              (tCallback.fpTxnAgentJobCommit != NULL)  &&
              (tCallback.fpTxnAgentJobRollback != NULL) )
    {
        serviceCapability = CL_TXN_AGENT_SERVICE_2PC;
    }
    else
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Invalid parameter - callback function not defined properly"));
        CL_FUNC_EXIT();
        return CL_TXN_RC(CL_ERR_INVALID_PARAMETER);
    }

    /* Check if entry already exists or not */
    rc = clCntDataForKeyGet( (ClCntHandleT) clTxnAgntCfg->compServiceMap, 
                              (ClCntKeyHandleT) &serviceId, 
                              (ClCntDataHandleT *) &pNewCompService);
    if ( CL_OK == rc )
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Component-service already registered\n"));
        CL_FUNC_EXIT();
        return (CL_TXN_RC(CL_ERR_DUPLICATE));
    }

    pNewCompService = (ClTxnAgentCompServiceInfoT *) clHeapAllocate(sizeof(ClTxnAgentCompServiceInfoT));
    CL_TXN_NULL_CHECK_RETURN(pNewCompService, CL_ERR_NO_MEMORY, 
                             ("Failed to allocate memory\n"));

    memset(pNewCompService, 0, sizeof(ClTxnAgentCompServiceInfoT));
    pNewCompService->serviceType = serviceId;
    pNewCompService->serviceCapability = serviceCapability;

    pNewCompService->pCompCallbacks = 
        (ClTxnAgentCallbacksT *) clHeapAllocate(sizeof(ClTxnAgentCallbacksT));
    
    
    CL_TXN_NULL_CHECK_RETURN(pNewCompService->pCompCallbacks, CL_ERR_NO_MEMORY,
                             ("Failed to allocate memory\n"));
    memcpy(pNewCompService->pCompCallbacks, &tCallback, sizeof(ClTxnAgentCallbacksT));

    /* Put the entry into hash-map with service-id to be the key */
    rc = clCntNodeAdd(clTxnAgntCfg->compServiceMap, 
                       (ClCntKeyHandleT) &(pNewCompService->serviceType), 
                       (ClCntDataHandleT)pNewCompService, NULL);

    if (CL_OK != rc)
    {
        clHeapFree(pNewCompService->pCompCallbacks);
        clHeapFree(pNewCompService);
    }
    else 
    {
        /* Update agent-cfg */
        if (serviceCapability == CL_TXN_AGENT_SERVICE_1PC)
        {
#ifdef CL_TXN_DEBUG
            CL_DEBUG_PRINT(CL_DEBUG_ERROR,( "Service registered is 1pc\n"));
#endif
            clTxnAgntCfg->agentCapability |= CL_TXN_AGENT_SERVICE_1PC;
        }
        else
            clTxnAgntCfg->agentCapability |= CL_TXN_AGENT_SERVICE_2PC;

        *pHandle = (ClTxnAgentServiceHandleT )pNewCompService;
        clLogNotice("AGT", "INI",
                "Registered [%s] service successfully having serviceId [%d]", 
                (serviceCapability == CL_TXN_AGENT_SERVICE_1PC)?"READ": "2P",
                serviceId);
    }
    CL_TXN_RETURN_RC(rc, ("Failed to register new component-service rc:0x%x\n", rc));
}