//--------------------------------------------------------------------------
/// Write a trace's metadata file and return the contenst through the out-param.
/// \param inFullResponseString The full response string for a collected linked trace request.
/// \param outMetadataXML The XML metadata string to return to the client.
/// \returns True if writing the metadata file was successful.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::WriteTraceAndMetadataFiles(const std::stringstream& inFullResponseString, std::string& outMetadataXML)
{
    bool bWrittenSuccessfully = false;

    // Empty out the incoming path to the metadata file. We'll know the exact path later.
    outMetadataXML.assign("");

    osModuleArchitecture moduleArchitecture;
    osRuntimePlatform currentPlatform;
    gtString executablePath;
    gtString commandLine;
    gtString workingDirectory;

    // Retrieve the name of the instrumented application. Construct a metadata filename which references it.
    if (osGetProcessLaunchInfo(osGetCurrentProcessId(), moduleArchitecture, currentPlatform, executablePath, commandLine, workingDirectory) == true)
    {
        osFilePath executableFilepath;
        executableFilepath.setFullPathFromString(executablePath);

        gtString appName;
        if (executableFilepath.getFileName(appName) == true)
        {
            osTime currentTime;
            currentTime.setFromCurrentTime();
            tm timeStruct;
            currentTime.timeAsTmStruct(timeStruct, osTime::LOCAL);

            // Need to add 1900, since tm contains "years since 1900".
            int year = timeStruct.tm_year + 1900;

            // Need to add 1, since tm contains "months since January".
            int month = timeStruct.tm_mon + 1;
            int day = timeStruct.tm_mday;

            int hour = timeStruct.tm_hour;
            int minute = timeStruct.tm_min;
            int second = timeStruct.tm_sec;

            gtASCIIString metadataFilename;

            // ExecutableFilename-YEAR-MM-DD-HOUR-MINUTE-SECOND
            metadataFilename.appendFormattedString("description-%s-%d-%d-%d-%d-%d-%d.xml",
                appName.asASCIICharArray(),
                year, month, day,
                hour, minute, second);

            // Build a path to the GPS folder within the Temp directory.
            osFilePath systemTempDirectory;
            systemTempDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);


            gtString toolDirectory;
#ifdef CODEXL_GRAPHICS
            toolDirectory.fromASCIIString("CodeXL");
#else
            toolDirectory.fromASCIIString(GetPerfStudioDirName());
#endif
            // @TODO: Construct a path from the temp + tool directory.
            systemTempDirectory.appendSubDirectory(toolDirectory);

            gtList<osFilePath> toolDirectoryPaths;
            osDirectory d;
            d.setDirectoryPath(systemTempDirectory);

            // @TODO: Find a better way to create the "Session" directory. We shouldn't need to search through existing session directories.
            int maxSessionIndex = 0;
            bool bGotSubDirectories = d.getSubDirectoriesPaths(osDirectory::SORT_BY_NAME_DESCENDING, toolDirectoryPaths);
            if (bGotSubDirectories)
            {
                // Step through each directory, and look for "Session" folders.
                gtList<osFilePath>::iterator subdirectoryIter;
                for (subdirectoryIter = toolDirectoryPaths.begin(); subdirectoryIter != toolDirectoryPaths.end(); ++subdirectoryIter)
                {
                    gtString subdirString;
                    osFilePath subdir = *subdirectoryIter;
                    subdir.getFileName(subdirString);

                    if (subdir.isDirectory() && subdirString.startsWith(L"Session"))
                    {
                        // Remove the "Session" part of the string. We're only interested in the number at the end.
                        subdirString.replace(L"Session", L"", true);

                        const char* sessionIndexAsString = subdirString.asASCIICharArray();
                        int thisSessionId = atoi(sessionIndexAsString);

                        if (thisSessionId > maxSessionIndex)
                        {
                            maxSessionIndex = thisSessionId;
                        }
                    }

                }
            }


            gtASCIIString pathToDataDirectory = systemTempDirectory.asString().asASCIICharArray();

            // Metadata files will be saved to the temp directory with the following filename scheme:
            // "%TEMP%/ToolDirectory/Session[Index]/ApplicationBinaryName/Frame[Index]/description.xml"

            int frameIndex = GetInterceptor()->GetParentLayerManager()->GetFrameCount();

            // Generate a "Session" folder with a number at the end. Compute the correct number by looking at the
            // Session folders that already exist
            gtASCIIString sessionString;
            sessionString.appendFormattedString("Session%d", maxSessionIndex + 1);

            pathToDataDirectory.appendFormattedString("\\%s\\%s\\Frame%d\\", sessionString.asCharArray(), appName.asASCIICharArray(), frameIndex);

            // Create the data directory if it doesn't already exist.
            gtString fullPathToDataDirectoryAsGTString;
            fullPathToDataDirectoryAsGTString.fromASCIIString(pathToDataDirectory.asCharArray());

            osDirectory dir;
            dir.setDirectoryFullPathFromString(fullPathToDataDirectoryAsGTString);
            if (!dir.exists())
            {
                bool bDirectoryCreated = dir.create();
                if (!bDirectoryCreated)
                {
                    Log(logERROR, "Failed to create data directory for traced frame: '%s'.\n", fullPathToDataDirectoryAsGTString.asASCIICharArray());
                }
            }

            gtASCIIString pathToMetadataFile = pathToDataDirectory;
            pathToMetadataFile.appendFormattedString("%s", metadataFilename.asCharArray());

            gtString fullMetadataFilepathAsGTString;
            fullMetadataFilepathAsGTString.fromASCIIString(pathToMetadataFile.asCharArray());

            osFile metadataFile(fullMetadataFilepathAsGTString);
            bool bMetadataFileOpened = metadataFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

            // If we've successfully opened the metadata file, we'll also attempt to write the trace file.
            if (bMetadataFileOpened)
            {
                osFilePath traceFileDirectory;
                traceFileDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);

                traceFileDirectory.appendSubDirectory(toolDirectory);

                // Construct a filename for the cached trace response.
                gtASCIIString fullTraceFilename;
                fullTraceFilename.appendFormattedString("LinkedTrace-%s-%d-%d-%d-%d-%d-%d.ltr", appName.asASCIICharArray(), year, month, day, hour, minute, second);

                gtASCIIString fullTraceFilePath = pathToDataDirectory;
                fullTraceFilePath.appendFormattedString("%s", fullTraceFilename.asCharArray());

                gtString fullTraceResponseFilepathGTString;
                fullTraceResponseFilepathGTString.fromASCIIString(fullTraceFilePath.asCharArray());

                // Write the contents of the trace response file.
                osFile traceResponseFile(fullTraceResponseFilepathGTString);
                bool bTraceResponseFileOpened = traceResponseFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);
                if (bTraceResponseFileOpened)
                {
                    // Dump the response into an ASCII string.
                    std::string responseAsString = inFullResponseString.str();

                    // Now put the ASCII string into a gtString so we can write it to the open file.
                    gtString gtStringResponse;
                    std::wstring wideString;
                    wideString.assign(responseAsString.begin(), responseAsString.end());
                    gtStringResponse.appendFormattedString(L"%s", wideString.c_str());
                    traceResponseFile.writeString(gtStringResponse);
                    traceResponseFile.close();
                }
                else
                {
                    Log(logERROR, "Failed to write trace response to file: '%s'\n", fullTraceResponseFilepathGTString.asASCIICharArray());
                }

                // Write the filename for the associated trace response that was just collected.
                std::string traceFilepathAsString;
                traceFilepathAsString.assign(fullTraceResponseFilepathGTString.asASCIICharArray());

                TraceMetadata metadata;

                // Insert the location of the metadata file being written out.
                metadata.mMetadataFilepath = pathToMetadataFile.asCharArray();

                // Insert the path to the cached trace file.
                metadata.mPathToTraceFile = fullTraceFilePath.asCharArray();

                // @TODO: When we have a framebuffer image system working, assign the path-to-image here.
                metadata.mPathToFrameBufferImage = "UNKNOWNPATH";

                ModernAPILayerManager* layerManager = GetInterceptor()->GetParentLayerManager();

                FrameInfo frameInfo;

                layerManager->GetFrameInfo(frameInfo);

                // Populate the metadata structure with the values stored in the LayerManager.
                metadata.mFrameInfo = frameInfo;
                metadata.mFrameIndex = frameIndex;
                metadata.mArchitecture = moduleArchitecture;

                // Write the metadata xml into the output file.
                gtASCIIString metadataXMLString;
                metadata.WriteToXML(metadataXMLString);
                gtString metadataXMLAsGTString;
                metadataXMLAsGTString.fromASCIIString(metadataXMLString.asCharArray());

                // Write the metadata XML into the file, and close.
                metadataFile.writeString(metadataXMLAsGTString);
                metadataFile.close();

                // The client will receive the full metadata XML string to parse.
                outMetadataXML.assign(metadataXMLString.asCharArray());

                bWrittenSuccessfully = true;
            }
            else
            {
                Log(logERROR, "Failed to open trace metadata file for writing: '%s'\n", metadataFilename);
            }
        }
        else
        {
            Log(logERROR, "Failed to retrieve the instrumented process's application filename.\n");
        }
    }
    else
    {
        Log(logERROR, "Failed to retrieve process launch info for target application.\n");
    }

    return bWrittenSuccessfully;
}
//--------------------------------------------------------------------------
/// Write a trace's metadata file and return the contenst through the out-param.
/// \param inHeaderString The full response string for a collected linked trace request.
/// \param inResponseString Response string
/// \param outMetadataXML The XML metadata string to return to the client.
/// \returns True if writing the metadata file was succesful.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::WriteTraceAndMetadataFiles(const gtASCIIString& inHeaderString, const gtASCIIString& inResponseString, std::string& outMetadataXML)
{
    bool bWrittenSuccessfully = false;

    // Empty out the incoming path to the metadata file. We'll know the exact path later.
    outMetadataXML.assign("");

    // Use this object to pass data into and out of the GetSessionManagaerData() method.
    SessionManagerData smd;

    smd.frameIndex = GetParentLayerManager()->GetFrameCount();

    bool result = SessionManager::Instance()->GetSessionManagerData(smd);

    if (result == false)
    {
        return result;
    }

    gtASCIIString pathToMetadataFile = smd.pathToDataDirectory; //pathToDataDirectory;
    pathToMetadataFile.appendFormattedString("%s", smd.metadataFilename.asCharArray());

    gtString fullMetadataFilepathAsGTString;
    fullMetadataFilepathAsGTString.fromASCIIString(pathToMetadataFile.asCharArray());

    osFile metadataFile(fullMetadataFilepathAsGTString);
    bool bMetadataFileOpened = metadataFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

    // If we've successfully opened the metadata file, we'll also attempt to write the trace file.
    if (bMetadataFileOpened == false)
    {
        Log(logERROR, "Failed to open trace metadata file for writing: '%s'\n", smd.metadataFilename.asCharArray());
        return false;
    }

    osFilePath traceFileDirectory;
    traceFileDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);

    traceFileDirectory.appendSubDirectory(smd.toolDirectory);

    // Construct a filename for the cached trace response.
    gtASCIIString fullTraceFilename;
    fullTraceFilename.appendFormattedString("LinkedTrace-%s-%d-%d-%d-%d-%d-%d.ltr", smd.appName.asASCIICharArray(), smd.year, smd.month, smd.day, smd.hour, smd.minute, smd.second);

    gtASCIIString fullTraceFilePath = smd.pathToDataDirectory;
    fullTraceFilePath.appendFormattedString("%s", fullTraceFilename.asCharArray());

    gtString fullTraceResponseFilepathGTString;
    fullTraceResponseFilepathGTString.fromASCIIString(fullTraceFilePath.asCharArray());

    // Write the contents of the trace response file.
    osFile traceResponseFile(fullTraceResponseFilepathGTString);
    bool bTraceResponseFileOpened = traceResponseFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

    if (bTraceResponseFileOpened)
    {
        traceResponseFile.writeString(inHeaderString);
        traceResponseFile.writeString(inResponseString);
        traceResponseFile.close();
    }
    else
    {
        Log(logERROR, "Failed to write trace response to file: '%s'\n", fullTraceResponseFilepathGTString.asASCIICharArray());
    }

    // Write the filename for the associated trace response that was just collected.
    std::string traceFilepathAsString;
    traceFilepathAsString.assign(fullTraceResponseFilepathGTString.asASCIICharArray());

    TraceMetadata metadataToWrite;

    // Insert the location of the metadata file being written out.
    metadataToWrite.mMetadataFilepath = pathToMetadataFile.asCharArray();

    // Insert the path to the cached trace file.
    metadataToWrite.mPathToTraceFile = fullTraceFilePath.asCharArray();

    // Write object files info. It is currently assumed trace and object data will exists simultaneously.
    metadataToWrite.mPathToObjectTreeFile = smd.pathToDataDirectory.asCharArray();
    metadataToWrite.mPathToObjectTreeFile.append("ObjectTree.xml");

    metadataToWrite.mPathToObjectDatabaseFile = smd.pathToDataDirectory.asCharArray();
    metadataToWrite.mPathToObjectDatabaseFile.append("FullObjectDatabase.xml");

    ModernAPIFrameDebuggerLayer* frameDebugger = GetParentLayerManager()->GetFrameDebuggerLayer();

    unsigned char* pngData = NULL;
    unsigned int numBytes = 0;

    // NOTE: Passing in 0 for the width and height will cause the renderer to render the PNG image at the same resolution as the applications frame buffer (i.e full resolution).
    bool bCapturedSuccessfully = frameDebugger->CaptureFrameBuffer(0, 0, &pngData, &numBytes, true);

    if (bCapturedSuccessfully)
    {
        gtASCIIString fullImageFilename;
        fullImageFilename.appendFormattedString("%s_FrameBuffer%d.png", smd.appName.asASCIICharArray(), smd.frameIndex);

        gtASCIIString imageFilepath = smd.pathToDataDirectory;
        imageFilepath.appendFormattedString("%s", fullImageFilename.asCharArray());

        FILE* frameBufferImageFile = fopen(imageFilepath.asCharArray(), "wb");

        if (frameBufferImageFile != NULL)
        {
            fwrite(pngData, sizeof(unsigned char), numBytes, frameBufferImageFile);
            fclose(frameBufferImageFile);

            // Add the captured image's path into the XML metadata.
            metadataToWrite.mPathToFrameBufferImage = imageFilepath.asCharArray();
        }
        else
        {
            Log(logERROR, "Failed to write frame buffer image file.\n");
        }

        SAFE_DELETE_ARRAY(pngData);
    }
    else
    {
        metadataToWrite.mPathToFrameBufferImage = "ERROR - Failed to capture frame buffer image.";
        Log(logERROR, "Failed to capture frame buffer for captured frame.\n");
    }

    // @TODO: This is placeholder for now to get a prototype working.
    // We should also be able to set this to "Capture".
    metadataToWrite.mTraceType = kTraceType_Linked;

    // @TODO: This is temporary until TraceMetadata generation is moved to the LayerManager.
    FrameInfo frameInfo;
    frameDebugger->GetFrameInfo(&frameInfo);

    // Populate the metadata structure with the values stored in the LayerManager.
    metadataToWrite.mFrameInfo = &frameInfo;
    metadataToWrite.mArchitecture = smd.moduleArchitecture;
    metadataToWrite.mAPICallCount = GetNumTracedAPICalls();
    metadataToWrite.mDrawCallCount = GetNumTracedDrawCalls();

    bool bMetadataWriteSuccessful = WriteMetadataFile(&metadataToWrite, pathToMetadataFile.asCharArray(), outMetadataXML);

    if (bMetadataWriteSuccessful)
    {
        bWrittenSuccessfully = true;
    }

    return bWrittenSuccessfully;
}