//*****************************************************************************
// Driver for the delta process.
//*****************************************************************************
__checkReturn
HRESULT
CMiniMdRW::ApplyDelta(
    CMiniMdRW &mdDelta) // Interface to MD with the ENC delta.
{
    HRESULT hr = S_OK;
    ULONG   iENC;       // Loop control.
    ULONG   iRid;       // RID of some record.
    ULONG   iNew;       // RID of a new record.
    int     i;          // Loop control.
    ULONG   ixTbl;      // A table.

#ifdef _DEBUG
    if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_ApplyDeltaBreak))
    {
        _ASSERTE(!"CMiniMDRW::ApplyDelta()");
    }
#endif // _DEBUG

    // Init the suppressed column table.  We know this one isn't zero...
    if (m_SuppressedDeltaColumns[TBL_TypeDef] == 0)
    {
        m_SuppressedDeltaColumns[TBL_EventMap]      = (1 << EventMapRec::COL_EventList);
        m_SuppressedDeltaColumns[TBL_PropertyMap]   = (1 << PropertyMapRec::COL_PropertyList);
        m_SuppressedDeltaColumns[TBL_EventMap]      = (1 << EventMapRec::COL_EventList);
        m_SuppressedDeltaColumns[TBL_Method]        = (1 << MethodRec::COL_ParamList);
        m_SuppressedDeltaColumns[TBL_TypeDef]       = (1 << TypeDefRec::COL_FieldList)|(1<<TypeDefRec::COL_MethodList);
    }

    // Verify the version of the MD.
    if (m_Schema.m_major != mdDelta.m_Schema.m_major ||
            m_Schema.m_minor != mdDelta.m_Schema.m_minor)
    {
        _ASSERTE(!"Version of Delta MetaData is a incompatible with current MetaData.");
        //<TODO>@FUTURE: unique error in the future since we are not shipping ENC.</TODO>
        return E_INVALIDARG;
    }

    // verify MVIDs.
    ModuleRec *pModDelta;
    ModuleRec *pModBase;
    IfFailGo(mdDelta.GetModuleRecord(1, &pModDelta));
    IfFailGo(GetModuleRecord(1, &pModBase));
    GUID GuidDelta;
    GUID GuidBase;
    IfFailGo(mdDelta.getMvidOfModule(pModDelta, &GuidDelta));
    IfFailGo(getMvidOfModule(pModBase, &GuidBase));
    if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) && (GuidDelta != GuidBase))
    {
        _ASSERTE(!"Delta MetaData has different base than current MetaData.");
        return E_INVALIDARG;
    }

#ifndef FEATURE_CORECLR
    // Verify that the delta is based on the base.
    IfFailGo(mdDelta.getEncBaseIdOfModule(pModDelta, &GuidDelta));
    IfFailGo(getEncBaseIdOfModule(pModBase,&GuidBase));
    if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) &&
            CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas) &&
            (GuidDelta != GuidBase))
    {
        _ASSERTE(!"The Delta MetaData is based on a different generation than the current MetaData.");
        return E_INVALIDARG;
    }
#endif //!FEATURE_CORECLR    

    // Let the other md prepare for sparse records.
    IfFailGo(mdDelta.StartENCMap());

    // Fix the heaps.
    IfFailGo(ApplyHeapDeltas(mdDelta));

    // Truncate some tables in preparation to copy in new ENCLog data.
    for (i = 0; (ixTbl = m_TruncatedEncTables[i]) != (ULONG)-1; ++i)
    {
        m_Tables[ixTbl].Delete();
        IfFailGo(m_Tables[ixTbl].InitializeEmpty_WithRecordCount(
                     m_TableDefs[ixTbl].m_cbRec,
                     mdDelta.m_Schema.m_cRecs[ixTbl]
                     COMMA_INDEBUG_MD(TRUE)));       // fIsReadWrite
        INDEBUG_MD(m_Tables[ixTbl].Debug_SetTableInfo(NULL, ixTbl));
        m_Schema.m_cRecs[ixTbl] = 0;
    }

    // For each record in the ENC log...
    for (iENC = 1; iENC <= mdDelta.m_Schema.m_cRecs[TBL_ENCLog]; ++iENC)
    {
        // Get the record, and the updated token.
        ENCLogRec *pENC;
        IfFailGo(mdDelta.GetENCLogRecord(iENC, &pENC));
        ENCLogRec *pENC2;
        IfFailGo(AddENCLogRecord(&pENC2, &iNew));
        IfNullGo(pENC2);
        ENCLogRec *pENC3;
        _ASSERTE(iNew == iENC);
        ULONG func = pENC->GetFuncCode();
        pENC2->SetFuncCode(pENC->GetFuncCode());
        pENC2->SetToken(pENC->GetToken());

        // What kind of record is this?
        if (IsRecId(pENC->GetToken()))
        {   // Non-token table
            iRid = RidFromRecId(pENC->GetToken());
            ixTbl = TblFromRecId(pENC->GetToken());
        }
        else
        {   // Token table.
            iRid = RidFromToken(pENC->GetToken());
            ixTbl = GetTableForToken(pENC->GetToken());
        }

        RID rid_Ignore;
        // Switch based on the function code.
        switch (func)
        {
        case eDeltaMethodCreate:
            // Next ENC record will define the new Method.
            MethodRec *pMethodRecord;
            IfFailGo(AddMethodRecord(&pMethodRecord, &rid_Ignore));
            IfFailGo(AddMethodToTypeDef(iRid, m_Schema.m_cRecs[TBL_Method]));
            break;

        case eDeltaParamCreate:
            // Next ENC record will define the new Param.  This record is
            //  tricky because params will be re-ordered based on their sequence,
            //  but the sequence isn't set until the NEXT record is applied.
            //  So, for ParamCreate only, apply the param record delta before
            //  adding the parent-child linkage.
            ParamRec *pParamRecord;
            IfFailGo(AddParamRecord(&pParamRecord, &rid_Ignore));

            // Should have recorded a Param delta after the Param add.
            _ASSERTE(iENC<mdDelta.m_Schema.m_cRecs[TBL_ENCLog]);
            IfFailGo(mdDelta.GetENCLogRecord(iENC+1, &pENC3));
            _ASSERTE(pENC3->GetFuncCode() == 0);
            _ASSERTE(GetTableForToken(pENC3->GetToken()) == TBL_Param);
            IfFailGo(ApplyTableDelta(mdDelta, TBL_Param, RidFromToken(pENC3->GetToken()), eDeltaFuncDefault));

            // Now that Param record is OK, set up linkage.
            IfFailGo(AddParamToMethod(iRid, m_Schema.m_cRecs[TBL_Param]));
            break;

        case eDeltaFieldCreate:
            // Next ENC record will define the new Field.
            FieldRec *pFieldRecord;
            IfFailGo(AddFieldRecord(&pFieldRecord, &rid_Ignore));
            IfFailGo(AddFieldToTypeDef(iRid, m_Schema.m_cRecs[TBL_Field]));
            break;

        case eDeltaPropertyCreate:
            // Next ENC record will define the new Property.
            PropertyRec *pPropertyRecord;
            IfFailGo(AddPropertyRecord(&pPropertyRecord, &rid_Ignore));
            IfFailGo(AddPropertyToPropertyMap(iRid, m_Schema.m_cRecs[TBL_Property]));
            break;

        case eDeltaEventCreate:
            // Next ENC record will define the new Event.
            EventRec *pEventRecord;
            IfFailGo(AddEventRecord(&pEventRecord, &rid_Ignore));
            IfFailGo(AddEventToEventMap(iRid, m_Schema.m_cRecs[TBL_Event]));
            break;

        case eDeltaFuncDefault:
            IfFailGo(ApplyTableDelta(mdDelta, ixTbl, iRid, func));
            break;

        default:
            _ASSERTE(!"Unexpected function in ApplyDelta");
            IfFailGo(E_UNEXPECTED);
            break;
        }
    }
    m_Schema.m_cRecs[TBL_ENCLog] = mdDelta.m_Schema.m_cRecs[TBL_ENCLog];

ErrExit:
    // Store the result for returning (IfFailRet will modify hr)
    HRESULT hrReturn = hr;
    IfFailRet(mdDelta.EndENCMap());

#ifndef FEATURE_CORECLR
    if (SUCCEEDED(hrReturn) &&
            CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_DeltaCheck) &&
            CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_UseMinimalDeltas))
    {
        GUID GuidNewBase;

        // We'll use the delta's 'delta guid' for our new base guid
        IfFailRet(mdDelta.getEncIdOfModule(pModDelta, &GuidNewBase));

        IfFailRet(PutGuid(TBL_Module, ModuleRec::COL_EncBaseId, pModBase, GuidNewBase));
    }
#endif //!FEATURE_CORECLR

    return hrReturn;
} // CMiniMdRW::ApplyDelta