//--------------------------------------------------------------
/// Generates XML that describes the injected processes and which
/// plugins were injected.
/// \return string containing the XML
//--------------------------------------------------------------
gtASCIIString ProcessTracker::GetProcessesXML()
{
    std::unordered_map< DWORD, gtASCIIString > procXMLMap;

    this->UpdateListOfInjectedProcesses();

    ProcessInfoList injectedProcesses = this->GetListOfInjectedProcesses();

    WrapperMap wrappers = GetWrapperMap();

    // the strPlatform is named this way to match options in the client.
#ifdef X64
    gtASCIIString strPlatform = "Win64";
#else
    gtASCIIString strPlatform = "Win32";
#endif

#ifndef CODEXL_GRAPHICS

    if (injectedProcesses.empty() == true)
    {
        LogConsole(logERROR, "There are no processes running which have been injected with the GPU PerfStudio server plugin\n");
        LogConsole(logERROR, "Please ensure that the server has the same bitness as the target application\n");
        LogConsole(logERROR, "For example, use the 64-bit GPU PerfStudio server with a 64-bit application\n");
    }

#endif

    for (ProcessInfoList::iterator procIter = injectedProcesses.begin();
         procIter != injectedProcesses.end();
         ++procIter)
    {

        DWORD pid = procIter->th32ProcessID;

        // only add the process info if it wasn't already found.
        if (procXMLMap.find(pid) == procXMLMap.end())
        {
            // insert new process and the process info
            gtASCIIString tmpString = XML("Name", XMLEscape(procIter->szExeFile).asCharArray());
            tmpString += XML("PID", pid);
            tmpString += XML("Path", XMLEscape(procIter->szPath).asCharArray());
            tmpString += XML("Platform",  strPlatform.asCharArray());
            procXMLMap[ pid ] = tmpString;

            // if this process is the one that was launched, then we know what the args and working directory were.
            // we could probably get the actual args and working dir from MicroDLL if it is a different app though, which
            // would be the case if this app uses a launcher app.
            if (m_injectedAppName.compare(procIter->szPath) == 0)
            {
                tmpString = XML("Args", XMLEscape(m_injectedAppArgs.c_str()).asCharArray());
                tmpString += XML("WDir", XMLEscape(m_injectedAppDir.c_str()).asCharArray());
                procXMLMap[ pid ] += tmpString.asCharArray();
            }
        }

        // add an API node for each of the wrappers that are injected into the app
        for (WrapperMap::const_iterator wrapperIter = wrappers.begin(); wrapperIter != wrappers.end(); ++wrapperIter)
        {
            // List of plugin extensions to check for. On Windows, test for 32 and 64 bit plugins.
            // On linux, just check for the plugin corresponding to the server bitness
            static const char* pluginExtensions[] =
            {
#ifdef WIN32
                GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX "." DLL_EXTENSION,
                "-x64" GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX "." DLL_EXTENSION
#else
                GDT_PROJECT_SUFFIX "." DLL_EXTENSION
#endif
            };

            int numPlugins = sizeof(pluginExtensions) / sizeof(pluginExtensions[0]);

            for (int loop = 0; loop < numPlugins; loop++)
            {
                // check to see if this wrapper is in the application
                gtASCIIString strPluginName = wrapperIter->second.strPluginName;

                if (SG_GET_BOOL(OptionDllReplacement) == false)
                {
                    strPluginName += pluginExtensions[loop];
                }

                if (IsLibraryLoadedInProcess(pid, strPluginName.asCharArray(), NULL))
                {
                    bool attached = false;

                    if (g_activeWrappersMap.find(FormatText("%lu/%s", pid, wrapperIter->first.c_str()).asCharArray()) != g_activeWrappersMap.end())
                    {
                        // the pid/plugin string was listed in the active wrappers map, so the plugin must be active.
                        attached = true;
                    }

                    procXMLMap[pid] += XMLAttrib("API", FormatText("attached='%s'", attached ? "TRUE" : "FALSE").asCharArray(), wrapperIter->second.strPluginShortDesc.asCharArray());
                }
            }
        }
    }

    // concatenate the process XML and additional info
    gtASCIIString xml;

    for (std::unordered_map< DWORD, gtASCIIString >::iterator iterP = procXMLMap.begin();
         iterP != procXMLMap.end();
         iterP++)
    {
        xml += XML("Process", (iterP->second).asCharArray());
    }

    gtASCIIString out = XMLHeader();
    out += XML("ProcessList", xml.asCharArray());

    return out;
}
//--------------------------------------------------------------------------
/// Allows the application to send a response for this command.
/// If the server is streaming data, setting pData to NULL will closer the connection
/// \param pData the data to respond with,
/// \param uBytes if ContentType is JPEG, specifies the number of bytes in
///      the image; ignored otherwise
//--------------------------------------------------------------------------
void CommandResponse::Send(const char* pData, unsigned int uBytes)
{
    // send the data back to all received requests
    for (std::list< CommunicationID >::const_iterator iRequestID = m_requestIDs.begin(); iRequestID != m_requestIDs.end(); ++iRequestID)
    {
        if (m_bStreamingEnabled == true)
        {
            //server wants to close the connection?
            if (pData == NULL)
            {
                SendResponse(*iRequestID, "", NULL, 0, m_bStreamingEnabled);
                m_bStreamingEnabled = false;
                continue;
            }
        }

        // string which is used to add tags around XML and HTML responses
        string strResponse;

        bool bResult = false;

        switch (m_eContentType)
        {
            case CONTENT_XML:
            {
                strResponse += XMLHeader().asCharArray();
                strResponse += "<XML src='";
                strResponse += GetURL();
                strResponse += "'>";
                strResponse += (char*) pData;
                strResponse += "</XML>";
                bResult = SendResponse(*iRequestID, "text/xml", strResponse.c_str(), (unsigned int) strResponse.size(), m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_HTML:
            {
                strResponse += "<HTML>";
                strResponse += (char*) pData;
                strResponse += "</HTML>";
                bResult = SendResponse(*iRequestID, "text/html", strResponse.c_str(), (unsigned int) strResponse.size(), m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_TEXT:
            {
                bResult = SendResponse(*iRequestID, "text/plain", (char*) pData, (unsigned int) strlen((char*) pData), m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_PNG:
            {
                bResult = SendResponse(*iRequestID, "image/png", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_JPG:
            {
                bResult = SendResponse(*iRequestID, "image/jpeg", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_BMP:
            {
                bResult = SendResponse(*iRequestID, "image/bmp", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_DDS:
            {
                bResult = SendResponse(*iRequestID, "application/dds", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_PEF:
            {
                bResult = SendResponse(*iRequestID, "bytes/pef", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_SCO:
            {
                bResult = SendResponse(*iRequestID, "bytes/sco", (char*) pData, uBytes, m_bStreamingEnabled);
                m_eResponseState = SENT_RESPONSE;
                break;
            }

            case CONTENT_REQUEST:
            default:
            {
                SendError("Attempted to send without setting ContentType");
                m_eResponseState = SENT_RESPONSE;
                break;
            }
        }

        if (bResult == false)
        {
            m_bStreamingEnabled = false;
            m_eResponseState = ERROR_SENDING_RESPONSE;
        }
    }

    if (m_bStreamingEnabled == false)
    {
        // we sent a response and the request was not for streaming data, so unset the request
        m_requestIDs.clear();
    }
}