#include "gpuCacheConfig.h"
#include "gpuCacheVramQuery.h"
#include <maya/MString.h>
#include <maya/MGlobal.h>
#include <stdio.h>
#include <limits>
#ifdef max
#undef max
#endif
namespace {
using namespace GPUCache;
{
    return expandedEnv != envQuery;
}
size_t getVP2OverrideAPIDefault()
{
    
    if (expandEnv(vp2OverrideEnv, "MAYA_GPUCACHE_VP2_OVERRIDE_API")) {
        if (vp2OverrideEnv == "MPxDrawOverride") {
            return Config::kMPxDrawOverride;
        }
        else if (vp2OverrideEnv == "MPxSubSceneOverride") {
            return Config::kMPxSubSceneOverride;
        }
        else {
            printf("MAYA_GPUCACHE_VP2_OVERRIDE_API is set but it is neither "
                   "MPxDrawOverride nor MPxSubSceneOverride. "
                   "Using MPxSubSceneOverride instead.\n");
            return Config::kMPxSubSceneOverride;
        }
    }
    
    return Config::kMPxSubSceneOverride;
}
bool getIgnoreUVsDefault()
{
    
    bool result = 1;
    return result;
}
size_t getMinVertsForVBOsDefault()
{
    
    
    
    
    size_t result = 128;
    return result;
}
size_t getMaxVBOCountDefault()
{
    
    
    
    
#ifdef OSMac_
    
    
    const size_t defVal = 8192;
#else    
    const size_t defVal = std::numeric_limits<int>::max();
#endif
    size_t result = defVal;
    return result;
}
size_t getMaxVBOSizeDefault()
{
    size_t result;
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    const size_t vramMB = VramQuery::queryVram() / 1024 / 1024;
    float resultMB = 0.0f;
    
    if (vramMB < 128) {
        resultMB = 0.f;
    }
    else if (vramMB < 512) {
        resultMB = (vramMB -  128) * (( 256.f -   0.f) / ( 512.f -  128.f)) +   0.f;
    }
    else if (vramMB < 1024) {
        resultMB = (vramMB -  512) * (( 640.f - 256.f) / (1024.f -  512.f)) + 256.f;
    }
    else if (vramMB < 2048) {
        resultMB = (vramMB - 1024) * ((1536.f - 640.f) / (2048.f - 1024.f)) + 640.f;
    }
    else {
        resultMB = vramMB - 512;
    }
    result = size_t(resultMB * 1024 * 1024);
    return result;
}
bool getUseVertexArrayWhenVRAMIsLowDefault()
{
    bool result;
    
#if defined(_WIN32)
    
    
    result = false;
#elif defined(LINUX)
    
    
    
    
    
    
    
    
    
    
    
    result = !VramQuery::isQuadro();
#else        
    
    
    result = true;
#endif
    return result;
}
bool getUseVertexArrayForGLPickingDefault()
{
    bool result;
    
#ifdef OSMac_
    
    
    
    
    result = true;
#else
    result = false;
#endif
    return result;
}
bool getUseGLPrimitivesInsteadOfVADefault()
{
    bool result = false;
    
#if defined(_WIN32)
    if (VramQuery::isQuadro() ) {
        
        
        
        
        int driverVersion[3] = {0};
        VramQuery::driverVersion(driverVersion);
        if (driverVersion[0] < 295 || 
                (driverVersion[0] == 295 && driverVersion[1] < 65)) {
            result = true;
        }
    }
#endif
    return result;
}
bool getEmulateTwoSidedLightingDefault()
{
    bool result;
    
    
#ifdef _WIN32
    result = VramQuery::isGeforce();
#else
    result = false;
#endif
    return result;
}
size_t getOpenGLPickingWireframeThresholdDefault()
{
#ifdef OSMac_
    
    
    const int defVal = std::numeric_limits<int>::max();
#else    
    const int defVal = 128;
#endif
    return defVal;
}
size_t getOpenGLPickingSurfaceThresholdDefault()
{
#ifdef OSMac_
    
    
    const int defVal = std::numeric_limits<int>::max();
#else    
    const int defVal = 1024;
#endif
    return defVal;
}
bool getBackgroundReadingDefault()
{
    return true;
}
size_t getBackgroundReadingRefreshDefault()
{
    return 1000;
}
bool getUseHardwareInstancingDefault()
{
    return true;
}
size_t getHardwareInstancingThresholdDefault()
{
    return 2;
}
}
namespace GPUCache {
const MColor Config::kDefaultGrayColor = 
MColor(0.5f, 0.5f, 0.5f) * 0.8f;
 
const MColor Config::kDefaultTransparency = 
MColor(0.0f, 0.0f, 0.0f);
 
const unsigned short Config::kLineStippleShortDashed = 0x0303;
const unsigned short Config::kLineStippleDotted = 0x0101;
const MString Config::kDisplayFilter = 
"gpuCacheDisplayFilter";
 
bool   Config::sInitialized = false;
size_t Config::sDefaultMaxVBOSize;
size_t Config::sDefaultMaxVBOCount;
size_t Config::sDefaultMinVertsForVBOs;
bool   Config::sDefaultUseVertexArrayWhenVRAMIsLow;
bool   Config::sDefaultUseVertexArrayForGLPicking;
size_t Config::sDefaultOpenGLPickingWireframeThreshold;
size_t Config::sDefaultOpenGLPickingSurfaceThreshold;
bool   Config::sDefaultUseGLPrimitivesInsteadOfVA;
bool   Config::sDefaultEmulateTwoSidedLighting;
bool   Config::sDefaultIsIgnoringUVs;
size_t Config::sDefaultVP2OverrideAPI;
bool   Config::sDefaultBackgroundReading;
size_t Config::sDefaultBackgroundReadingRefresh;
bool   Config::sDefaultUseHardwareInstancing;
size_t Config::sDefaultHardwareInstancingThreshold;
size_t Config::sMaxVBOSize;
size_t Config::sMaxVBOCount;
size_t Config::sMinVertsForVBOs;
bool   Config::sUseVertexArrayWhenVRAMIsLow;
bool   Config::sUseVertexArrayForGLPicking;
size_t Config::sOpenGLPickingWireframeThreshold;
size_t Config::sOpenGLPickingSurfaceThreshold;
bool   Config::sUseGLPrimitivesInsteadOfVA;
bool   Config::sEmulateTwoSidedLighting;
bool   Config::sIsIgnoringUVs;
size_t Config::sVP2OverrideAPI;
bool   Config::sBackgroundReading;
size_t Config::sBackgroundReadingRefresh;
bool   Config::sUseHardwareInstancing;
size_t Config::sHardwareInstancingThreshold;
void syncIntOptionVar(bool automatic, const char * autoOptVar, const char * valueOptVar, size_t defaultValue, size_t& dest, int multiplier=1)
{
    bool existAuto = false;
    if ( !automatic && existAuto && autoValue == 0 ) {
        bool exist = false;
        if (exist) {
            dest = value * multiplier;
        }
        else {
            dest = defaultValue;
        }
    }
    else {
        dest = defaultValue;
    }
}
void syncBoolOptionVar(bool automatic, const char * autoOptVar, const char * valueOptVar, bool defaultValue, bool& dest, bool valueToCompare)
{
    bool existAuto = false;
    if ( !automatic && existAuto && autoValue == 0 ){
        bool exist = false;
        if (exist) {
            dest = (static_cast<bool>(value) == valueToCompare);
        }
        else {
            dest = defaultValue;
        }
    }
    else {
        dest = defaultValue;
    }
}
void syncBoolOptionVar(bool automatic, const char * autoOptVar, const char * valueOptVar, bool defaultValue, bool& dest, int valueToCompare)
{
    bool existAuto = false;
    
    int defaultInt = (valueToCompare==1 ? (defaultValue ? 1 : 2) : (defaultValue ? 2 : 1) );
    if ( !automatic && existAuto && autoValue == 0 ){
        bool exist = false;
        if (exist) {
            dest = (value == valueToCompare);
        }
        else {
            dest = defaultValue;
        }
    }
    else {
        dest = defaultValue;
    }
}
Config::VP2OverrideAPI Config::vp2OverrideAPI()
{
    
    
    
    static bool initialized = false;
    static VP2OverrideAPI sCurrentVP2OverrideAPI = kMPxSubSceneOverride;
    
    
    if (!initialized) {
        
        sVP2OverrideAPI = sDefaultVP2OverrideAPI = getVP2OverrideAPIDefault();
        
        bool existAllAuto = false;
        bool automatic = !existAllAuto || allAutoValue == 1;
        
        syncIntOptionVar(automatic, "gpuCacheVP2OverrideAPIAuto", "gpuCacheVP2OverrideAPI", sDefaultVP2OverrideAPI, sVP2OverrideAPI);
        sCurrentVP2OverrideAPI = (Config::VP2OverrideAPI)sVP2OverrideAPI;
        initialized = true;
    }
    return sCurrentVP2OverrideAPI;
}
bool Config::isIgnoringUVs()
{
    initialize();
    return sIsIgnoringUVs;
}
size_t Config::minVertsForVBOs()
{
    initialize();
    return sMinVertsForVBOs;
}
size_t Config::maxVBOCount()
{
    initialize();
    return sMaxVBOCount;
}
size_t Config::maxVBOSize()
{
    initialize();
    return sMaxVBOSize;
}
bool Config::useVertexArrayWhenVRAMIsLow()
{
    initialize();
    return sUseVertexArrayWhenVRAMIsLow;
}
bool Config::useVertexArrayForGLPicking()
{
    initialize();
    return sUseVertexArrayForGLPicking;
}
bool Config::useGLPrimitivesInsteadOfVA()
{
    initialize();
    return sUseGLPrimitivesInsteadOfVA;
}
bool Config::emulateTwoSidedLighting()
{
    initialize();
    return sEmulateTwoSidedLighting;
}
size_t Config::openGLPickingWireframeThreshold()
{
    initialize();
    return sOpenGLPickingWireframeThreshold;
}
size_t Config::openGLPickingSurfaceThreshold()
{
    initialize();
    return sOpenGLPickingSurfaceThreshold;
}
bool Config::backgroundReading()
{
    initialize();
    return sBackgroundReading;
}
size_t Config::backgroundReadingRefresh()
{
    initialize();
    return sBackgroundReadingRefresh;
}
bool Config::useHardwareInstancing()
{
    initialize();
    return sUseHardwareInstancing;
}
size_t Config::hardwareInstancingThreshold()
{
    initialize();
    return sHardwareInstancingThreshold;
}
void Config::refresh()
{
    if (!sInitialized) {
        initialize();
        return;  
    }
    bool existAllAuto = false;
    if (!existAllAuto) {
    }
    bool automatic = !existAllAuto || allAutoValue == 1;
    syncIntOptionVar(automatic, "gpuCacheMaxVramAuto", "gpuCacheMaxVram", sDefaultMaxVBOSize, sMaxVBOSize, 1024*1024);
    syncIntOptionVar(automatic, "gpuCacheMaxNumOfBuffersAuto", "gpuCacheMaxNumOfBuffers", sDefaultMaxVBOCount, sMaxVBOCount);
    syncIntOptionVar(automatic, "gpuCacheMinVerticesPerShapeAuto", "gpuCacheMinVerticesPerShape", sDefaultMinVertsForVBOs, sMinVertsForVBOs);
    syncBoolOptionVar(automatic, "gpuCacheLowVramOperationAuto", "gpuCacheLowMemMode", sDefaultUseVertexArrayWhenVRAMIsLow, sUseVertexArrayWhenVRAMIsLow, 2);
    syncBoolOptionVar(automatic, "gpuCacheGlSelectionModeAuto", "gpuCacheGlSelectionMode", sDefaultUseVertexArrayForGLPicking, sUseVertexArrayForGLPicking, 1);
    syncIntOptionVar(automatic, "gpuCacheSelectionWireThresholdAuto", "gpuCacheSelectionWireThreshold", sDefaultOpenGLPickingWireframeThreshold, sOpenGLPickingWireframeThreshold);
    syncIntOptionVar(automatic, "gpuCacheSelectionSurfaceThresholdAuto", "gpuCacheSelectionSurfaceThreshold", sDefaultOpenGLPickingSurfaceThreshold, sOpenGLPickingSurfaceThreshold);
    syncBoolOptionVar(automatic, "gpuCacheDisableVertexArraysAuto", "gpuCacheUseVertexArrays", sDefaultUseGLPrimitivesInsteadOfVA, sUseGLPrimitivesInsteadOfVA, 2);
    syncBoolOptionVar(automatic, "gpuCacheTwoSidedLightingAuto", "gpuCacheTwoSidedLightingMode", sDefaultEmulateTwoSidedLighting, sEmulateTwoSidedLighting, 2);
    syncBoolOptionVar(automatic, "gpuCacheUvCoordinatesAuto", "gpuCacheIgnoreUv", sDefaultIsIgnoringUVs, sIsIgnoringUVs, true);
    syncIntOptionVar(automatic, "gpuCacheVP2OverrideAPIAuto", "gpuCacheVP2OverrideAPI", sDefaultVP2OverrideAPI, sVP2OverrideAPI);
    syncBoolOptionVar(automatic, "gpuCacheBackgroundReadingAuto", "gpuCacheBackgroundReading", sDefaultBackgroundReading, sBackgroundReading, true);
    syncIntOptionVar(automatic, "gpuCacheBackgroundReadingRefreshAuto", "gpuCacheBackgroundReadingRefresh", sDefaultBackgroundReadingRefresh, sBackgroundReadingRefresh);
    syncBoolOptionVar(automatic, "gpuCacheUseHardwareInsancingAuto", "gpuCacheUseHardwareInstancing", sDefaultUseHardwareInstancing, sUseHardwareInstancing, true);
    syncIntOptionVar(automatic, "gpuCacheHardwareInstancingThresholdAuto", "gpuCacheHardwareInstancingThreshold", sDefaultHardwareInstancingThreshold, sHardwareInstancingThreshold);
}
void Config::initialize()
{
    
    if (!sInitialized) {
        
        sDefaultMaxVBOSize                      = getMaxVBOSizeDefault();
        sDefaultMaxVBOCount                     = getMaxVBOCountDefault();
        sDefaultMinVertsForVBOs                 = getMinVertsForVBOsDefault();
        sDefaultUseVertexArrayWhenVRAMIsLow     = getUseVertexArrayWhenVRAMIsLowDefault();
        sDefaultUseVertexArrayForGLPicking      = getUseVertexArrayForGLPickingDefault();
        sDefaultOpenGLPickingWireframeThreshold = getOpenGLPickingWireframeThresholdDefault();
        sDefaultOpenGLPickingSurfaceThreshold   = getOpenGLPickingSurfaceThresholdDefault();
        sDefaultUseGLPrimitivesInsteadOfVA      = getUseGLPrimitivesInsteadOfVADefault();
        sDefaultEmulateTwoSidedLighting         = getEmulateTwoSidedLightingDefault();
        sDefaultIsIgnoringUVs                   = getIgnoreUVsDefault();
        sDefaultBackgroundReading               = getBackgroundReadingDefault();
        sDefaultBackgroundReadingRefresh        = getBackgroundReadingRefreshDefault();
        sDefaultUseHardwareInstancing           = getUseHardwareInstancingDefault();
        sDefaultHardwareInstancingThreshold     = getHardwareInstancingThresholdDefault();
        
        sMaxVBOSize                      = sDefaultMaxVBOSize;
        sMaxVBOCount                     = sDefaultMaxVBOCount;
        sMinVertsForVBOs                 = sDefaultMinVertsForVBOs;
        sUseVertexArrayWhenVRAMIsLow     = sDefaultUseVertexArrayWhenVRAMIsLow;
        sUseVertexArrayForGLPicking      = sDefaultUseVertexArrayForGLPicking;
        sOpenGLPickingWireframeThreshold = sDefaultOpenGLPickingWireframeThreshold;
        sOpenGLPickingSurfaceThreshold   = sDefaultOpenGLPickingSurfaceThreshold;
        sUseGLPrimitivesInsteadOfVA      = sDefaultUseGLPrimitivesInsteadOfVA;
        sEmulateTwoSidedLighting         = sDefaultEmulateTwoSidedLighting;
        sIsIgnoringUVs                   = sDefaultIsIgnoringUVs;
        sBackgroundReading               = sDefaultBackgroundReading;
        sBackgroundReadingRefresh        = sDefaultBackgroundReadingRefresh;
        sUseHardwareInstancing           = sDefaultUseHardwareInstancing;
        sHardwareInstancingThreshold     = sDefaultHardwareInstancingThreshold;
        sInitialized = true;
    
        
        refresh();
#ifdef _WIN32
        
        
        if (VramQuery::isQuadro()) {
            int driverVersion[3] = {0};
            VramQuery::driverVersion(driverVersion);
            if (driverVersion[0] != 0 && getenv("MAYA_GPUCACHE_WORKAROUND_QUADRO_PAGE_READONLY") == NULL &&
                (driverVersion[0] < 332 ||
                    (driverVersion[0] == 332 && driverVersion[1] < 50))) {
                printf("The graphics driver (%d.%d.%d) has known issues and might not work properly with gpuCache.\n",
                    driverVersion[0], driverVersion[1], driverVersion[2]);
                printf("Please upgrade the graphics driver to the latest version. (> 332.50)\n");
                printf("Otherwise, set MAYA_GPUCACHE_WORKAROUND_QUADRO_PAGE_READONLY env if the driver has to be kept.\n");
            }
        }
#endif
    }
}
}