//--------------------------------------------------------------------------
/// EndFrame is used to signal the end of a rendered frame and to stop/send
/// the captured results that were logged during the frame render.
/// \return Nothing.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::EndFrame()
{
    // Check again which trace type is active at the end of the frame. Need to match how it was started.
    int autotraceFlags = GetTraceTypeFlags();

    // If the linked trace was requested, return all of the results through a single response.
    bool bLinkedTraceRequested = (mCmdLinkedTrace.IsActive() || mCmdLinkedTraceWithSave.IsActive()) || (autotraceFlags == kTraceType_Linked);

    // Will we be required to dump the trace response to a file on disk?
    bool bSaveResponseToFile = mCmdLinkedTraceWithSave.IsActive();

    // If we want a linked trace, we'll need responses from all trace types.
    bool bAPITraceResponseNeeded = m_apiTraceTXT.IsActive() || bLinkedTraceRequested || (autotraceFlags & kTraceType_API);
    bool bGPUTraceResponseNeeded = m_cmdGPUTrace.IsActive() || bLinkedTraceRequested || (autotraceFlags & kTraceType_GPU);

    if (bAPITraceResponseNeeded || bGPUTraceResponseNeeded)
    {
        // We're done collecting, so turn off the interception and profiler switch.
        InterceptorBase* interceptor = GetInterceptor();
        interceptor->SetCollectTrace(false);
        interceptor->SetProfilingEnabled(false);

        AfterAPITrace();
        AfterGPUTrace();

        std::string apiTraceResponseString, gpuTraceResponseString;

        if (bAPITraceResponseNeeded)
        {
            mbCollectingApiTrace = false;
            apiTraceResponseString.assign(GetAPITraceTXT().c_str());
        }

        if (bGPUTraceResponseNeeded)
        {
            mbGPUTraceAlreadyCollected = false;
            gpuTraceResponseString.assign(GetGPUTraceTXT().c_str());
        }

        std::stringstream fullResponseString;

        if (bAPITraceResponseNeeded)
        {
#if !defined(CODEXL_GRAPHICS)

            // In Linked trace mode, insert a separator indicating the split between response types.
            if (bLinkedTraceRequested)
            {
                fullResponseString << "//Type:API" << std::endl;
            }

#endif
            fullResponseString << apiTraceResponseString.c_str() << std::endl;
        }

        if (bGPUTraceResponseNeeded)
        {
#if !defined(CODEXL_GRAPHICS)

            // In Linked trace mode, insert a separator indicating the split between response types.
            if (bLinkedTraceRequested)
            {
                fullResponseString << "//Type:GPU" << std::endl;
            }

#endif
            fullResponseString << gpuTraceResponseString.c_str() << std::endl;
        }

        // If the autotrace flags are anything besides "None," we'll just store the trace log internally so the client can pick it up later.
        bool bShouldCacheForAutotrace = (autotraceFlags != kTraceType_None);

        if (bShouldCacheForAutotrace)
        {
            // Don't send the response back through a command yet.
            // The client will know to pick it up through a special AutoCapture command.
            mCachedTraceResponse.assign(fullResponseString.str());
            mbWaitingForAutocaptureClient = true;
        }
        else
        {
            // Send the response string back to the server through a specific request command.
            if (bLinkedTraceRequested)
            {
                HandleLinkedTraceResponse(fullResponseString, bSaveResponseToFile);
            }
            else
            {
                if (bAPITraceResponseNeeded)
                {
                    m_apiTraceTXT.Send(apiTraceResponseString.c_str());
                }
                else if (bGPUTraceResponseNeeded)
                {
                    m_cmdGPUTrace.Send(gpuTraceResponseString.c_str());
                }
            }
        }

    }

    // When AutoCapture is enabled, we need to delay rendering so the user has time to retrieve the cached response.
    if (mbWaitingForAutocaptureClient)
    {
        // For AutoCapture the client will send the separate request below to collect the cached trace response.
        if (mCmdAutoCaptureCachedTrace.IsActive())
        {
            // We're done waiting to send the cached response to the client. Move on with playback.
            mbWaitingForAutocaptureClient = false;

            mCmdAutoCaptureCachedTrace.Send(mCachedTraceResponse.c_str());
            mCachedTraceResponse.clear();
        }
        else
        {
            // Sleep to give the user a chance to connect during playback.
            osSleep(500);
        }
    }

    if (mRetrieveCachedTraceResponse.IsActive())
    {
        const char* pathToTraceMetadata = mRetrieveCachedTraceResponse.GetValue();
        if (strlen(pathToTraceMetadata) > 0)
        {
            // Read the metadata file and store the contents in a structure.
            TraceMetadata traceMetadata;
            bool bReadMetadataFileSuccessfully = ReadTraceMetadataFile(pathToTraceMetadata, traceMetadata);
            if (bReadMetadataFileSuccessfully)
            {
                gtASCIIString traceContents;
                bool bReadTraceSuccessfully = LoadTraceFile(traceMetadata.mPathToTraceFile, traceContents);
                if (bReadTraceSuccessfully)
                {
                    // At this point the full trace response text should be loaded into our string and ready to be sent back to the client.
                    mRetrieveCachedTraceResponse.Send(traceContents.asCharArray());
                }
                else
                {
                    Log(logERROR, "Failed to read trace file at '%s'.", traceMetadata.mPathToTraceFile.c_str());
                }
            }
            else
            {
                Log(logERROR, "Failed to read metadata file at '%s'.", pathToTraceMetadata);
            }
        }
        else
        {
            Log(logERROR, "Failed to locate valid path to trace metadata file.");
        }
    }
}
//--------------------------------------------------------------------------
/// Handle what happens when a Linked Trace is requested. We can either:
/// 1. Return the trace response as normal.
/// 2. Cache the response to disk, and generate a "trace metadata" file used to retrieve the trace later.
/// \param inFullResponseString The response string built by tracing the application.
/// \param inbSaveResponseToFile A switch used to determine which response method to use.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::HandleLinkedTraceResponse(gtASCIIString& inFullResponseString, bool inbSaveResponseToFile)
{
    ModernAPILayerManager* parentLayerManager = GetParentLayerManager();

    if (parentLayerManager->InCapturePlayer())
    {
        const std::string& metadataFile = parentLayerManager->GetPathToTargetMetadataFile();

        if (metadataFile.length() > 0)
        {
            // Read the metadata file and store the contents in a structure.
            TraceMetadata traceMetadata;
            traceMetadata.mFrameInfo = new FrameInfo;

            bool bReadMetadataFileSuccessfully = ReadMetadataFile(metadataFile, &traceMetadata);

            if (bReadMetadataFileSuccessfully)
            {
                gtASCIIString traceContents;
                bool bReadTraceSuccessfully = LoadTraceFile(traceMetadata.mPathToTraceFile, traceContents);

                if (bReadTraceSuccessfully)
                {
                    // At this point the full trace response text should be loaded into our string and ready to be sent back to the client.
                    mCmdLinkedTrace.Send(traceContents.asCharArray());
                }
                else
                {
                    Log(logERROR, "Failed to read trace file at '%s'.", traceMetadata.mPathToTraceFile.c_str());
                }
            }
            else
            {
                Log(logERROR, "Failed to read metadata file at '%s'.", metadataFile.c_str());
            }

            // Destroy the FrameInfo instance that was created above.
            SAFE_DELETE(traceMetadata.mFrameInfo);
        }
        else
        {
            Log(logERROR, "Failed to locate valid path to trace metadata file.");
        }
    }
    else
    {
        gtASCIIString traceHeaderBlock;
        bool bBuiltHeaderSuccessfully = GenerateLinkedTraceHeader(traceHeaderBlock);

        if (bBuiltHeaderSuccessfully)
        {
            bool bKeypressTrigger = parentLayerManager->IsTraceTriggeredByKeypress();

            // Collect a trace and generate the trace metadata string. Write the trace and metadata files to disk.
            std::string metadataXMLString;
            bool bWriteMetadataSuccessful = WriteTraceAndMetadataFiles(traceHeaderBlock, inFullResponseString, metadataXMLString);

            // If the trace wasn't triggered by a keypress, we'll need to send a response back through either of the following commands.
            CommandResponse& frameCaptureWithSaveResponse = (inbSaveResponseToFile == true) ? parentLayerManager->mCmdFrameCaptureWithSave : mCmdLinkedTrace;

            if (bWriteMetadataSuccessful)
            {
                // We only need to send the response back through a request if the client triggered collection.
                if (!bKeypressTrigger)
                {
                    // Check if we want to cache the response to disk, or return it as-is.
                    if (inbSaveResponseToFile)
                    {
                        if (bWriteMetadataSuccessful)
                        {
                            // Send a response back to the client indicating which trace metadata file was written to disk.
                            frameCaptureWithSaveResponse.Send(metadataXMLString.c_str());
                        }
                        else
                        {
                            Log(logERROR, "Failed to write trace metadata XML.\n");
                            frameCaptureWithSaveResponse.Send("Failed");
                        }
                    }
                    else
                    {
                        // Send a response containing the API and GPU trace text.
                        frameCaptureWithSaveResponse.Send(inFullResponseString.asCharArray());
                    }
                }
                else
                {
                    Log(logMESSAGE, "Successfully traced frame %d.\n", parentLayerManager->GetFrameCount());
                }
            }
            else
            {
                Log(logERROR, "Failed to write trace metadata XML.\n");

                // If a failed trace collection was triggered by a command, we need to respond with an error message.
                if (!bKeypressTrigger)
                {
                    frameCaptureWithSaveResponse.Send("Failed");
                }
            }
        }
    }
}