/**
 * This is a finalization API for transaction agent. This API must be called
 * during finalization of EO.
 */
ClRcT clTxnAgentFinalize()
{
    ClRcT rc = CL_OK;
    ClEoExecutionObjT   *pEOObj;


    CL_FUNC_ENTER();
    /* - Release all allocated memeory 
       FIXME: Check for active transaction, and try to consolidate
    */

    if (clTxnAgntCfg != NULL)
    {
        clTxnDbFini(clTxnAgntCfg->activeTxnMap);

        clCntDelete(clTxnAgntCfg->compServiceMap);
        clTxnMutexDelete(clTxnAgntCfg->actMtx);
        clHeapFree(clTxnAgntCfg);

        clTxnCommIfcFini();

        rc = clEoMyEoObjectGet(&pEOObj);
        clEoClientUninstallTables(pEOObj, 
                                 CL_EO_SERVER_SYM_MOD(gAspFuncTable, TXNAgent));

        clTxnAgntCfg = NULL;
    }
    CL_TXN_RETURN_RC(rc, ("Failed to finalize transaction-agent rc:0x%x\n", rc));
}
/**
 * Internal function to pack/prepare state oa given transaction
 */
static ClRcT _clTxnServiceCkptTxnPack(
    CL_IN   ClUint32T   dataSetId,
    CL_OUT  ClAddrT     *ppData,
    CL_OUT  ClUint32T   *pDataLen,
    CL_IN   ClPtrT      cookie)
{
    ClRcT                   rc = CL_OK;
    ClTxnDefnT              *pTxnDefn;
    ClBufferHandleT  txnStateBuf;
    ClUint32T               msgLen;

    CL_FUNC_ENTER();

    pTxnDefn = (ClTxnDefnT *) cookie;

    if (pTxnDefn == NULL)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Null argument"));
        CL_FUNC_EXIT();
        return (CL_ERR_NULL_POINTER);
    }

    CL_DEBUG_PRINT(CL_DEBUG_TRACE,
                   ("Packing for data-set 0x%x for txn[0x%x:0x%x]", dataSetId,
                    pTxnDefn->serverTxnId.txnMgrNodeAddress, pTxnDefn->serverTxnId.txnId));

    /* Validate between dataSetId and txn-Id */
#if 0
    if ( (dataSetId  - CL_TXN_CKPT_TXN_DATA_SET_ID_OFFSET) != pTxnId->txnId)
    {
        CL_TXN_RETURN_RC(CL_ERR_INVALID_PARAMETER,
                         ("Invalid data-set(0x%x) for transaction-id(0x%x)\n",
                          dataSetId, pTxnId->txnId));
    }
#endif

    rc = clBufferCreate (&txnStateBuf);

    if (CL_OK == rc)
    {
        rc = clTxnStreamTxnCfgInfoPack (pTxnDefn, txnStateBuf);

        *ppData = NULL;
        *pDataLen = 0;

        if (CL_OK == rc)
        {
            /* Copy the state-information from message-buffer to ppData */
            rc = clBufferLengthGet(txnStateBuf, &msgLen);
        }

        if (CL_OK == rc)
        {
            *pDataLen = msgLen;
            *ppData = (ClInt8T *) clHeapAllocate(msgLen);
            if ( *ppData == NULL )
            {
                CL_DEBUG_PRINT(CL_DEBUG_ERROR, ("Failed to allocate memory"));
                rc = CL_ERR_NO_MEMORY;
            }
        }

        if (CL_OK == rc)
        {
            rc = clBufferNBytesRead(txnStateBuf, (ClUint8T *) *ppData, &msgLen);
            if (CL_OK != rc)
            {
                clHeapFree(*ppData);
                *pDataLen = 0x0;
                CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                               ("Failed to pack/serialize txn-defn[0x%x:0x%x] for ckpt. rc:0x%x",
                                pTxnDefn->serverTxnId.txnMgrNodeAddress, pTxnDefn->serverTxnId.txnId, rc));
                rc = CL_GET_ERROR_CODE(rc);
            }


        }

        rc = clBufferDelete(&txnStateBuf);

    }
    CL_FUNC_EXIT();
    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));
}