#include "cgfxShaderCommon.h"
#include "cgfxShaderCmd.h"
#include "cgfxShaderNode.h"
#include "cgfxProfile.h"
#include "cgfxFindImage.h"
#include <maya/MArgDatabase.h>
#include <maya/MCommandResult.h>
#include <maya/MDagPath.h>
#include <maya/MFeedbackLine.h>
#include <maya/MFnDagNode.h>
#include <maya/MGlobal.h>
#define kMaxTexCoordsFlag           "-mtc"
#define kMaxTexCoordsFlagLong       "-maxTexCoords"
#define kPluginPathFlag             "-pp"
#define kPluginPathFlagLong         "-pluginPath"
#define kFxFlag                     "-fx"
#define kFxFlagLong                 "-fxFile"
#define kFxPathFlag                 "-fxp"
#define kFxPathFlagLong             "-fxPath"
#define kFxTechniqueFlag            "-t"
#define kFxTechniqueFlagLong        "-technique"
#define kFxProfileFlag              "-pr"
#define kFxProfileFlagLong          "-profile"
#define kNameFlag                   "-n"
#define kNameFlagLong               "-name"
#define kListTechniquesFlag         "-lt"
#define kListTechniquesFlagLong     "-listTechniques"
#define kListProfilesFlag           "-lpr"
#define kListProfilesFlagLong       "-listProfiles"
#define kListParametersFlag         "-lp"
#define kListParametersFlagLong     "-listParameters"
#define kParameterFlag              "-p"
#define kParameterFlagLong          "-parameter"
#define kTexCoordSourceFlag         "-tcs"
#define kTexCoordSourceFlagLong     "-texCoordSource"
#if MAYA_API_VERSION >= 700
    #define kColorSourceFlag         "-cs"
    #define kColorSourceFlagLong     "-colorSource"
#endif
#define kEmptyUVFlag                "-euv"
#define kEmptyUVFlagLong            "-emptyUV"
#define kEmptyUVShapesFlag          "-eus"
#define kEmptyUVShapesFlagLong      "-emptyUVShapes"
#define kCaseInsensitiveFlag        "-ci"
#define kCaseInsensitiveFlagLong    "-caseInsensitive"
#define kDescriptionFlag            "-des"
#define kDescriptionFlagLong        "-description"
MString cgfxShaderCmd::sPluginPath;    
 
cgfxShaderCmd::doIt( 
const MArgList& args )
{
    try
    {
        stat = doCmd( args );
    }
    catch ( cgfxShaderCommon::InternalError* e )   
    {
        reportInternalError( __FILE__, (size_t)e );
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
    }
    return stat;
}                                      
cgfxShaderCmd::redoIt()
{
#ifdef KH_DEBUG
    ss += fArgString;
    ss += "\n";
    ::OutputDebugString( ss.
asChar() );
#endif
    try
    {
        
        stat = fNodeSelection.getDependNode( 0, oNode );
        M_CHECK( stat );
        M_CHECK( stat && fnNode.typeId() == cgfxShaderNode::sId );
        cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.userNode();
        M_CHECK( pNode );
        
        stat = redoCmd( oNode, fnNode, pNode );
    }
    catch ( cgfxShaderCommon::InternalError* e )   
    {
        reportInternalError( __FILE__, (size_t)e );
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
    }
#ifdef KH_DEBUG
    ss = "  .. redone\n";
    ::OutputDebugString( ss.
asChar() );
#endif
    return stat;
}                                      
cgfxShaderCmd::undoIt()
{
#ifdef KH_DEBUG
    ss += fArgString;
    ss += "\n";
    ::OutputDebugString( ss.
asChar() );
#endif
    try
    {
        stat = undoCmd();
    }
    catch ( cgfxShaderCommon::InternalError* e )   
    {
        reportInternalError( __FILE__, (size_t)e );
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
    }
#ifdef KH_DEBUG
    ss = "  .. undone\n";
    ::OutputDebugString( ss.
asChar() );
#endif
    return stat;
}                                      
cgfxShaderCmd::doCmd(
const MArgList& args)
{
    
    
    
#if defined(_WIN32) && defined(CGFX_DEBUG_MEMORY)
    if (tmpFlag == -1)
    {
        tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
        
        
        tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
        
        
        tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
        _CrtSetDbgFlag( tmpFlag );
    }
#endif 
    status = parseArgs(args, selList);
    if (!status)
    {
        return status;
    }
    
    
    
    
    
    if ( fPluginPath )
    {
        setResult( sPluginPath );
    }
    
    
    
    
    
    
    
    
    
    if ( fListProfiles )
    {
        setResult( cgfxProfile::getProfileList() );
        return status;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    if ( fMaxTexCoords )    
    {
        GLint     mtc = 0;
        if ( status &&
        {
            glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &mtc );
            GLint mic = 0;
            glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &mic );
            if (mic < mtc)
                mtc = mic;
            if ( mtc < 1 )
                mtc = 1;
            else if ( mtc > CGFXSHADERNODE_GL_TEXTURE_MAX )
                mtc = CGFXSHADERNODE_GL_TEXTURE_MAX;
            
        }
        setResult( (int)mtc );
    }
    
    cgfxShaderNode*   pNode = NULL;
    if ( fIsEdit || fIsQuery )
    {
        
        
        
        
        {
            return status;
        }
        
        
        
        fNodeName = tmpList[0];
        if ( fNodeName.length() ) 
        {
            sWho += " \"";
            sWho += fNodeName;
            sWho += "\"";
        }
        if (!status)
        {
            return status;
        }
        if (!status)
        {
            sFeedback = sWho;
            sFeedback += " is not a cgfxShader node.";
            return status;
        }
        if (fnNode.
typeId() != cgfxShaderNode::sId)
 
        {
            sFeedback = sWho;
            sFeedback += " is not a cgfxShader node.";
            return status;
        }
        pNode = (cgfxShaderNode*)fnNode.
userNode();
        if (!pNode)
        {
            sFeedback = sWho;
            sFeedback += " is not cgfxShader node.";
            return status;
        }
    }
    if ( fIsQuery ) {
        
        
        
        if ( fFxFile )
        {
            MString path = pNode->shaderFxFile();
 
            setResult( path );
        }
        
        
        
        
        if ( fFxPath )
        {
            MString path = cgfxFindFile(pNode->shaderFxFile());
 
            setResult( path );
        }
        
        
        if ( fTechnique )
        {
            MString path = pNode->getTechnique();
 
            setResult( path );
        }
        
        
        if ( fProfile )
        {
            MString path = pNode->getProfile();
 
            setResult( path );
        }
        
        
        
        
        
        
        
        
        
        
        
        
        if ( fListTechniques )
        {
            setResult( pNode->getTechniqueList() );
            return status;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        if ( fListParameters )
        {
            cgfxRCPtr<cgfxAttrDefList> list = cgfxAttrDef::attrsFromNode( oNode );
            for ( cgfxAttrDefList::iterator it = list; it; ++it )
            {
                cgfxAttrDef* aDef = *it;
                if ( fDescription )
                {
                    sResult = aDef->fName.
length() ? aDef->fName : 
" ";
                    sResult += "\t";
                    sTemp = aDef->typeName();
                    sResult += sTemp.
length() ? sTemp : 
" ";
                    sResult += "\t";               
                    sResult += aDef->fSemantic.
length() ? aDef->fSemantic : 
" ";
                    sResult += "\t";
                    sResult += aDef->fDescription.
length() ? aDef->fDescription : 
" ";
                    sResult += "\t";               
                    const char* suffix = aDef->getExtraAttrSuffix();
                    sResult += suffix ? suffix : " ";
                }
                else
                    sResult = aDef->fName;
            }
            setResult( saResult );
            return status;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if ( fParameterName.length() > 0 )
        {
            cgfxRCPtr<cgfxAttrDefList> list = cgfxAttrDef::attrsFromNode( oNode );
            cgfxAttrDefList::iterator it; 
            if ( fCaseInsensitive )
                it = list->findInsensitive( fParameterName );
            else
                it = list->find( fParameterName );
            if ( fDescription )
            {
                if ( it )
                {
                    cgfxAttrDef* aDef = *it;
                    saResult.
append( aDef->fName );
                    saResult.
append( aDef->typeName() );
                    saResult.
append( aDef->fSemantic );
                    saResult.
append( aDef->fDescription );
                    const char* suffix = aDef->getExtraAttrSuffix();
                    saResult.
append( suffix ? suffix : 
"" );
                }
                setResult( saResult );
            }
            else
            {
                if ( it )
                    sResult = (*it)->typeName();
                setResult( sResult );
            }
            return status;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        if ( fEmptyUV )
        {
            setResult( pNode->getEmptyUVSets() );
        }
        
        
        
        
        
        
        
        if ( fEmptyUVShapes )
        {
            const MObjectArray& oaShapes = pNode->getEmptyUVSetShapes();
 
            for ( 
unsigned iShape = 0; iShape < oaShapes.
length(); ++iShape )
 
            {
            }
            setResult( saResult );
        }
        
        
        
        
        if ( fTexCoordSource )
        {
            setResult( pNode->getTexCoordSource() );
        }
#if MAYA_API_VERSION >= 700
        
        
        
        
        if ( fColorSource )
        {
            setResult( pNode->getColorSource() );
        }
#endif
        
    }
    
    
    if (!fFxFile && pNode)
        fNewFxFile = pNode->shaderFxFile();
    
    
    
    
    
    
    if (!fTechnique && pNode)
        fNewTechnique = pNode->getTechnique();
    
    
    if (!fProfile && pNode)
        fNewProfile = pNode->getProfile();
    
    
    
    if (fFxFile)
    {
        
        
        MString file = cgfxFindFile(fNewFxFile);
 
        MString projectFile = cgfxFindFile(fNewFxFile, 
true);
 
        
        fNewEffect = cgfxEffect::loadEffect(file, cgfxProfile::getProfile(fNewProfile));
        if (fNewEffect->isValid())
        {           
            
            
            if ( !status ) return status;
            fNewFxFile = projectFile;
            
            
            
            
            
            {
                MString es = 
"There is no active view to bind " + sWho + 
" to.";
 
            }
        }
        
        if (fNewEffect->isValid())
        {
            sFeedback = sWho;
            sFeedback += " loaded effect \"";
            sFeedback += file;
            sFeedback += "\"";
        }
        else
        {
            sFeedback = sWho;
            sFeedback += " unable to load effect \"";
            sFeedback += file.
length() ? file : fNewFxFile;
            sFeedback += "\"";
        }
    }
    
    
    
    
    if ( !fIsEdit )
    {
        
        oNode = fDagMod->createNode(cgfxShaderNode::sId, &status);
        M_CHECK( status );
        if ( fNodeName.length() > 0 )
        {
            status = fDagMod->renameNode(oNode, fNodeName);
            M_CHECK( status );
        }
        M_CHECK( status && fnNode.
typeId() == cgfxShaderNode::sId );
        pNode = (cgfxShaderNode*)fnNode.
userNode();
        M_CHECK( pNode );
        
        
        M_CHECK( status );
    }
    if (fFxFile) {
        
        fOldFxFile    = pNode->shaderFxFile();
        fOldEffect = pNode->effect();   
        cgfxShaderNode::NodeList nodes;
        
        
        
        
        getNodesToUpdate(fOldEffect, pNode, nodes);
        cgfxShaderNode::NodeList::const_iterator it = nodes.begin();
        cgfxShaderNode::NodeList::const_iterator itEnd = nodes.end();
        for(; it != itEnd; ++it)
        {
            cgfxShaderNode* node = *it;
            cgfxRCPtr<cgfxAttrDefList> &oldAttrDefList = fOldAttrDefList[node];
            cgfxRCPtr<cgfxAttrDefList> &newAttrDefList = fNewAttrDefList[node];
            node->getAttributeList( oldAttributeList );
            oldAttrDefList = node->attrDefList(); 
            
            
            
            
            
            
            
            
            
            
            
            cgfxAttrDef::updateNode( fNewEffect,             
                                     node,                   
                                     fDagMod,                
                                     newAttrDefList,         
                                     newAttributeList );     
        }
    }
    
    status = fNodeSelection.add( oNode );
    M_CHECK( status );
    
    fOldTechnique = pNode->getTechnique();
    fOldProfile   = pNode->getProfile();
    
    
    
    
    
    
    
    return redoCmd( oNode, fnNode, pNode );
}                                      
cgfxShaderCmd::redoCmd( 
MObject&           oNode,
                        cgfxShaderNode*    pNode )
{
    if (!fOldAttrDefList.empty()) {
        
        
        NodeAttrDefList::iterator it = fOldAttrDefList.begin();
        NodeAttrDefList::iterator itEnd = fOldAttrDefList.end();
        for(; it != itEnd; ++it)
        {
            cgfxRCPtr<cgfxAttrDefList> &attrDefList = it->second;
            if(!attrDefList.isNull())
                attrDefList->releaseTextures();
        }
    }
    if (fFxFile) {
        
        
        
        pNode->setAttrDefList( cgfxRCPtr<cgfxAttrDefList>() );
        cgfxRCPtr<cgfxAttrDefList> currNodeAttrDefList;
        
        status = fDagMod->doIt();
        M_CHECK( status );
        cgfxShaderNode::NodeList nodes;
        
        
        
        
        getNodesToUpdate(fOldEffect, pNode, nodes);
        cgfxShaderNode::NodeList::const_iterator it = nodes.begin();
        cgfxShaderNode::NodeList::const_iterator itEnd = nodes.end();
        for(; it != itEnd; ++it)
        {
            cgfxShaderNode* node = *it;
            cgfxRCPtr<cgfxAttrDefList> &attrDefList = fNewAttrDefList[node];
            if(node == pNode)
                currNodeAttrDefList = attrDefList;
            node->setAttributeList(attributeList);
            node->setAttrDefList(attrDefList);
            
            node->setShaderFxFileChanged(true);
            node->setEffect(fNewEffect);
        }
        cgfxAttrDef::initializeAttributes( oNode, currNodeAttrDefList, false, fDagMod);
    }
    else {
        
        status = fDagMod->doIt();
        M_CHECK( status );
    }
    if (fTechnique) {
    }
    if (fProfile) {
    }
    if ( !fIsEdit ) 
    {
        
        
        fNodeName = fnNode.
name();
        
        
        
        
        
    }
}                                      
cgfxShaderCmd::undoCmd()
{
    
    
    status = fNodeSelection.getDependNode(0, oNode);
    M_CHECK( status );
    M_CHECK( status && fnNode.
typeId() == cgfxShaderNode::sId );
    cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.
userNode();
    M_CHECK( pNode );
    if (!fNewAttrDefList.empty()) {
        
        
        NodeAttrDefList::iterator it = fNewAttrDefList.begin();
        NodeAttrDefList::iterator itEnd = fNewAttrDefList.end();
        for(; it != itEnd; ++it)
        {
            cgfxRCPtr<cgfxAttrDefList> &attrDefList = it->second;
            if(!attrDefList.isNull())
                attrDefList->releaseTextures();
        }
    }
    if (fFxFile) {
        
        
        
        pNode->setAttrDefList( cgfxRCPtr<cgfxAttrDefList>() );
    }
    
    
    
    status = fDagMod->undoIt();
    M_CHECK( status );
    if ( fIsEdit )
    {
        if (fFxFile) {
            cgfxRCPtr<cgfxAttrDefList> currNodeAttrDefList;
            cgfxShaderNode::NodeList nodes;
            
            
            
            
            getNodesToUpdate(fNewEffect, pNode, nodes);
            cgfxShaderNode::NodeList::const_iterator it = nodes.begin();
            cgfxShaderNode::NodeList::const_iterator itEnd = nodes.end();
            for(; it != itEnd; ++it)
            {
                cgfxShaderNode* node = *it;
                cgfxRCPtr<cgfxAttrDefList> &attrDefList = fOldAttrDefList[node];
                if(node == pNode)
                    currNodeAttrDefList = attrDefList;
                node->setAttributeList(attributeList);
                node->setAttrDefList(attrDefList);
                
                node->setShaderFxFileChanged(true);
                node->setEffect(fOldEffect);
            }
            cgfxAttrDef::initializeAttributes( oNode, currNodeAttrDefList, true, fDagMod);
        }
        if (fTechnique) {
        }
        if (fProfile) {
        }
    }
    else 
    {
    }
}                                      
{
    syntax.
addFlag( kPluginPathFlag, kPluginPathFlagLong );
    syntax.
addFlag( kMaxTexCoordsFlag, kMaxTexCoordsFlagLong );
    syntax.
addFlag( kListTechniquesFlag, kListTechniquesFlagLong );
    syntax.
addFlag( kListProfilesFlag, kListProfilesFlagLong );
    syntax.
addFlag(kListParametersFlag, kListParametersFlagLong);
    syntax.
addFlag( kEmptyUVFlag, kEmptyUVFlagLong );
    syntax.
addFlag( kEmptyUVShapesFlag, kEmptyUVShapesFlagLong );
    syntax.
addFlag( kTexCoordSourceFlag, kTexCoordSourceFlagLong );
#if MAYA_API_VERSION >= 700
    syntax.
addFlag( kColorSourceFlag, kColorSourceFlagLong );
#endif
    syntax.
addFlag( kCaseInsensitiveFlag, kCaseInsensitiveFlagLong );
    syntax.
addFlag( kDescriptionFlag, kDescriptionFlagLong );
    
    
    
    
    
    return syntax;
}
void* cgfxShaderCmd::creator()
{
    return new cgfxShaderCmd();
}
cgfxShaderCmd::cgfxShaderCmd()
:   fIsEdit( false )
,   fIsQuery( false )
,   fMaxTexCoords( false )
,   fFxFile( false )
,   fFxPath( false )
,   fTechnique( false )
,   fProfile( false )
,   fPluginPath( false )
,   fEmptyUV( false )
,   fEmptyUVShapes( false )
,   fListParameters(false)
,   fListTechniques( false )
,   fListProfiles( false )
,   fTexCoordSource( false )
#if MAYA_API_VERSION >= 700
,   fColorSource( false )
#endif
,   fCaseInsensitive( false )
,   fDescription( false )
,   fOldEffect(0)
,   fNewEffect(0)
,   fDagMod(0)
{  }
cgfxShaderCmd::~cgfxShaderCmd()
{
    try
    {
#ifdef KH_DEBUG
        if ( !fIsQuery )
        {
            ss += fArgString;
            ss += "\n";
            ::OutputDebugString( ss.
asChar() );
        }
#endif
        fNewAttrDefList.clear();
        delete fDagMod;
    }
    catch ( cgfxShaderCommon::InternalError* e )   
    {
        reportInternalError( __FILE__, (size_t)e );
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
    }
}
bool cgfxShaderCmd::isUndoable() const
{
    return !fIsQuery;
}
{
    for ( 
unsigned iArg = 0; iArg < args.
length(); ++iArg )
 
    {
        if ( iArg > 0 )
            fArgString += " ";
    }
#ifdef KH_DEBUG
    ss += fArgString;
    ss += "\n";
    ::OutputDebugString( ss.
asChar() );
#endif
    if ( !status )
        return status;
    bool bCgfxShaderNodeRequired = true;
    fIsEdit = argData.isEdit();
    fIsQuery = argData.isQuery();
    if ( argData.isFlagSet( kMaxTexCoordsFlag ) )       
    {
        bCgfxShaderNodeRequired = false;
        fMaxTexCoords = true;    
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kPluginPathFlag ) )       
    {
        bCgfxShaderNodeRequired = false;
        fPluginPath = true;    
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kEmptyUVFlag ) )       
    {
        fEmptyUV = true;    
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kEmptyUVShapesFlag ) )       
    {
        fEmptyUVShapes = true;    
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kTexCoordSourceFlag ) )       
    {
        fTexCoordSource = true;    
        fIsQuery = true;
    }
#if MAYA_API_VERSION >= 700
    if ( argData.isFlagSet( kColorSourceFlag ) )       
    {
        fColorSource = true;    
        fIsQuery = true;
    }
#endif
    if (argData.isFlagSet(kFxFlag))
    {
        fFxFile = true;
        if (!fIsQuery) {
            argData.getFlagArgument(kFxFlag, 0, fNewFxFile);
        }
    }
    if (argData.isFlagSet(kFxPathFlag))
    {
        fFxPath = true;
        fIsQuery = true;
    }
    if (argData.isFlagSet(kFxTechniqueFlag))
    {
        fTechnique = true;
        if (!fIsQuery) {
            argData.getFlagArgument( kFxTechniqueFlag, 0, fNewTechnique );
        }
    }
    if (argData.isFlagSet(kFxProfileFlag))
    {
        fProfile = true;
        if (!fIsQuery) {
            argData.getFlagArgument( kFxProfileFlag, 0, fNewProfile );
        }
    }
    if (argData.isFlagSet(kNameFlag))
    {
        argData.getFlagArgument(kNameFlag, 0, fNodeName);
    }
    if (argData.isFlagSet(kListParametersFlag))
    {
        fListParameters = true;
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kListTechniquesFlag ) )
    {
        fListTechniques = true;
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kListProfilesFlag ) )
    {
        bCgfxShaderNodeRequired = false;
        fListProfiles = true;
        fIsQuery = true;
    }
    if (argData.isFlagSet(kParameterFlag))
    {
        argData.getFlagArgument(kParameterFlag, 0, fParameterName);
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kCaseInsensitiveFlag ) )       
    {
        fCaseInsensitive = true;    
        fIsQuery = true;
    }
    if ( argData.isFlagSet( kDescriptionFlag ) )       
    {
        fDescription = true;    
        fIsQuery = true;
    }
    
    if ( fIsQuery &&
        fIsEdit )
    {
        MString es = 
"cgfxShader: invalid use of -e/-edit flag";
 
    }
    
    if ( bCgfxShaderNodeRequired )
    {
        argData.getObjects(selList);
        {
            sMsg = "Exactly one node must be specified or selected for command:  cgfxShader ";
            sMsg += fArgString;
        }
    }
    return status;
}
void cgfxShaderCmd::getNodesToUpdate(const cgfxRCPtr<const cgfxEffect>& effect, cgfxShaderNode* currNode, cgfxShaderNode::NodeList& nodes) const
{
    
    bool isReload = (fNewFxFile == fOldFxFile);
    if(isReload)
    {
        
        cgfxShaderNode::getNodesUsingEffect(effect, nodes);
    }
    else
    {
        
        nodes.insert(currNode);
    }
}
void
cgfxShaderCmd::reportInternalError( const char* sFile, size_t errcode )
{
    MString es = 
"cgfxShader internal error ";
 
    es += (int)errcode;
    if ( this &&
        fArgString.length() > 0 )
    {
        es += " with args: ";
        es += fArgString;
    }
#ifdef _WINDOWS
    ::OutputDebugString( es.
asChar() );
    ::OutputDebugString( "\n" );
#endif
}