BOOLEAN CopyArgumentGroup(_Out_ OVS_ARGUMENT_GROUP* pDest, _In_ const OVS_ARGUMENT_GROUP* pSource, UINT16 argsMore)
{
    OVS_CHECK(pDest);
    OVS_CHECK(pSource);

    AllocateArgumentsToGroup(pSource->count + argsMore, pDest);

    pDest->count = pSource->count + argsMore;
    pDest->groupSize = pSource->groupSize;

    for (UINT i = 0; i < pSource->count; ++i)
    {
        if (!CopyArgument(pDest->args + i, pSource->args + i))
        {
            return FALSE;
        }
    }

    return TRUE;
}
//TODO: should we put OVS_MESSAGE_FLAG_MULTIPART for Flow_Dump?
BOOLEAN CreateMsgFromFlow(_In_ const OVS_FLOW* pFlow, UINT8 command, _Inout_ OVS_MESSAGE* pMsg, UINT32 sequence, UINT32 dpIfIndex, UINT32 portId)
{
    OVS_ARGUMENT_GROUP* pFlowGroup = NULL;
    OVS_ARGUMENT* pPIArg, *pMasksArg, *pTimeUsedArg, *pFlowStats, *pTcpFlags, *pActionsArg;
    BOOLEAN ok = TRUE;
    UINT16 flowArgCount = 0;
    UINT16 curArg = 0;
    OVS_WINL_FLOW_STATS winlStats = { 0 };
    OVS_FLOW_STATS stats = { 0 };
    UINT64 tickCount = 0;
    UINT8 tcpFlags = 0;
    UINT16 argsDataSize = 0;
    LOCK_STATE_EX lockState = { 0 };

    OVS_OFPACKET_INFO unmaskedPacketInfo = { 0 };
    OVS_OFPACKET_INFO maskedPacketInfo = { 0 };
    OVS_OFPACKET_INFO packetInfoMask = { 0 };

    OVS_CHECK(pMsg);

    pPIArg = pMasksArg = pTimeUsedArg = pFlowStats = pTcpFlags = pActionsArg = NULL;

    FLOW_LOCK_READ(pFlow, &lockState);

    unmaskedPacketInfo = pFlow->unmaskedPacketInfo;
    maskedPacketInfo = pFlow->maskedPacketInfo;
    packetInfoMask = pFlow->pMask->packetInfo;

#if OVS_VERSION == OVS_VERSION_1_11
    tickCount = pFlow->stats.lastUsedTime;
    stats.noOfMatchedPackets = pFlow->stats.packetsMached;
    stats.noOfMatchedBytes = pFlow->stats.bytesMatched;
    tcpFlags = pFlow->stats.tcpFlags;
#elif OVS_VERSION == OVS_VERSION_2_3
    //TODO: Flow_GetStats()
    Flow_GetStats_Unsafe(pFlow, &stats);
    winlStats.noOfMatchedBytes = stats.bytesMatched;
    winlStats.noOfMatchedPackets = stats.packetsMached;
#endif

    FLOW_UNLOCK(pFlow, &lockState);

    //2. INIT OVS_MESSAGE
    pMsg->length = sizeof(OVS_MESSAGE);
    pMsg->type = OVS_MESSAGE_TARGET_FLOW;
    pMsg->flags = 0;
    pMsg->sequence = sequence;
    pMsg->pid = portId;

    pMsg->command = command;
    pMsg->version = 1;
    pMsg->reserved = 0;

    pMsg->dpIfIndex = dpIfIndex;

    //3. OVS_ARGUMENT_GROUP
    pFlowGroup = KZAlloc(sizeof(OVS_ARGUMENT_GROUP));
    if (!pFlowGroup)
    {
        return FALSE;
    }

    //3.1. Packet Info
    pPIArg = CreateArgFromPacketInfo(&unmaskedPacketInfo, NULL, OVS_ARGTYPE_FLOW_PI_GROUP);
    if (!pPIArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    argsDataSize += pPIArg->length;
    ++curArg;

    //3.2. Packet Info Mask
    pMasksArg = CreateArgFromPacketInfo(&maskedPacketInfo, &packetInfoMask, OVS_ARGTYPE_FLOW_MASK_GROUP);
    if (!pMasksArg)
    {
        ok = FALSE;
        goto Cleanup;
    }

    argsDataSize += pMasksArg->length;
    ++curArg;

    //3.3. Flow Time Used
    if (tickCount > 0)
    {
        UINT64 usedTimeInMs = 0, curTimeInMs = 0;

        usedTimeInMs = _TicksToMiliseconds(tickCount);
        curTimeInMs = _TicksToMiliseconds(KeQueryPerformanceCounter(NULL).QuadPart);

        pTimeUsedArg = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_TIME_USED, &usedTimeInMs);
        if (!pTimeUsedArg)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pTimeUsedArg->length;
        ++curArg;
    }

    //3.4. Flow Stats
    if (winlStats.noOfMatchedPackets > 0)
    {
        pFlowStats = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_STATS, &stats);
        if (!pFlowStats)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pFlowStats->length;
        ++curArg;
    }

    //3.5. Flow Tcp Flags
    if (tcpFlags)
    {
        pTcpFlags = CreateArgument_Alloc(OVS_ARGTYPE_FLOW_TCP_FLAGS, &tcpFlags);
        if (!pTcpFlags)
        {
            ok = FALSE;
            goto Cleanup;
        }

        argsDataSize += pTcpFlags->length;
        ++curArg;
    }

    FLOW_LOCK_READ(pFlow, &lockState);
    //NOTE: we don't need to use OVS_REFERENCE for pFlow->pActions here
    //because the actions cannot be deleted while under the lock of pFlow
    //pFlow is here referenced, so it and its Actions cannot be deleted
    pActionsArg = _CreateFlowActionsGroup(pFlow->pActions->pActionGroup);
    FLOW_UNLOCK(pFlow, &lockState);

    if (!pActionsArg)
    {
        return FALSE;
    }

    DBGPRINT_ARG(LOG_INFO, pActionsArg, 0, 0);

    argsDataSize += pActionsArg->length;
    ++curArg;

    flowArgCount = curArg;
    if (!AllocateArgumentsToGroup(flowArgCount, pFlowGroup))
    {
        ok = FALSE;
        goto Cleanup;
    }

    pFlowGroup->args[0] = *pPIArg;
    pFlowGroup->args[1] = *pMasksArg;

    curArg = 2;

    if (pTimeUsedArg)
    {
        pFlowGroup->args[curArg] = *pTimeUsedArg;
        curArg++;
    }

    if (pFlowStats)
    {
        pFlowGroup->args[curArg] = *pFlowStats;
        curArg++;
    }

    if (pTcpFlags)
    {
        pFlowGroup->args[curArg] = *pTcpFlags;
        curArg++;
    }

    pFlowGroup->args[curArg] = *pActionsArg;
    ++curArg;

    pFlowGroup->groupSize += argsDataSize;
    pMsg->pArgGroup = pFlowGroup;

Cleanup:
    VerifyGroup_Size_Recursive(pMsg->pArgGroup);

    if (ok)
    {
        KFree(pPIArg);
        KFree(pMasksArg);
        KFree(pTimeUsedArg);
        KFree(pFlowStats);
        KFree(pTcpFlags);
        KFree(pActionsArg);
    }
    else
    {
        FreeGroupWithArgs(pFlowGroup);

        DestroyArgument(pPIArg);
        DestroyArgument(pMasksArg);
        DestroyArgument(pTimeUsedArg);
        DestroyArgument(pFlowStats);
        DestroyArgument(pTcpFlags);
        DestroyArgument(pActionsArg);
    }

    return ok;
}