//-----------------------------------------------------------------------------
/// RemoveRequest
///
/// Provides a way for the Server to remove a request that it will not be
/// responding to. This is typical if the request is passed on to a plugin
/// in a different process which will be sending a response.
///
/// \param requestID a requestID returned by a call to CreateRequest
//-----------------------------------------------------------------------------
void RemoveRequest(CommunicationID requestID)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    RequestMap::iterator iter = g_requestMap.find(requestID);

    if (iter != g_requestMap.end())
    {
        HTTPRequestHeader* pRequest = iter->second;
        delete pRequest;
        g_requestMap.erase(iter);
    }
}
//-----------------------------------------------------------------------------
/// GetRequestBinary
///
/// This provides a method of accessing any binary data of a request. Requests
/// that have binary data as the primary means of communication will have the
/// string "binary" returned by GetRequestText(...); other commands may or may
/// not have associated binary data, as determined by the client.
///
/// \param requestID An ID for a particular request; the plugin will get this
///  id as a parameter to the ProcessRequest( ) function
///
/// \return pointer to the binary data that is associated with the request;
///  NULL if no binary data exists
//-----------------------------------------------------------------------------
void* GetRequestBinary(CommunicationID requestID)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    HTTPRequestHeader* pRequest = NULL;

    RequestMap::iterator iterRequest = g_requestMap.find(requestID);

    if (iterRequest != g_requestMap.end())
    {
        pRequest = iterRequest->second;
    }

    return pRequest;
}
CommunicationID CreateRequest(HTTPRequestHeader* pRequest, bool bReceivedOverSocket)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    CommunicationID requestID = CommunicationID(pRequest->GetClientSocket());

    pRequest->SetReceivedOverSocket(bReceivedOverSocket);

    if (g_requestMap.find(requestID) != g_requestMap.end())
    {
        Log(logWARNING, "RequestID %u already exists from request: %s\n", requestID, pRequest->GetUrl());
        // Remove the pre-existing request (required to cleanup memory).
        RemoveRequest(requestID);
    }

    g_requestMap[requestID] = pRequest;

    return requestID;
}
//-----------------------------------------------------------------------------
bool MakeResponse(CommunicationID requestID, Response** ppResponse)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    PsAssert(ppResponse != NULL);

    // first see if we already have this ID as a streaming response
    ResponseMap::iterator iterResponse = g_streamingResponseMap.find(requestID);

    if (iterResponse != g_streamingResponseMap.end())
    {
        *ppResponse = iterResponse->second;
        return true;
    }

    // otherwise we need to create a new response based on the original request
    // so get the request
    RequestMap::iterator iterRequest = g_requestMap.find(requestID);

    if (iterRequest == g_requestMap.end())
    {
        // the original request couldn't be found, so return failure
        return false;
    }

    // need to create a new response
    if (PsNew(*ppResponse) == false)
    {
        return false;
    }

    HTTPRequestHeader* pRequest = iterRequest->second;
    PsAssert(pRequest != NULL);

    if (pRequest->GetReceivedOverSocket() == true)
    {
        (*ppResponse)->client_socket = pRequest->GetClientSocket();
    }
    else
    {
#if defined (_WIN32)
        (*ppResponse)->client_socket = NetSocket::CreateFromDuplicate(pRequest->GetProtoInfo());
#else
        // create a new socket and connect to the streamSocket on the server
        (*ppResponse)->client_socket = NetSocket::Create();

        if ((*ppResponse)->client_socket != NULL)
        {
            osPortAddress portAddress((unsigned short)pRequest->GetPort());
            (*ppResponse)->client_socket->Connect(portAddress);
        }

#endif
    }

    if ((*ppResponse)->client_socket == NULL)
    {
        int Err = NetSocket::LastError();
        Log(logERROR, "Could not create socket: NetSocket failed with error: %ld\n", Err);
        return false;
    }

    // see if this should be added as a streaming response
    gtASCIIString strUrl(pRequest->GetUrl());
    int32 iStream = strUrl.find(STR_STREAM_TOKEN);

    if (iStream >= 0)
    {
        const char* pBuf = strUrl.asCharArray();
        const char* pRate = &pBuf[ iStream + strlen(STR_STREAM_TOKEN)];
        unsigned int uRate = 0;

        // try to get the rate from the command;
        if (sscanf_s(pRate, "%u", &uRate) < 1)
        {
            // default to max rate
            uRate = COMM_MAX_STREAM_RATE;
        }

        // set the response as streaming with the specified rate
        (*ppResponse)->m_bStreamingEnabled = true;
        (*ppResponse)->m_dwMaxStreamsPerSecond = uRate;
        g_streamingResponseMap[ requestID ] = *ppResponse;
    }
    else
    {
        // streaming requests need to be kept around so that
        // additional responses can be directed to the right place,
        // HOWEVER, non-streaming requests only get a single response
        // and we just created the response for it, so it is safe
        // to remove the request from the requestMap. This will
        // help keep communication quick as the number of incoming
        // requests grows.
        RemoveRequest(requestID);
    }

    return true;
}