#include <maya/MHardwareRenderer.h>
#include <maya/MGlobal.h>
#include <maya/MGLFunctionTable.h>
#include "cgfxEffectDef.h"
#include "cgfxFindImage.h"
#include "cgfxShaderNode.h"
#include <sys/stat.h>
#include <map>
#ifdef _WIN32
#else
#   include <sys/timeb.h>
#   include <string.h>
#
#   define stricmp strcasecmp
#   define strnicmp strncasecmp
#endif
#undef ENABLE_TRACE_API_CALLS
#ifdef ENABLE_TRACE_API_CALLS
#define TRACE_API_CALLS(x) cerr << "cgfxShader: "<<(x)<<"\n"
#else
#define TRACE_API_CALLS(x)
#endif
cgfxVertexAttribute::cgfxVertexAttribute()
 : fNext( NULL), fSourceType( kUnknown), fSourceIndex( 0), refcount(0)
{
}
void cgfxVertexAttribute::release() const
{
    --refcount;
    if (refcount <= 0)
    {
        M_CHECK( refcount == 0 );
        delete this;
    }
}
cgfxVaryingParameter::cgfxVaryingParameter(CGparameter parameter)
 :  fParameter( parameter),
    fVertexAttribute( NULL),
    fVertexStructure( NULL),
    fNext( NULL)
{
    if( parameter)
    {
        fName = cgGetParameterName( parameter);
    }
}
cgfxVaryingParameter::~cgfxVaryingParameter()
{
    delete fVertexStructure;
    delete fNext;
}
void cgfxVaryingParameter::setupAttributes( cgfxRCPtr<cgfxVertexAttribute>& vertexAttributes, CGprogram program)
{
    
    int lastDot = attrName.
rindex( 
'.');
 
    if( lastDot >= 0)
    
    MString semanticName = cgGetParameterSemantic( fParameter);
 
    cgGetParameterSemantic( fParameter);
    semantic.toUpperCase();
    
    CGtype type = cgGetNamedUserType( program, attrName.
asChar());
    if( type != CG_UNKNOWN_TYPE)
    {
        
        CGcontext context = cgGetProgramContext( program); 
        CGparameter packing = cgCreateParameter( context, type);
        fVertexStructure = new cgfxVaryingParameterStructure();
        fVertexStructure->fLength = 0;
        fVertexStructure->fSize = 0;
        CGparameter element = cgGetFirstStructParameter( packing);
        while( element)
        {
            MString elementName = cgGetParameterName( element);
 
            int lastDot = elementName.
rindex( 
'.');
 
            if( lastDot >= 0)
                elementName = elementName.
substring( lastDot + 1, elementName.
length() - 1);
            cgfxRCPtr<cgfxVertexAttribute> attr = setupAttribute( elementName, semantic, element, vertexAttributes);
            fVertexStructure->fElements[ fVertexStructure->fLength].fVertexAttribute = attr;
            int size = cgGetParameterRows( element) * cgGetParameterColumns( element);
            CGtype type = cgGetParameterBaseType( element);
            if( type == CG_FLOAT) size *= sizeof( GLfloat);
            else if( type == CG_INT) size *= sizeof( GLint);
            fVertexStructure->fElements[ fVertexStructure->fLength].fSize = size;
            fVertexStructure->fLength++;
            fVertexStructure->fSize += size;
            element = cgGetNextParameter( element);
        }
        cgDestroyParameter( packing); 
    }
    else
    {
        
        fVertexAttribute = setupAttribute( attrName, semantic, fParameter, vertexAttributes);
    }
    
    
    
    int radix = 1;
    fGLIndex = 0;
    unsigned int length = semantic.length();
    const char* str = semantic.asChar();
    
    if (length == 0) {
         fGLType = glRegister::kUnknown;
         return;
    }
    for(;;)
    {
        char c = str[ length - 1];
        if( c < '0' || c > '9') break;
        fGLIndex += radix * (c - '0');
        radix *= 10;
        --length;
    }
    if( semantic.length() != length)
        semantic = semantic.substring( 0, length - 1);
    
    
    
    
    
    
    if( semantic == "POSITION")
    {
        fGLType = glRegister::kPosition;
        fVertexAttribute->fSourceName = "position";
    }
    else if( semantic == "NORMAL")
    {
        fGLType = glRegister::kNormal;
        if( fVertexAttribute.isNull() == false ) 
            fVertexAttribute->fSourceName = "normal";
    }
    else if( semantic == "TEXCOORD")
    {
        fGLType = glRegister::kTexCoord;
        if( fVertexAttribute.isNull() == false ) 
        {
                fVertexAttribute->fSourceName = "tangent:map1";
                fVertexAttribute->fSourceName = "binormal:map1";
            else
                fVertexAttribute->fSourceName = "uv:map1";
        }
    }
    else if( semantic == "TANGENT")
    {
        fGLType = glRegister::kTexCoord;
        fGLIndex += 6; 
        if( fVertexAttribute.isNull() == false ) 
            fVertexAttribute->fSourceName = "tangent:map1";
    }
    else if( semantic == "BINORMAL")
    {
        fGLType = glRegister::kTexCoord;
        fGLIndex += 7; 
        if( fVertexAttribute.isNull() == false ) 
            fVertexAttribute->fSourceName = "binormal:map1";
    }
    else if( semantic == "COLOR")
    {
        fGLType = fGLIndex == 1 ? glRegister::kSecondaryColor : glRegister::kColor;
    }
    else if( semantic == "ATTR")
    {
        fGLType = glRegister::kVertexAttrib;
        if( fVertexAttribute.isNull() == false ) 
        {
            fVertexAttribute->fSourceName = semanticName;
        }
    }
    else if( semantic == "PSIZE")
    {
        fGLType = glRegister::kVertexAttrib;
        fGLIndex = 6;
    }
    else
    {
        fGLType = glRegister::kUnknown;
    }
}
cgfxRCPtr<cgfxVertexAttribute> cgfxVaryingParameter::setupAttribute(
    CGparameter parameter, 
    cgfxRCPtr<cgfxVertexAttribute>& vertexAttributes
)
{
    
    cgfxRCPtr<cgfxVertexAttribute>* attribute = &vertexAttributes;
    while( attribute->isNull() == false )
    {
        if( (*attribute)->fName == name)
        {
            return *attribute;
        }
        attribute = &(*attribute)->fNext;
    }
    
    cgfxRCPtr<cgfxVertexAttribute> attr = cgfxRCPtr<cgfxVertexAttribute>(new cgfxVertexAttribute());
    *attribute = attr;
    
    attr->fName = name;
    attr->fType = cgGetTypeString( cgGetParameterType( parameter));
    attr->fSemantic = semantic;
    return attr;
}
void cgfxVaryingParameter::bind(
    const MDagPath& shape, cgfxStructureCache* cache,
 
    int vertexCount, const float * vertexArray,
    int normalsPerVertex, int normalCount, const float ** normalArrays,
    int colorCount, const float ** colorArrays,
    int texCoordCount, const float ** texCoordArrays
) const
{
    bool result = false;
    if( fVertexAttribute.isNull() == false  && fParameter)
    {
        switch( fVertexAttribute->fSourceType)
        {
            case cgfxVertexAttribute::kPosition:
                result = bind( vertexArray, 3);
                break;
            case cgfxVertexAttribute::kNormal:
                if( normalCount > 0 && normalArrays[ 0])
                    result = bind( normalArrays[0], 3);
                break;
            case cgfxVertexAttribute::kUV:
                if( texCoordCount > fVertexAttribute->fSourceIndex && texCoordArrays[ fVertexAttribute->fSourceIndex])
                    result = bind( texCoordArrays[ fVertexAttribute->fSourceIndex], 2);
                break;
            case cgfxVertexAttribute::kTangent:
                if( normalCount >= normalsPerVertex * fVertexAttribute->fSourceIndex + 1 && normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 1])
                    result = bind( normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 1], 3);
                break;
            case cgfxVertexAttribute::kBinormal:
                if( normalCount >= normalsPerVertex * fVertexAttribute->fSourceIndex + 2 && normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 2])
                    result = bind( normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 2], 3);
                break;
            case cgfxVertexAttribute::kColor:
                if( colorCount > fVertexAttribute->fSourceIndex && colorArrays[ fVertexAttribute->fSourceIndex])
                    result = bind( colorArrays[ fVertexAttribute->fSourceIndex], 4);
                break;
            default:
                break;
        }
    }
    else if( fVertexStructure && fParameter && vertexCount)
    {
        
        structureName += fVertexStructure->fSize;
        for( int i = 0; i < fVertexStructure->fLength; i++)
        {
                cgfxRCPtr<cgfxVertexAttribute> vertexAttribute = fVertexStructure->fElements[ i].fVertexAttribute;
                if( vertexAttribute.isNull() == false ) structureName += fVertexStructure->fElements[ i].fVertexAttribute->fSourceName;
                structureName += fVertexStructure->fElements[ i].fSize;
        }
        
        char* data = cache->findEntry(shape, structureName);
        
        if (!data)
        {
            
            
            data = cache->addEntry(
                shape, structureName, fVertexStructure->fSize, vertexCount);
            char* dest = data;
            for( int i = 0; i < fVertexStructure->fLength; i++)
            {
                cgfxRCPtr<cgfxVertexAttribute> vertexAttribute = fVertexStructure->fElements[ i].fVertexAttribute;
                if( vertexAttribute.isNull() == false )
                {
                    const char* src = NULL;
                    int size = 0;
                    switch( vertexAttribute->fSourceType)
                    {
                        case cgfxVertexAttribute::kPosition:
                            src = (const char*)vertexArray;
                            size = 3 * sizeof( float);
                            break;
                        case cgfxVertexAttribute::kNormal:
                            if( normalCount > 0 && normalArrays[ 0])
                            {
                                src = (const char*)normalArrays[0];
                                size = 3 * sizeof( float);
                            }
                            break;
                        case cgfxVertexAttribute::kUV:
                            if( texCoordCount > vertexAttribute->fSourceIndex && texCoordArrays[ vertexAttribute->fSourceIndex])
                            {
                                src = (const char*)texCoordArrays[ vertexAttribute->fSourceIndex];
                                size = 2 * sizeof( float);
                            }
                            break;
                        case cgfxVertexAttribute::kTangent:
                            if( normalCount >= normalsPerVertex * vertexAttribute->fSourceIndex + 1 && normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 1])
                            {
                                src = (const char*)normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 1];
                                size = 3 * sizeof( float);
                            }
                            break;
                        case cgfxVertexAttribute::kBinormal:
                            if( normalCount >= normalsPerVertex * vertexAttribute->fSourceIndex + 2 && normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 2])
                            {
                                src = (const char*)normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 2];
                                size = 3 * sizeof( float);
                            }
                            break;
                        case cgfxVertexAttribute::kColor:
                            if( colorCount > vertexAttribute->fSourceIndex && colorArrays[ vertexAttribute->fSourceIndex])
                            {
                                src = (const char*)colorArrays[ vertexAttribute->fSourceIndex];
                                size = 4 * sizeof( float);
                            }
                            break;
                        default:
                            break;
                    }
                    
                    if( src && size)
                    {
                        
                        int srcSkip = 0;
                        if( size > fVertexStructure->fElements[ i].fSize)
                        {
                            srcSkip = size - fVertexStructure->fElements[ i].fSize;
                            size = fVertexStructure->fElements[ i].fSize;
                        }
                        int dstSkip = fVertexStructure->fSize - size;
                        char* dst = dest;
                        for( int v = 0; v < vertexCount; v++)
                        {
                            for( int b = 0; b < size; b++)
                                *dst++ = *src++;
                            src += srcSkip;
                            dst += dstSkip;
                        }
                    }
                    else
                    {
                        
                        size = fVertexStructure->fElements[ i].fSize;
                        int dstSkip = fVertexStructure->fSize - size;
                        char* dst = dest;
                        for( int v = 0; v < vertexCount; v++)
                        {
                            for( int b = 0; b < size; b++)
                                *dst++ = 0;
                            dst += dstSkip;
                        }
                    }
                }
                dest += fVertexStructure->fElements[ i].fSize;
            }
        }
        result = bind( (const float*)data, fVertexStructure->fSize / sizeof( float));
    }
    
    if( !result)
        null();
}
bool cgfxVaryingParameter::bind( const float* data, int stride) const
{
    bool result = false;
    switch( fGLType)
    {
        case glRegister::kPosition:
            glStateCache::instance().enablePosition();
            glVertexPointer( stride, GL_FLOAT, 0, data);
            result = true;
            break;
        case glRegister::kNormal:
            if( stride == 3)
            {
                glStateCache::instance().enableNormal();
                glNormalPointer( GL_FLOAT, 0, data);
                result = true;
            }
            break;
        case glRegister::kTexCoord:
            if( fGLIndex < glStateCache::sMaxTextureUnits)
            {
                glStateCache::instance().enableAndActivateTexCoord( fGLIndex);
                glTexCoordPointer( stride, GL_FLOAT, 0, data);
                result = true;
            }
            break;
        case glRegister::kColor:
            if( stride > 2)
            {
                glStateCache::instance().enableColor();
                glColorPointer( stride, GL_FLOAT, 0, data);
                result = true;
            }
            break;
        case glRegister::kSecondaryColor:
            if( stride > 2)
            {
                glStateCache::instance().enableSecondaryColor();
                if( glStateCache::glVertexAttribPointer) 
                    glStateCache::glSecondaryColorPointer( stride, GL_FLOAT, 0, (GLvoid*)data);
                result = true;
            }
            break;
        case glRegister::kVertexAttrib:
            glStateCache::instance().enableVertexAttrib( fGLIndex);
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glVertexAttribPointer( fGLIndex, stride, GL_FLOAT, GL_FALSE, 0, data);
            result = true;
            break;
        default:
            break;
    }
    return result;
}
bool cgfxVaryingParameter::bind(const sourceStreamInfo& source) const
{
    
    
    
    if ( 0 == gGLFT )
    
    const unsigned int stride = source.fStride;
    const unsigned int offset = source.fOffset;
    const unsigned int dimension  = source.fDimension;
    const unsigned int elementSize  = source.fElementSize;
    const GLuint bufferId = source.fDataBufferId;
    gGLFT->
glBindBufferARB(MGL_ARRAY_BUFFER_ARB, bufferId);
    #define GLOBJECT_BUFFER_OFFSET(i) ((char *)NULL + (i)) // For GLObject offsets
    switch( fGLType)
    {
        case glRegister::kPosition:
            glStateCache::instance().enablePosition();
            glVertexPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;
        case glRegister::kNormal:
            glStateCache::instance().enableNormal();
            glNormalPointer(GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;
        case glRegister::kTexCoord:
            if( fGLIndex < glStateCache::sMaxTextureUnits)
            {
                glStateCache::instance().enableAndActivateTexCoord( fGLIndex);
                glTexCoordPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            }
            break;
        case glRegister::kColor:
            glStateCache::instance().enableColor();
            glColorPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;
        case glRegister::kSecondaryColor:
            glStateCache::instance().enableSecondaryColor();
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glSecondaryColorPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;
        case glRegister::kVertexAttrib:
            glStateCache::instance().enableVertexAttrib( fGLIndex);
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glVertexAttribPointer( fGLIndex, dimension, GL_FLOAT, GL_FALSE, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            if(source.fSourceType == cgfxVertexAttribute::kPosition) {
                glStateCache::instance().enablePosition();
                glVertexPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            }
            break;
        default:
            return false;  
    }
    return true;
}
void cgfxVaryingParameter::null() const
{
    switch( fGLType)
    {
        case glRegister::kPosition:
            
            break;
        case glRegister::kNormal:
            glNormal3f( 0.0f, 0.0f, 1.0f);
            break;
        case glRegister::kTexCoord:
            glStateCache::instance().activeTexture( fGLIndex);
            glStateCache::glMultiTexCoord4fARB( GL_TEXTURE0 + fGLIndex, 0.0f, 0.0f, 0.0f, 0.0f );
            break;
        case glRegister::kColor:
            glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
            break;
        case glRegister::kSecondaryColor:
            if( glStateCache::glSecondaryColor3f) 
                glStateCache::glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
            break;
        case glRegister::kVertexAttrib:
            if( glStateCache::glVertexAttrib4f) 
                glStateCache::glVertexAttrib4f( fGLIndex, 0.0f, 0.0f, 0.0f, 0.0f);
            break;
        default:
            break;
    }
}
inline void cgfxVaryingParameter::addRecursive(
    CGparameter parameter,
    cgfxVaryingParameter**& nextParameter
)
{
    if( cgGetParameterVariability( parameter) == CG_VARYING)
    {
        if( cgGetParameterType( parameter) == CG_STRUCT)
        {
            CGparameter input = cgGetFirstStructParameter( parameter);
            while( input)
            {
                addRecursive( input, nextParameter);
                input = cgGetNextParameter( input);
            }
        }
        else if( cgIsParameterReferenced( parameter))
        {
            *nextParameter = new cgfxVaryingParameter( parameter);
            nextParameter = &(*nextParameter)->fNext;
        }
    }
}
cgfxPass::cgfxPass(
    CGpass                  pass,
    const cgfxProfile*      profile
)
 :  fPass( pass),
    fProgram( NULL),
    fParameters( NULL),
    fDefaultProfile("default", pass),
    fNext( NULL)
{
    if( pass)
    {
        fName = cgGetPassName( pass);
        CGstateassignment stateAssignment = cgGetFirstStateAssignment( pass);
        cgfxVaryingParameter** nextParameter = &fParameters;
        while( stateAssignment )
        {
            CGstate state = cgGetStateAssignmentState( stateAssignment);
            if( cgGetStateType( state) == CG_PROGRAM_TYPE && 
                    ( stricmp( cgGetStateName( state), "vertexProgram") == 0 ||
                      stricmp( cgGetStateName( state), "vertexShader") == 0))
            {
                fProgram = cgGetProgramStateAssignmentValue( stateAssignment);
                if( fProgram)
                {
                    CGparameter parameter = cgGetFirstParameter( fProgram, CG_PROGRAM);
                    while( parameter)
                    {
                        cgfxVaryingParameter::addRecursive( parameter, nextParameter);
                        parameter = cgGetNextParameter( parameter);
                    }
                }
            }
            setProfile(profile);
            stateAssignment = cgGetNextStateAssignment( stateAssignment);
        }
    }
}
cgfxPass::~cgfxPass()
{
    delete fNext;
    delete fParameters;
}
void cgfxPass::setupAttributes(cgfxRCPtr<cgfxVertexAttribute>& vertexAttributes) const
{
    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        parameter->setupAttributes( vertexAttributes, fProgram);
        parameter = parameter->fNext;
    }
}
void cgfxPass::setProfile(const cgfxProfile* profile) const
{
    
    
    if (profile == NULL) profile = &fDefaultProfile;
    CGprogram vp = cgGetPassProgram(fPass, CG_VERTEX_DOMAIN);
    if (vp != NULL &&
        cgGetProgramProfile(vp) != profile->getVertexProfile())
    {
        cgSetProgramProfile(vp, profile->getVertexProfile());
    }
    CGprogram gp = cgGetPassProgram(fPass, CG_GEOMETRY_DOMAIN);
    if (gp != NULL &&
        profile->getGeometryProfile() != CG_PROFILE_UNKNOWN &&
        cgGetProgramProfile(gp) != profile->getGeometryProfile())
    {
        cgSetProgramProfile(gp, profile->getGeometryProfile());
    }
    CGprogram fp = cgGetPassProgram(fPass, CG_FRAGMENT_DOMAIN);
    if (fp != NULL &&
        cgGetProgramProfile(fp) != profile->getFragmentProfile())
    {
        cgSetProgramProfile(fp, profile->getFragmentProfile());
    }
}
void cgfxPass::bind(
    const MDagPath& shape, cgfxStructureCache* cache,
 
    int vertexCount, const float * vertexArray,
    int normalsPerVertex, int normalCount, const float ** normalArrays,
    int colorCount, const float ** colorArrays,
    int texCoordCount, const float ** texCoordArrays
) const
{
    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        parameter->bind(shape, cache, 
                        vertexCount, vertexArray, 
                        normalsPerVertex, normalCount, normalArrays, 
                        colorCount, colorArrays, 
                        texCoordCount, texCoordArrays);
        parameter = parameter->fNext;
    }
}
void cgfxPass::bind(const sourceStreamInfo dataSources[], const int sourceCount) const
{
    TRACE_API_CALLS("cgfxPass::bind");
    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        
        if (parameter->fVertexAttribute.isNull() == false) {
            
            int index = 0;
            for(index = 0; index < sourceCount; ++index)
            {
                
                
                
                
                
                
                
                
                if (dataSources[index].fSourceName == parameter->fVertexAttribute->fSourceName )
                {
                    
                    break;
                }
            }   
            if(index < sourceCount)
            {
                
                if(!parameter->bind(dataSources[index])) {
                    
                    
                    
                    
                    parameter->null();
                    MString s = 
"cgfxShader : Couldn't bind source \"";
 
                    s += dataSources[index].fSourceName;
                    s += "\" for vertex attribute \"";
                    s += parameter->fVertexAttribute->fSourceName;;
                    s +="\".";
                }
            }
            else
            {
                
                
                
                
                
                
                
                
                
                parameter->null();
            }
        }
        
        parameter = parameter->fNext;
    }
    
}
cgfxTechnique::cgfxTechnique(
    CGtechnique         technique,
    const cgfxProfile*  profile
)
:   fTechnique( technique),
    fValid(false),
    fPasses(NULL),
    fNumPasses(0),
    fNext(NULL)
{
    if (technique)
    {
        fName = cgGetTechniqueName(technique);
        CGpass pass = cgGetFirstPass(technique);
        cgfxPass** nextPass = &fPasses;
        while (pass)
        {
            ++fNumPasses;
            *nextPass = new cgfxPass(pass, profile);
            nextPass = &(*nextPass)->fNext;
            pass = cgGetNextPass(pass);
        }
        fHasBlending = hasBlending(fTechnique);
        setProfile(profile);
    }
}
cgfxTechnique::~cgfxTechnique()
{
    delete fNext;
    delete fPasses;
    fNext = 0;
    fPasses = 0;
}
void cgfxTechnique::setProfile(const cgfxProfile* profile) const
{
    const cgfxProfile* supportedProfile = getSupportedProfile(profile);
    
    const cgfxPass* pass = fPasses;
    while (pass) {
        pass->setProfile(supportedProfile);
        pass = pass->fNext;
    }
    
    validate();
}
void cgfxTechnique::validate() const
{
    fValid = (cgValidateTechnique(fTechnique) == CG_TRUE);
    if (fValid) {
        fErrorString = "";
    }
    else {
        CGerror error = cgGetError();
        if (error != CG_NO_ERROR) {
            fErrorString = cgGetErrorString(cgGetError());
        }
        fErrorString += "\nCg compilation errors for technique \"";
        fErrorString += fName;
        fErrorString += "\":\n";
        fErrorString += cgGetLastListing(cgfxShaderNode::sCgContext);
        fErrorString += "\n";
    }
}
const cgfxProfile* cgfxTechnique::getSupportedProfile(const cgfxProfile* profile) const
{
    if (profile == NULL) {
        
        
        bool allPassProfilesSupported = true;
        const cgfxPass* pass = fPasses;
        while (pass) {
            if (!pass->fDefaultProfile.isSupported()) {
                allPassProfilesSupported = false;
                break;
            }
            pass = pass->fNext;
        }
        if (allPassProfilesSupported) {
            
            return NULL;
        }
        else {
            es += "The technique \"";
            es += fName;
            es += "\" specifies Cg profiles that are unsupported on this platform. "
                "The profile \"";
            es += cgfxProfile::getBestProfile()->getName();
            es += "\" will be used instead.";
        
            return cgfxProfile::getBestProfile();
        }
    }
    else {
        return profile;
    }
}
cgfxRCPtr<cgfxVertexAttribute> cgfxTechnique::getVertexAttributes() const
{
    cgfxRCPtr<cgfxVertexAttribute> vertexAttributes;
    
    const cgfxPass* pass = fPasses;
    while (pass) {
        pass->setupAttributes(vertexAttributes);
        pass = pass->fNext;
    }
    return vertexAttributes;
}
bool cgfxTechnique::hasBlending(CGtechnique technique)
{
    
    bool hasBlending = false;
    
    
    
    
    CGpass cgPass = cgGetFirstPass(technique);
    bool foundBlendEnabled = false;
    bool foundBlendFunc = false;
    if (cgPass)
    {
        CGstateassignment stateAssignment = cgGetFirstStateAssignment(cgPass);
        while ( stateAssignment )
        {
            CGstate state = cgGetStateAssignmentState( stateAssignment);
            const char *stateName = cgGetStateName(state);
            
            if (!foundBlendEnabled && stricmp( stateName, "BlendEnable") == 0)
            {
                int numValues = 0;
                const CGbool *values = cgGetBoolStateAssignmentValues(stateAssignment, &numValues);
                if (values && numValues)
                {
                    if (values[0])
                    {
                        foundBlendEnabled = true;
                    }
                }
            }
            
            else if (!foundBlendFunc && ( stricmp( stateName, "BlendFunc") == 0 ||
                                          stricmp( stateName, "BlendFuncSeparate") == 0 ))
            {
                int numValues = 0;
                const int * values = cgGetIntStateAssignmentValues(stateAssignment, &numValues);
                if (values)
                {
#if defined(CGFX_DEBUG_BLEND_FUNCTIONS)
                    
                    {
                        "GL_SRC_COLOR", 
                        "GL_ONE_MINUS_SRC_COLOR", 
                        "GL_SRC_ALPHA", 
                        "GL_ONE_MINUS_SRC_ALPHA", 
                        "GL_DST_ALPHA", 
                        "GL_ONE_MINUS_DST_ALPHA" 
                    };
#endif
                    for (int i=0; i<numValues; i++)
                    {
                        if ((values[i] >= GL_SRC_COLOR) && (values[i] <= GL_ONE_MINUS_DST_ALPHA))
                        {
#if defined(CGFX_DEBUG_BLEND_FUNCTIONS)
                            printf("Found blend function = %s, %s\n",
                            blendStringTable[ values[0]-GL_SRC_COLOR].asChar(),
                            blendStringTable[ values[1]-GL_SRC_COLOR].asChar());
#endif
                            foundBlendFunc = true;
                            break;
                        }
                    }
                }
            }
            hasBlending = foundBlendEnabled && foundBlendFunc;
            if (hasBlending)
                break;
            stateAssignment = cgGetNextStateAssignment( stateAssignment);
        }
    }
    return hasBlending;
}
namespace cgfxEffectInternal
{
    time_t fileTimeStamp(
const MString& fileName)
    {
        struct stat statBuf;
        if( stat(fileName.
asChar(), &statBuf) != 0 )
 
            return 0;
        return statBuf.st_mtime;
    }
    struct EffectKey
    {
        const cgfxProfile* profile;
        time_t timeStamp;
    };
    bool operator< (const EffectKey& lhs, const EffectKey& rhs)
    {
        return (lhs.profile <  rhs.profile) ||
               (lhs.profile == rhs.profile && ( (lhs.timeStamp <  rhs.timeStamp) ||
                                                (lhs.timeStamp == rhs.timeStamp && strcmp(lhs.fileName.asChar(), rhs.fileName.asChar()) < 0) ) );
    }
    
    
    
    
    class cgfxEffectCollection
    {
    public:
        cgfxEffect* find(
const MString& fileName, 
const cgfxProfile* profile) 
const;
        void add(cgfxEffect* effect, 
const MString& fileName, 
const cgfxProfile* profile);
 
        void remove(cgfxEffect* effect);
    private:
        typedef std::map< cgfxEffect*, EffectKey > Effect2KeyMap;
        Effect2KeyMap effect2KeyMap;
        typedef std::map< EffectKey, cgfxEffect* > Key2EffectMap;
        Key2EffectMap key2EffectMap;
    };
    cgfxEffect* cgfxEffectCollection::find(
const MString& fileName, 
const cgfxProfile* profile)
 const    {
        cgfxEffect* effect = NULL;
        EffectKey key = { profile, fileName, fileTimeStamp(fileName) } ;
        Key2EffectMap::const_iterator it = key2EffectMap.find(key);
        if(it != key2EffectMap.end())
        {
            effect = it->second;
        }
        return effect;
    }
    void cgfxEffectCollection::add(cgfxEffect* effect, 
const MString& fileName, 
const cgfxProfile* profile)
 
    {
        EffectKey key = { profile, fileName, fileTimeStamp(fileName) } ;
        key2EffectMap.insert( std::make_pair(key, effect) );
        effect2KeyMap.insert( std::make_pair(effect, key) );
    }
    void cgfxEffectCollection::remove(cgfxEffect* effect)
    {
        Effect2KeyMap::iterator it = effect2KeyMap.find(effect);
        if(it != effect2KeyMap.end())
        {
            key2EffectMap.erase( it->second );
            effect2KeyMap.erase( it );
        }
    }
    static cgfxEffectCollection gEffectsCollection;
}
cgfxRCPtr<const cgfxEffect> cgfxEffect::loadEffect(
const MString& fileName, 
const cgfxProfile* profile)
{
    cgfxEffect *effect = cgfxEffectInternal::gEffectsCollection.find(fileName, profile);
    if(effect == NULL)
    {
        effect = new cgfxEffect(fileName, profile);
        cgfxEffectInternal::gEffectsCollection.add(effect, fileName, profile);
    }
    return cgfxRCPtr<const cgfxEffect>(effect);
}
cgfxEffect::cgfxEffect(
const MString& fileName, 
const cgfxProfile* profile)
  : refcount(0),
    fEffect(NULL),
    fTechniques(NULL),
    fProfile(NULL)
{
    cgfxGetFxIncludePath( fileName, fileOptions );
    fileOptions.
append(
"-DMAYA_CGFX=1");
    if (cgfxProfile::getTexCoordOrientation() == cgfxProfile::TEXCOORD_OPENGL) {
        fileOptions.
append(
"-DMAYA_TEXCOORD_ORIENTATION_OPENGL=1");
    }
    else {
        fileOptions.
append(
"-DMAYA_TEXCOORD_ORIENTATION_DIRECTX=1");
    }
    
    const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_];
    unsigned int numOpts = fileOptions.
length();
 
    if (numOpts)
    {
        numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_-1) ?
            _CGFX_PLUGIN_MAX_COMPILER_ARGS_-1 : numOpts;
        for (unsigned int i=0; i<numOpts; i++)
            opts[i] = fileOptions[i].asChar();
        opts[numOpts] = NULL;
    }
    fEffect = cgCreateEffectFromFile(cgfxShaderNode::sCgContext, fileName.
asChar(), opts);
    if (fEffect)
    {
        CGtechnique technique = cgGetFirstTechnique(fEffect);
        cgfxTechnique** nextTechnique = const_cast<cgfxTechnique**>(&fTechniques);
        while (technique)
        {
            *nextTechnique = new cgfxTechnique(technique, profile);
            nextTechnique = &(*nextTechnique)->fNext;
            technique = cgGetNextTechnique(technique);
        }
        fProfile = profile;
    }
}
cgfxEffect::~cgfxEffect()
{
    
    cgfxEffectInternal::gEffectsCollection.remove(this);
    delete fTechniques;
    if (fEffect) {
        cgDestroyEffect(fEffect);
        fEffect = NULL;
    }
    
    fTechniques = NULL;
}
void cgfxEffect::release() const
{
    --refcount;
    if (refcount <= 0)
    {
        M_CHECK( refcount == 0 );
        delete this;
    }
}
    
const cgfxTechnique* cgfxEffect::getTechnique(
MString techniqueName)
 const 
{
    const cgfxTechnique* technique = fTechniques;
    while(technique)
    {
        if(technique->fName == techniqueName)
        {
            break;
        }
        technique = technique->fNext;
    }
    return technique;
}
void cgfxEffect::setProfile(const cgfxProfile* profile) const
{
    if (fProfile != profile) {
        fProfile = profile;
        const cgfxTechnique* technique = fTechniques;
        while(technique)
        {
            technique->setProfile(profile);
            technique = technique->fNext;
        }
    }
}
cgfxRCPtr<cgfxAttrDefList> cgfxEffect::attrsFromEffect() const
{
    if (!fEffect)
        return cgfxRCPtr<cgfxAttrDefList>();
    cgfxRCPtr<cgfxAttrDefList> list(new cgfxAttrDefList);
    CGparameter cgParameter = cgGetFirstEffectParameter(fEffect);
    int i = 0;
    while (cgParameter)
    {
        cgfxAttrDef* aDef = new cgfxAttrDef(cgParameter);
        list->add(aDef);
        cgParameter = cgGetNextParameter(cgParameter);
        ++i;
    } 
    return list;
}
cgfxStructureCache::cgfxStructureCache()
    : fEntries(NULL)
{}
cgfxStructureCache::~cgfxStructureCache()
{
    flush();
}
cgfxStructureCache::Entry::Entry(
)
  : fShape(shape.node()),
    fName(name),
    fData(new char[ stride * count])
{
}
cgfxStructureCache::Entry::~Entry()
{
    delete[] fData;
    fNext = 0;
}
char* cgfxStructureCache::findEntry(
const MDagPath& shape, 
const MString& name)
 
{
    Entry** entry = &fEntries;
    
    while (*entry)
    {
        if( !(*entry)->fShape.isValid() || !(*entry)->fShape.isAlive())
        {
            Entry* staleEntry = *entry;
            *entry = staleEntry->fNext;
            delete staleEntry;
        }
        else 
        {
            if( (*entry)->fShape == shape.
node() && (*entry)->fName == name)
 
            {
                return (*entry)->fData;
            }
            entry = &(*entry)->fNext;
        }
    }
    return NULL;
}
char* cgfxStructureCache::addEntry(
    int             stride,
    int             count
)
{
    Entry* cacheEntry = new Entry(shape, name, stride, count);
    cacheEntry->fNext = fEntries;
    fEntries = cacheEntry;
    return cacheEntry->fData;
}
void cgfxStructureCache::flush()
{
    delete fEntries;
    fEntries = NULL;
}
void cgfxStructureCache::flush(
const MDagPath& shape)
 
{
    Entry** cacheEntry = &fEntries;
    while( *cacheEntry)
    {
        if( !(*cacheEntry)->fShape.isValid() || !(*cacheEntry)->fShape.isAlive() ||
            (*cacheEntry)->fShape == shape.
node())
        {
            Entry* staleEntry = (*cacheEntry);
            *cacheEntry = staleEntry->fNext;
            staleEntry->fNext = NULL;
            delete staleEntry;
        }
        else
        {
            cacheEntry = &(*cacheEntry)->fNext;
        }
    }
}