//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#include <slopeShaderBehavior.h>
#include <maya/MGlobal.h>
#include <maya/MPlugArray.h>
#include <maya/MFnDagNode.h>
#include <maya/MObjectArray.h>

slopeShaderBehavior::slopeShaderBehavior()
//
//	Description:
//		Constructor
//
{
}

slopeShaderBehavior::~slopeShaderBehavior()
//
//	Description:
//		Destructor
//
{
}

void *slopeShaderBehavior::creator()
//
//	Description:
//		Returns a new instance of this class
//
{
	return new slopeShaderBehavior;
}

bool slopeShaderBehavior::shouldBeUsedFor( MObject &sourceNode, MObject &destinationNode,
										   MPlug &sourcePlug, MPlug &destinationPlug)
//
//	Description:
//		Overloaded function from MPxDragAndDropBehavior
//	this method will return true if it is going to handle the connection
//	between the two nodes given.
//
{
	bool result = false;

	if(sourceNode.hasFn(MFn::kLambert))
	{
		//if the source node was a lambert
		//than we will check the downstream connections to see 
		//if a slope shader is assigned to it.
		//
		MObject shaderNode;
		MFnDependencyNode src(sourceNode);
		MPlugArray connections;

		src.getConnections(connections);
		for(unsigned i = 0; i < connections.length(); i++)
		{
			//check the incoming connections to this plug
			//
			MPlugArray connectedPlugs;
			connections[i].connectedTo(connectedPlugs, true, false);
			for(unsigned j = 0; j < connectedPlugs.length(); j++)
			{
				//if the incoming node is a slope shader than 
				//set shaderNode equal to it and break out of the inner 
				//loop
				//
				if(MFnDependencyNode(connectedPlugs[j].node()).typeName() == "slopeShader")
				{
					shaderNode = connectedPlugs[j].node();
					break;
				}
			}

			//if the shaderNode is not null
			//than we have found a slopeShader
			//
			if(!shaderNode.isNull())
			{
				//if the destination node is a mesh than we will take
				//care of this connection so set the result to true
				//and break out of the outer loop
				//
				if(destinationNode.hasFn(MFn::kMesh))
					result = true;

				break;
			}
		}
	}
	if(MFnDependencyNode(sourceNode).typeName() == "slopeShader")
	//if the sourceNode is a slope shader than check what we
	//are dropping on to
	//
	{
		if(destinationNode.hasFn(MFn::kLambert))
			result = true;
		else if(destinationNode.hasFn(MFn::kMesh))
			result = true;
	}	

	return result;
}

MStatus slopeShaderBehavior::connectNodeToNode( MObject &sourceNode, MObject &destinationNode, bool force )
//
//	Description:
//		Overloaded function from MPxDragAndDropBehavior
//	this method will handle the connection between the slopeShader and the shader it is
//	assigned to as well as any meshes that it is assigned to. 
//
{
	MStatus result = MS::kFailure;
	MFnDependencyNode src(sourceNode);

	//if we are dragging from a lambert
	//we want to check what we are dragging
	//onto.
	if(sourceNode.hasFn(MFn::kLambert))
	{
		MObject shaderNode;
		MPlugArray connections;
		MObjectArray shaderNodes;
		shaderNodes.clear();

		//if the source node was a lambert
		//than we will check the downstream connections to see 
		//if a slope shader is assigned to it.
		//
		src.getConnections(connections);
		unsigned i;
		for(i = 0; i < connections.length(); i++)
		{
			//check the incoming connections to this plug
			//
			MPlugArray connectedPlugs;
			connections[i].connectedTo(connectedPlugs, true, false);
			for(unsigned j = 0; j < connectedPlugs.length(); j++)
			{
				//if the incoming node is a slope shader than 
				//append the node to the shaderNodes array
				//
				MObject currentnode = connectedPlugs[j].node();
				if(MFnDependencyNode(currentnode).typeName() == "slopeShader")
				{
					shaderNodes.append(currentnode);
				}
			}
		}

		//if we found a shading node
		//than check the destination node 
		//type to see if it is a mesh
		//
		if(shaderNodes.length() > 0)
		{
			MFnDependencyNode dest(destinationNode);
			if(destinationNode.hasFn(MFn::kMesh))
			{
				//if the node is a mesh than for each slopeShader
				//connect the worldMesh attribute to the dirtyShaderPlug
				//attribute to force an evaluation of the node when the mesh
				//changes
				//
				for(i = 0; i < shaderNodes.length(); i++)
				{
					MPlug srcPlug = dest.findPlug("worldMesh");
					MPlug destPlug = MFnDependencyNode(shaderNodes[i]).findPlug("dirtyShaderPlug");

					if(!srcPlug.isNull() && !destPlug.isNull())
					{
						MString cmd = "connectAttr -na ";
						cmd += srcPlug.name() + " ";
						cmd += destPlug.name();
						MGlobal::executeCommand(cmd);
					}
				}

				//get the shading engine so we can assign the shader
				//to the mesh after doing the connection
				//
				MObject shadingEngine = findShadingEngine(sourceNode);

				//if there is a valid shading engine than make
				//the connection
				//
				if(!shadingEngine.isNull())
				{
					MString cmd = "sets -edit -forceElement ";
					cmd += MFnDependencyNode(shadingEngine).name() + " ";
					cmd += MFnDagNode(destinationNode).partialPathName();
					result = MGlobal::executeCommand(cmd);
				}
			}
		}
	}
	else if(src.typeName() == "slopeShader")
	//if we are dragging from a slope shader
	//than we want to see what we are dragging onto
	//
	{
		if(destinationNode.hasFn(MFn::kMesh))
		{
			//if the user is dragging onto a mesh
			//than make the connection from the worldMesh
			//to the dirtyShader plug on the slopeShader
			//
			MFnDependencyNode dest(destinationNode);
			MPlug srcPlug = dest.findPlug("worldMesh");
			MPlug destPlug = src.findPlug("dirtyShaderPlug");
			if(!srcPlug.isNull() && !destPlug.isNull())
			{
				MString cmd = "connectAttr -na ";
				cmd += srcPlug.name() + " ";
				cmd += destPlug.name();
				result = MGlobal::executeCommand(cmd);
			}
		}
	}

	return result;
}

MStatus slopeShaderBehavior::connectNodeToAttr( MObject &sourceNode, MPlug &destinationPlug, bool force )
//
//	Description:
//		Overloaded function from MPxDragAndDropBehavior
//	this method will assign the correct output from the slope shader 
//	onto the given attribute.
//
{
	MStatus result = MS::kFailure;
	MFnDependencyNode src(sourceNode);

	//if we are dragging from a slopeShader
	//to a shader than connect the outColor
	//plug to the plug being passed in
	//
	if(destinationPlug.node().hasFn(MFn::kLambert)) {
		if(src.typeName() == "slopeShader")
		{
			MPlug srcPlug = src.findPlug("outColor");
			if(!srcPlug.isNull() && !destinationPlug.isNull())
			{
				MString cmd = "connectAttr ";
				cmd += srcPlug.name() + " ";
				cmd += destinationPlug.name();
				result = MGlobal::executeCommand(cmd);
			}
		}
	} else {
		//in all of the other cases we do not need the plug just the node
		//that it is on
		//
        MObject destinationNode = destinationPlug.node();
		result = connectNodeToNode(sourceNode, destinationNode, force);
	}
        
	return result;
}

MObject slopeShaderBehavior::findShadingEngine(MObject &node)
//
//	Description:
//		Given the material MObject this method will
//	return the shading group that it is assigned to.
//	if there is no shading group associated with
//	the material than a null MObject is apssed back.
//
{
	MFnDependencyNode nodeFn(node);
	MPlug srcPlug = nodeFn.findPlug("outColor");
	MPlugArray nodeConnections;
	srcPlug.connectedTo(nodeConnections, false, true);
	//loop through the connections
	//and find the shading engine node that
	//it is connected to
	//
	for(unsigned i = 0; i < nodeConnections.length(); i++)
	{
		if(nodeConnections[i].node().hasFn(MFn::kShadingEngine))
			return nodeConnections[i].node();
	}

	//no shading engine associated so return a
	//null MObject
	//
	return MObject();
}

