//==================================================================================
// Copyright (c) 2016 , Advanced Micro Devices, Inc.  All rights reserved.
//
/// \author AMD Developer Tools Team
/// \file gsSamplersMonitor.cpp
///
//==================================================================================
#include <AMDTOpenGLServer/src/gsSamplersMonitor.h>
#include <AMDTOpenGLServer/src/gsExtensionsManager.h>
#include <AMDTServerUtilities/Include/suInterceptionMacros.h>
#include <AMDTOpenGLServer/src/gsWrappersCommon.h>
#include <AMDTBaseTools/Include/gtAssert.h>
#include <AMDTServerUtilities/Include/suAllocatedObjectsMonitor.h>

// C++
#include <algorithm>

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::gsSamplersMonitor
// Description: Constructor
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
gsSamplersMonitor::gsSamplersMonitor() : m_glSamplers(), m_glGetSamplerParameterfv(NULL), m_glGetSamplerParameteriv(NULL)
{
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::~gsSamplersMonitor
// Description: Destructor
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
gsSamplersMonitor::~gsSamplersMonitor()
{
    // Delete all objects.
    for (size_t i = 0; i < m_glSamplers.size(); i++)
    {
        delete m_glSamplers[i];
        m_glSamplers[i] = NULL;
    }

    // Clear the vector.
    m_glSamplers.clear();
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::GenSamplers
// Description: Creates new program sampler objects
// Arguments:   GLuint samplersCount - amount of sampler objects to be created
//              const GLuint* pSamplersArr - array of previously unused sampler
//              object names (generated by the OpenGL runtime)
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
void gsSamplersMonitor::genSamplers(GLuint samplersCount, const GLuint* pSamplersArr)
{
    gtVector<apAllocatedObject*> samplersForAllocationMonitor;

    // Save the generated samplers.
    for (size_t i = 0; i < samplersCount; i++)
    {
        // Create a stateless GL sampler object.
        apGLSampler* pCreatedSampler = new apGLSampler(pSamplersArr[i]);

        // Save the generated sampler.
        m_glSamplers.push_back(pCreatedSampler);
        samplersForAllocationMonitor.push_back(pCreatedSampler);
    }

    // Register the sampler objects in the allocated objects monitor.
    suAllocatedObjectsMonitor::instance().registerAllocatedObjects(samplersForAllocationMonitor);
}


// ---------------------------------------------------------------------------
// Name:        DeleteSingleSamplerByName
// Description: Internally-linked auxiliary function that finds a sampler object
//              in a container by its name, deletes it and removes it from the
//              container
// Arguments:   gtVector<apGLSampler*>& - the target container
//              GLuint samplerName - the name of the object to be deleted and removed
// Author:      Amit Ben-Moshe
// Date:        29/6/2014
// ---------------------------------------------------------------------------
static bool DeleteSingleSamplerByName(gtVector<apGLSampler*>& samplers, GLuint samplerName)
{
    bool ret = false;
    int index = -1;

    int samplerCount = (int)samplers.size();

    for (int i = 0; i < samplerCount; ++i)
    {
        apGLSampler* pCurSampler = samplers[i];
        GT_IF_WITH_ASSERT(pCurSampler != NULL)
        {
            if (pCurSampler->samplerName() == samplerName)
            {
                // We found the requested sampler.
                index = i;
                break;
            }
        }
    }

    // Check if we found the requested sampler in the container.
    bool isSamplerFound = (index > -1);
    GT_ASSERT(isSamplerFound);

    if (isSamplerFound)
    {
        // Delete the requested sampler object.
        apGLSampler* pSamplerToDelete = samplers[index];
        delete pSamplerToDelete;
        pSamplerToDelete = NULL;

        // Remove the sampler object from the container
        samplers.removeItem(index);

        ret = true;
    }

    return ret;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::DeleteSamplers
// Description: Deletes the requested samplers
// Arguments:   GLuint samplersCount - amount of sampler objects to be deleted
//              const GLuint* pSamplersArr - array with the names of the samplers
//              to be deleted
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
void gsSamplersMonitor::deleteSamplers(GLuint samplersCount, const GLuint* pSamplersArr)
{
    for (size_t i = 0; i < samplersCount; i++)
    {
        const GLuint samplerName = pSamplersArr[i];
        GT_IF_WITH_ASSERT(samplerName > 0)
        {
            GT_ASSERT(DeleteSingleSamplerByName(m_glSamplers, samplerName));
        }
    }
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::BindSampler
// Description: Binds a sampler to a texture unit
// Arguments:   GLuint textureUnit - the name of the texture unit
//              GLuint samplerName - the name of the sampler
// Return value:bool (success/failure)
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
bool gsSamplersMonitor::bindSampler(GLuint textureUnit, GLuint samplerName)
{
    bool ret = false;
    apGLSampler* pSampler = getSampler(samplerName);
    GT_IF_WITH_ASSERT(pSampler != NULL)
    {
        pSampler->bindToTextureUnit(textureUnit);
        ret = true;
    }
    return ret;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::BindSamplers
// Description: Binds existing sampler objects to texture image units numbered firstTextureImageUnit through
//              (firstTextureImageUnit + samplersCount -1).
// Arguments:   GLuint firstTextureImageUnit - the name of the first texture unit
//              GLuint samplersCount - the number of sampler objects
//              pSamplersArr - if pSamplerArr is not NULL, it specifies an array of samplersCount values, each of which
//                             must be zero or the name of an existing sampler object
// Return value:bool (success/failure)
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
void gsSamplersMonitor::bindSamplers(GLuint firstTextureImageUnit, GLuint samplersCount, const GLuint* pSamplerArr)
{
    for (GLuint i = 0; i < samplersCount; i++)
    {
        if (pSamplerArr == NULL)
        {
            bindSampler(firstTextureImageUnit + i, 0);
        }
        else
        {
            bindSampler(firstTextureImageUnit + i, pSamplerArr[i]);
        }
    }
}


// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::GetSampler
// Description: Finds the requested sampler and returns a handle to it
// Arguments:   GLuint samplerName - the name of the requested sampler
// Return value:apGLSampler* - a handle to the requested sampler
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
apGLSampler* gsSamplersMonitor::getSampler(GLuint samplerName)
{
    apGLSampler* ret = NULL;

    // Run until we either went through all stored sampler or find the requested sampler.
    for (apGLSampler* pCurSampler : m_glSamplers)
    {
        // Obtain a handle to the current sampler.
        GT_IF_WITH_ASSERT(pCurSampler != NULL)
        {
            if (pCurSampler->samplerName() == samplerName)
            {
                // We have found the requested sampler, we can terminate the loop.
                ret = pCurSampler;
                break;
            }
        }
    }

    return ret;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::UpdateSingleSamplerData
// Description: Auxiliary function that updates a sampler data object by
//              querying the OpenGL API.
// Arguments:   apGLSampler* pSampler - a pointer to the sampler object to be updated
// Note:        This is an auxiliary function that assumes that all pointers (pSampler, m_glGetSamplerParameterfv, m_glGetSamplerParameteriv)
//              are not NULL. This is the caller's responsibility to verify this condition before calling this function.
// Return value:bool (success/failure)
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
bool gsSamplersMonitor::updateSingleSamplerData(apGLSampler* pSampler)
{
    bool ret = false;
    GT_IF_WITH_ASSERT(pSampler != NULL)
    {
        GT_IF_WITH_ASSERT(m_glGetSamplerParameteriv != NULL && m_glGetSamplerParameterfv != NULL)
        {
            // Prepare buffers for the values.
            GLfloat textureBorderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
            GLint textureCompareFunc = 0;
            GLint textureCompareMode = 0;
            GLfloat textureLodBias = 0.0f;
            GLfloat textureMaxLod = 0.0f;
            GLfloat textureMinLod = 0.0f;
            GLint textureMagFilter = 0;
            GLint textureMinFilter = 0;
            GLint textureWrapS = 0;
            GLint textureWrapT = 0;
            GLint textureWrapR = 0;

            // Obtain the sampler's name.
            GLuint samplerName = pSampler->samplerName();

            // Use the GL API to obtain the updated data.
            m_glGetSamplerParameterfv(samplerName, GL_TEXTURE_BORDER_COLOR, textureBorderColor);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_COMPARE_FUNC, &textureCompareFunc);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_COMPARE_MODE, &textureCompareMode);
            m_glGetSamplerParameterfv(samplerName, GL_TEXTURE_LOD_BIAS, &textureLodBias);
            m_glGetSamplerParameterfv(samplerName, GL_TEXTURE_MAX_LOD, &textureMaxLod);
            m_glGetSamplerParameterfv(samplerName, GL_TEXTURE_MIN_LOD, &textureMinLod);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_MAG_FILTER, &textureMagFilter);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_MIN_FILTER, &textureMinFilter);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_WRAP_S, &textureWrapS);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_WRAP_T, &textureWrapT);
            m_glGetSamplerParameteriv(samplerName, GL_TEXTURE_WRAP_R, &textureWrapR);

            // Update the data object with the collected values.
            pSampler->setSamplerColor(textureBorderColor[0], textureBorderColor[1], textureBorderColor[2], textureBorderColor[3]);
            pSampler->setSamplerComparisonFunction(textureCompareFunc);
            pSampler->setSamplerComparisonMode(textureCompareMode);
            pSampler->setSamplerLodBias(textureLodBias);
            pSampler->setSamplerMaxLod(textureMaxLod);
            pSampler->setSamplerMinLod(textureMinLod);
            pSampler->setSamplerMagnificationFunction(textureMagFilter);
            pSampler->setSamplerMinificationFunction(textureMinFilter);
            pSampler->setSwrapMode(textureWrapS);
            pSampler->setTwrapMode(textureWrapT);
            pSampler->setRwrapMode(textureWrapR);

            ret = true;
        }
    }
    return ret;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::updateContextDataSnapshot
// Description: Updates the samplers objects data. This function is being called
//              by the render context monitor when the context thread freezes.
// Return Val:  bool - Success / failure.
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
bool gsSamplersMonitor::updateContextDataSnapshot()
{
    bool ret = false;
    GT_IF_WITH_ASSERT(m_glGetSamplerParameteriv != NULL && m_glGetSamplerParameterfv != NULL)
    {
        SU_BEFORE_EXECUTING_REAL_FUNCTION(ap_glGetSamplerParameterfv);
        SU_BEFORE_EXECUTING_REAL_FUNCTION(ap_glGetSamplerParameteriv);

        // Go through all stored samplers and update each of them.
        ret = true;

        for (size_t i = 0; i < m_glSamplers.size(); ++i)
        {
            // Obtain a handle to the current sampler.
            apGLSampler* pCurSampler = m_glSamplers[i];
            GT_IF_WITH_ASSERT(pCurSampler != NULL)
            {
                GLuint samplerName = pCurSampler->samplerName();
                GT_IF_WITH_ASSERT(samplerName != 0)
                {
                    // Update the current sampler's data.
                    ret = ret && updateSingleSamplerData(pCurSampler);
                    GT_ASSERT(ret);
                }
            }
        }

        SU_AFTER_EXECUTING_REAL_FUNCTION(ap_glGetSamplerParameteriv);
        SU_AFTER_EXECUTING_REAL_FUNCTION(ap_glGetSamplerParameterfv);
    }

    return ret;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::onFirstTimeContextMadeCurrent
// Description: Installs the relevant OpenGL API function pointers
// Note:        This function is to be called on the first time that the relevant
//              context becomes the current context.
// Author:      Amit Ben-Moshe
// Date:        25/6/2014
// ---------------------------------------------------------------------------
void gsSamplersMonitor::onFirstTimeContextMadeCurrent()
{
    // Obtain pointers to the GL API functions.
    m_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)gsGetSystemsOGLModuleProcAddress("glGetSamplerParameterfv");
    m_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)gsGetSystemsOGLModuleProcAddress("glGetSamplerParameteriv");
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::GetAmountOfSamplerObjects
// Description: Returns the number of sampler objects that are being
//              monitored by this object
// Return value - size_t (the amount of samplers)
// Author:      Amit Ben-Moshe
// Date:        25/6/2014
// ---------------------------------------------------------------------------
size_t gsSamplersMonitor::getAmountOfSamplerObjects() const
{
    return m_glSamplers.size();
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::GetSamplerDetails
// Description: Finds the requested sampler and returns a handle to it
// Arguments:   GLuint samplerName - the name of the requested sampler
// Return value:apGLSampler* - a handle to the requested sampler
// Author:      Amit Ben-Moshe
// Date:        26/6/2014
// ---------------------------------------------------------------------------
const apGLSampler* gsSamplersMonitor::GetSamplerDetails(GLuint samplerName) const
{
    const apGLSampler* retVal = nullptr;

    // Run until we either went through all stored sampler or find the requested sampler.
    for (const apGLSampler* pCurrentSampler : m_glSamplers)
    {
        // Obtain a handle to the current sampler.
        GT_IF_WITH_ASSERT(pCurrentSampler != NULL)
        {
            if (pCurrentSampler->samplerName() == samplerName)
            {
                // We have found the requested sampler, we can terminate the loop.
                retVal = pCurrentSampler;
                break;
            }
        }
    }

    return retVal;
}

// ---------------------------------------------------------------------------
// Name:        gsSamplersMonitor::getSamplerNameByIndex
// Description: Returns the name of the sampler object whose index in
//              the monitor's internal storage is the given index
// Arguments:   size_t samplerIndex - the given index
//              GLuint& samplerNameBuffer - an output parameter to hold the name of the
//              requested sampler object (note that the value in this
//              variable should be treated as valid only if the function succeeds
//              (returns true).
// Return value - bool (success/failure)
// Author:      Amit Ben-Moshe
// Date:        25/6/2014
// ---------------------------------------------------------------------------
bool gsSamplersMonitor::getSamplerNameByIndex(size_t samplerIndex, GLuint& samplerNameBuffer) const
{
    bool ret = false;

    GT_IF_WITH_ASSERT(samplerIndex < m_glSamplers.size())
    {
        apGLSampler* pSampler = m_glSamplers[samplerIndex];
        GT_IF_WITH_ASSERT(pSampler != NULL)
        {
            samplerNameBuffer = pSampler->samplerName();
            ret = true;
        }
    }

    return ret;
}
