/**
 * This method is invoked by the driver whenever a packet arrives.
 * It is the top-level dispatching method for dealing with incoming
 * packets, both for requests and responses.
 *
 * \param received
 *      Information about the new packet.
 */
void
BasicTransport::IncomingPacketHandler::handlePacket(Driver::Received* received)
{
    // The following method retrieves a header from a packet
    CommonHeader* common = received->getOffset<CommonHeader>(0);
    if (common == NULL) {
        RAMCLOUD_CLOG(WARNING, "packet from %s too short (%u bytes)",
                received->sender->toString().c_str(), received->len);
        return;
    }

    if (!(common->flags & FROM_CLIENT)) {
        // This packet was sent by the server, and it pertains to an RPC
        // for which we are the client.
        ClientRpcMap::iterator it = t->outgoingRpcs.find(
                common->rpcId.sequence);
        if (it == t->outgoingRpcs.end()) {
            LOG(NOTICE, "Received packet from %s for %s RPC: %s",
                    received->sender->toString().c_str(),
                    (common->rpcId.sequence < t->nextSequenceNumber)
                    ? "completed" : "unknown",
                    headerToString(common, received->len).c_str());
            return;
        }
        ClientRpc* clientRpc = it->second;
        clientRpc->silentIntervals = 0;
        switch (common->opcode) {
            case PacketOpcode::ALL_DATA: {
                // This RPC is now finished.
                AllDataHeader* header = received->getOffset<AllDataHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
                uint32_t length;
                char *payload = received->steal(&length);
                uint32_t requiredLength =
                        downCast<uint32_t>(header->messageLength) +
                        sizeof32(AllDataHeader);
                if (length < requiredLength) {
                    t->driver->release(payload);
                    RAMCLOUD_CLOG(WARNING, "ALL_DATA response from %s too "
                            "short (got %u bytes, expected %u)",
                            received->sender->toString().c_str(),
                            received->len, requiredLength);
                    return;
                }
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "client received ALL_DATA, sequence %u, length %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        length);
#endif
                Driver::PayloadChunk::appendToBuffer(clientRpc->response,
                        payload + sizeof32(AllDataHeader),
                        header->messageLength, t->driver, payload);
                t->outgoingRpcs.erase(header->common.rpcId.sequence);
                clientRpc->notifier->completed();
                t->clientRpcPool.destroy(clientRpc);
                return;
            }

            case PacketOpcode::DATA: {
                DataHeader* header = received->getOffset<DataHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record("client received DATA, "
                        "sequence %u, offset %u, length %u, flags %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset, received->len, header->common.flags);
#endif
                if (!clientRpc->accumulator) {
                    clientRpc->accumulator.construct(t, clientRpc->response);
                }
                clientRpc->accumulator->addPacket(received, header);
                if (clientRpc->response->size() >= header->totalLength) {
                    // Response complete.
                    if (clientRpc->response->size()
                            > header->totalLength) {
                        // We have more bytes than we want. This can happen
                        // if the last packet gets padded by the network
                        // layer to meet minimum size requirements. Just
                        // truncate the response.
                        clientRpc->response->truncate(header->totalLength);
                    }
                    t->outgoingRpcs.erase(header->common.rpcId.sequence);
                    clientRpc->notifier->completed();
                    t->clientRpcPool.destroy(clientRpc);
                } else {
                    // See if we need to output a GRANT.
                    if ((header->common.flags & NEED_GRANT) &&
                            (clientRpc->grantOffset <
                            (clientRpc->response->size() +
                            clientRpc->session->roundTripBytes)) &&
                            (clientRpc->grantOffset < header->totalLength)) {
                        clientRpc->grantOffset = clientRpc->response->size()
                                + clientRpc->session->roundTripBytes
                                + t->grantIncrement;
#if TIME_TRACE
                        TimeTrace::globalTimeTrace->record(
                                "client sending GRANT, sequence %u, offset %u",
                                downCast<uint32_t>(
                                header->common.rpcId.sequence),
                                clientRpc->grantOffset);
#endif
                        GrantHeader grant(header->common.rpcId,
                                clientRpc->grantOffset, FROM_CLIENT);
                        t->driver->sendPacket(clientRpc->session->serverAddress,
                                &grant, NULL);
                    }
                }
                if ((header->offset < clientRpc->resendLimit) &&
                        !(header->common.flags & RETRANSMISSION)) {
                    LOG(NOTICE, "Original data arrived from server %s after "
                            "RESEND: sequence %lu, offset %u, "
                            "resendLimit %u",
                            received->sender->toString().c_str(),
                            common->rpcId.sequence, header->offset,
                            clientRpc->resendLimit);
                    originalCount++;
                    if (originalCount == 10) {
                        TimeTrace::globalTimeTrace->record(
                                "Original data arrived after resend, sequence "
                                "%u, offset %u",
                                downCast<uint32_t>(
                                header->common.rpcId.sequence),
                                clientRpc->grantOffset);
                        TimeTrace::globalTimeTrace->printToLogBackground(
                                t->context->dispatch);
                        LogTimeTraceHeader logHeader(header->common.rpcId,
                                FROM_CLIENT);
                        t->driver->sendPacket(clientRpc->session->serverAddress,
                                &logHeader, NULL);
                    }
                }
                return;
            }

            case PacketOpcode::GRANT: {
                GrantHeader* header = received->getOffset<GrantHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "client received GRANT, sequence %u, offset %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset);
#endif
                if (header->offset > clientRpc->transmitOffset) {
                    t->sendBytes(clientRpc->session->serverAddress,
                            header->common.rpcId, clientRpc->request,
                            clientRpc->transmitOffset,
                            header->offset - clientRpc->transmitOffset,
                            FROM_CLIENT);
                    clientRpc->transmitOffset = header->offset;
                }
                return;
            }

            case PacketOpcode::RESEND: {
                ResendHeader* header = received->getOffset<ResendHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record("client received RESEND, "
                        "sequence %u, offset %u, length %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset, header->length);
#endif
                if (header->common.flags & RESTART) {
                    clientRpc->transmitOffset = 0;
                    clientRpc->grantOffset = 0;
                    clientRpc->resendLimit = 0;
                    clientRpc->accumulator.destroy();
                }
                t->sendBytes(clientRpc->session->serverAddress,
                        header->common.rpcId, clientRpc->request,
                        header->offset, header->length,
                        FROM_CLIENT|RETRANSMISSION);
                uint32_t resendEnd = header->offset + header->length;
                if (resendEnd > clientRpc->transmitOffset) {
                    clientRpc->transmitOffset = resendEnd;
                }
                return;
            }

            case PacketOpcode::RETRY: {
                LOG(NOTICE, "Client received RETRY from %s for sequence %lu",
                        received->sender->toString().c_str(),
                        common->rpcId.sequence);
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "client received RETRY, sequence %u",
                        downCast<uint32_t>(common->rpcId.sequence));
#endif
                Service::prepareRetryResponse(clientRpc->response,
                        0, 0, "BasicTransport suffered packet loss after "
                        "server freed its state");
                t->outgoingRpcs.erase(common->rpcId.sequence);
                clientRpc->notifier->completed();
                t->clientRpcPool.destroy(clientRpc);
                return;
            }

            case PacketOpcode::LOG_TIME_TRACE: {
                TimeTrace::globalTimeTrace->record(
                        "client received LOG_TIME_TRACE for sequence %u",
                        downCast<uint32_t>(common->rpcId.sequence));
                TimeTrace::globalTimeTrace->printToLog();
                return;
            }

            default:
            RAMCLOUD_CLOG(WARNING,
                    "unexpected opcode %s received from server %s",
                    opcodeSymbol(common->opcode).c_str(),
                    received->sender->toString().c_str());
            return;
        }
    } else {
        // This packet was sent by the client; it relates to an RPC
        // for which we are the server.

        // Find the record for this RPC, if one exists.
        ServerRpc* serverRpc = NULL;
        ServerRpcMap::iterator it = t->incomingRpcs.find(common->rpcId);
        if (it != t->incomingRpcs.end()) {
            serverRpc = it->second;
            serverRpc->silentIntervals = 0;
        }

        switch (common->opcode) {
            case PacketOpcode::ALL_DATA: {
                // Common case: the entire request fit in a single packet.

                AllDataHeader* header = received->getOffset<AllDataHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "server received ALL_DATA, sequence %u, length %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        received->len);
#endif
                if (serverRpc != NULL) {
                    // This shouldn't normally happen: it means this packet is
                    // a duplicate, so we can just discard it.
                    return;
                }
                uint32_t length;
                char *payload = received->steal(&length);
                uint32_t requiredLength =
                        downCast<uint32_t>(header->messageLength) +
                        sizeof32(AllDataHeader);
                if (length < requiredLength) {
                    t->driver->release(payload);
                    RAMCLOUD_CLOG(WARNING, "ALL_DATA request from %s too "
                            "short (got %u bytes, expected %u)",
                            received->sender->toString().c_str(),
                            received->len, requiredLength);
                    return;
                }
                serverRpc = t->serverRpcPool.construct(t, received->sender,
                        header->common.rpcId);
                t->incomingRpcs[header->common.rpcId] = serverRpc;
                Driver::PayloadChunk::appendToBuffer(&serverRpc->requestPayload,
                        payload + sizeof32(AllDataHeader),
                        header->messageLength, t->driver, payload);
                serverRpc->requestComplete = true;
                t->context->workerManager->handleRpc(serverRpc);
                return;
            }

            case PacketOpcode::DATA: {
                DataHeader* header = received->getOffset<DataHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record("server received DATA, "
                        "sequence %u, offset %u, length %u, flags %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset, received->len, header->common.flags);
#endif
                if (serverRpc == NULL) {
                    serverRpc = t->serverRpcPool.construct(t,
                            received->sender, header->common.rpcId);
                    t->incomingRpcs[header->common.rpcId] = serverRpc;
                    serverRpc->accumulator.construct(t,
                            &serverRpc->requestPayload);
                    t->serverTimerList.push_back(*serverRpc);
                } else if (serverRpc->requestComplete) {
                    // We've already received the full message, so
                    // ignore this packet.
                    TEST_LOG("ignoring extraneous packet");
                    goto serverDataDone;
                }
                serverRpc->accumulator->addPacket(received, header);
#if TIME_TRACE
                if (header->offset == 0) {
                    TimeTrace::globalTimeTrace->record(
                            "server received opcode %u, totalLength %u",
                            serverRpc->requestPayload.getStart<
                            WireFormat::RequestCommon>()->opcode,
                            header->totalLength);
                }
#endif
                if (serverRpc->requestPayload.size() >= header->totalLength) {
                    // Message complete; start servicing the RPC.
                    if (serverRpc->requestPayload.size()
                            > header->totalLength) {
                        // We have more bytes than we want. This can happen
                        // if the last packet gets padded by the network
                        // layer to meet minimum size requirements. Just
                        // truncate the request.
                        serverRpc->requestPayload.truncate(header->totalLength);
                    }
                    erase(t->serverTimerList, *serverRpc);
                    serverRpc->requestComplete = true;
                    t->context->workerManager->handleRpc(serverRpc);
                } else {
                    // See if we need to output a GRANT.
                    if ((header->common.flags & NEED_GRANT) &&
                            (serverRpc->grantOffset <
                            (serverRpc->requestPayload.size()
                            + t->roundTripBytes)) &&
                            (serverRpc->grantOffset < header->totalLength)) {
                        serverRpc->grantOffset =
                                serverRpc->requestPayload.size()
                                + t->roundTripBytes + t->grantIncrement;
#if TIME_TRACE
                        TimeTrace::globalTimeTrace->record(
                                "server sending GRANT, sequence %u, offset %u",
                                downCast<uint32_t>(
                                header->common.rpcId.sequence),
                                serverRpc->grantOffset);
#endif
                        GrantHeader grant(header->common.rpcId,
                                serverRpc->grantOffset, FROM_SERVER);
                        t->driver->sendPacket(serverRpc->clientAddress,
                                &grant, NULL);
                    }
                }
                serverDataDone:
                if ((header->offset < serverRpc->resendLimit) &&
                        !(header->common.flags & RETRANSMISSION)) {
                    LOG(NOTICE, "Original data arrived from client %s after "
                            "RESEND: sequence %lu, offset %u, "
                            "resendLimit %u",
                            received->sender->toString().c_str(),
                            common->rpcId.sequence, header->offset,
                            serverRpc->resendLimit);
                    originalCount++;
                    if (originalCount == 10) {
                        LOG(NOTICE, "Dumping time trace for client %s, "
                                "sequence %lu",
                                received->sender->toString().c_str(),
                                common->rpcId.sequence);
                        TimeTrace::globalTimeTrace->record(
                                "Original data arrived  after RESEND for "
                                "sequence %lu",
                                downCast<uint32_t>(common->rpcId.sequence));
                        TimeTrace::globalTimeTrace->printToLogBackground(
                                t->context->dispatch);
                        LogTimeTraceHeader logHeader(header->common.rpcId,
                                FROM_SERVER);
                        t->driver->sendPacket(serverRpc->clientAddress,
                                &logHeader, NULL);
                    }
                }
                return;
            }

            case PacketOpcode::GRANT: {
                GrantHeader* header = received->getOffset<GrantHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "server received GRANT, sequence %u, offset %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset);
#endif
                if ((serverRpc == NULL) || (serverRpc->transmitOffset == 0)) {
                    RAMCLOUD_LOG(WARNING, "unexpected GRANT from client %s, "
                            "id (%lu,%lu), grantOffset %u",
                            received->sender->toString().c_str(),
                            header->common.rpcId.clientId,
                            header->common.rpcId.sequence, header->offset);
                    return;
                }
                if (header->offset > serverRpc->transmitOffset) {
                    t->sendBytes(serverRpc->clientAddress,
                            header->common.rpcId, &serverRpc->replyPayload,
                            serverRpc->transmitOffset,
                            header->offset - serverRpc->transmitOffset,
                            FROM_SERVER);
                    serverRpc->transmitOffset = header->offset;
                    if (serverRpc->transmitOffset >=
                            serverRpc->replyPayload.size()) {
                        // See "Note A" in sendReply.
                        t->deleteServerRpc(serverRpc);

                    }
                }
                return;
            }

            case PacketOpcode::RESEND: {
                ResendHeader* header = received->getOffset<ResendHeader>(0);
                if (header == NULL)
                    goto packetLengthError;
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record("server received RESEND, "
                        "sequence %u, offset %u, length %u",
                        downCast<uint32_t>(header->common.rpcId.sequence),
                        header->offset, header->length);
#endif
                if (serverRpc == NULL) {
                    // This situation can happen if a packet of the response
                    // got lost but we have already freed the ServerRpc. We
                    // no longer have the missing data, so ask the client to
                    // restart the RPC from scratch.
                    RAMCLOUD_CLOG(WARNING, "received RESEND from client %s, "
                            "but RPC state no longer exists",
                            received->sender->toString().c_str());
                    RetryHeader retry(header->common.rpcId, FROM_SERVER);
                    t->driver->sendPacket(received->sender, &retry, NULL);
                    return;
                }
                if (serverRpc->transmitOffset == 0) {
                    // We haven't started transmitting the result yet, so
                    // we shouldn't have received this packet; ignored.
                    RAMCLOUD_CLOG(WARNING, "unexpected RESEND from client %s",
                            received->sender->toString().c_str());
                    return;
                }
                t->sendBytes(serverRpc->clientAddress,
                        serverRpc->rpcId, &serverRpc->replyPayload,
                        header->offset, header->length,
                        RETRANSMISSION|FROM_SERVER);
                uint32_t resendEnd = header->offset + header->length;
                if (resendEnd > serverRpc->transmitOffset) {
                    serverRpc->transmitOffset = resendEnd;
                    if (serverRpc->transmitOffset >=
                            serverRpc->replyPayload.size()) {
                        // See "Note A" in sendReply.
                        t->deleteServerRpc(serverRpc);
                    }
                }
                return;
            }

            case PacketOpcode::PING: {
#if TIME_TRACE
                TimeTrace::globalTimeTrace->record(
                        "server received PING, sequence %u",
                        downCast<uint32_t>(common->rpcId.sequence));
#endif
                if (serverRpc == NULL) {
                    // No record of this RPC. Either all of the packets got
                    // lost or we already sent a response and deleted our
                    // state. Ask the client to restart transmission.
                    ResendHeader resend(common->rpcId, 0, t->roundTripBytes,
                            FROM_SERVER|RESTART);
                    t->driver->sendPacket(received->sender, &resend, NULL);
                    RAMCLOUD_LOG(NOTICE, "Unexpected PING from %s for sequence"
                            "%lu (packets lost?); requesting retransmit of"
                            "bytes %u-%u",
                            received->sender->toString().c_str(),
                            common->rpcId.sequence,
                            0, t->roundTripBytes);
                    return;
                }
                if (serverRpc->transmitOffset == 0) {
                    // Either we haven't received the whole request message yet
                    // or we're still working on executing the RPC. In either
                    // case, just send a dummy GRANT back to the client,
                    // whose only purpose is to let the client know we're
                    // still alive. If there's a problem receiving the request,
                    // handleTimerEvent will take care of that.
                    GrantHeader grant(common->rpcId, serverRpc->grantOffset,
                            FROM_SERVER);
                    t->driver->sendPacket(serverRpc->clientAddress,
                            &grant, NULL);
                    if (!serverRpc->requestComplete) {
                        LOG(NOTICE, "PING request from %s after receiving %u "
                                "bytes for sequence %lu, %lu fragments",
                                received->sender->toString().c_str(),
                                serverRpc->requestPayload.size(),
                                common->rpcId.sequence,
                                serverRpc->accumulator->fragments.size());
                    }
                } else {
                    // We have started sending the response message. It's
                    // possible that all of the response packets got lost,
                    // so we have to retransmit something. On the other hand,
                    // it's also possible that the PING arrived just after
                    // we started sending a response, so no packets have
                    // actually been lost. This is the more likely case, so
                    // only resent one packet's worth of data (as opposed to
                    // retransmitting everything we've already sent).
                    t->sendBytes(serverRpc->clientAddress,
                            serverRpc->rpcId, &serverRpc->replyPayload,
                            0, t->maxDataPerPacket, FROM_SERVER|RETRANSMISSION);
                    LOG(NOTICE, "PING request from %s while sending reply, "
                            "%u bytes already sent",
                            received->sender->toString().c_str(),
                            serverRpc->transmitOffset);
                }
                return;
            }

            case PacketOpcode::LOG_TIME_TRACE: {
                TimeTrace::globalTimeTrace->record(
                        "server received LOG_TIME_TRACE for sequence %u",
                        downCast<uint32_t>(common->rpcId.sequence));
                TimeTrace::globalTimeTrace->printToLogBackground(
                        t->context->dispatch);
                return;
            }

            default:
                RAMCLOUD_CLOG(WARNING,
                        "unexpected opcode %s received from client %s",
                        opcodeSymbol(common->opcode).c_str(),
                        received->sender->toString().c_str());
                return;
        }
    }

    packetLengthError:
    RAMCLOUD_CLOG(WARNING, "packet of type %s from %s too short (%u bytes)",
            opcodeSymbol(common->opcode).c_str(),
            received->sender->toString().c_str(),
            received->len);

}