// PolyMeshDuplicatorPlugin
// Initial code generated by XSI SDK Wizard
// Executed Sun Oct 18 14:47:47 UTC+0300 2009 by Administrator
// 
// 
// Tip: You need to compile the generated code before you can load the plug-in.
// After you compile the plug-in, you can load it by clicking Update All in the Plugin Manager.
#include <xsi_application.h>
#include <xsi_context.h>
#include <xsi_pluginregistrar.h>
#include <xsi_status.h>
#include <xsi_customoperator.h>
#include <xsi_operatorcontext.h>
#include <xsi_ppglayout.h>
#include <xsi_ppgeventcontext.h>
#include <xsi_selection.h>
#include <xsi_command.h>
#include <xsi_factory.h>
#include <xsi_primitive.h>
#include <xsi_kinematics.h>
#include <xsi_outputport.h>

#include <xsi_menu.h>
#include <xsi_menuitem.h>

#include <xsi_string.h>
#include <xsi_vector3.h>
#include <xsi_longarray.h>
#include <xsi_point.h>
#include <xsi_vertex.h>
#include <xsi_polygonnode.h>
#include <xsi_iceattribute.h>
#include <xsi_iceattributedataarray.h>

#include <xsi_x3dobject.h>
#include <xsi_filter.h>
#include <xsi_meshbuilder.h>
#include <xsi_polygonmesh.h>
#include <xsi_polygonnode.h>
#include <xsi_operator.h>
#include <xsi_port.h>
#include <xsi_null.h>

#include <xsi_progressbar.h>
#include <xsi_argument.h>
#include <xsi_comapihandler.h>
#include <xsi_uitoolkit.h>


#ifndef _DEBUG
    #include <omp.h>
#endif /* _DEBUG */

using namespace XSI; 

XSIPLUGINCALLBACK CStatus XSILoadPlugin( PluginRegistrar& in_reg )
{
	in_reg.PutAuthor(L"Nassos Yiannopoulos");
	in_reg.PutName(L"PolyMeshDuplicatorPlugin");
	in_reg.PutEmail(L"");
	in_reg.PutURL(L"");
	in_reg.PutVersion(1,0);
	in_reg.RegisterOperator(L"PolyMeshDuplicator");
	in_reg.RegisterCommand(L"PolyMeshSeparate", L"PolyMeshSeparate");
	//RegistrationInsertionPoint - do not remove this line

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus XSIUnloadPlugin( const PluginRegistrar& in_reg )
{
	CString strPluginName;
	strPluginName = in_reg.GetName();
#ifdef _DEBUG
	Application().LogMessage(strPluginName + L" has been unloaded.",siVerboseMsg);
#endif
	return CStatus::OK;
}



XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_Define( CRef& in_ctxt )
{
	Context ctxt( in_ctxt );
	CustomOperator oCustomOperator;
	Parameter oParam;
	CRef oPDef;
	Factory oFactory = Application().GetFactory();
	oCustomOperator = ctxt.GetSource();
	oPDef = oFactory.CreateParamDef(L"Mute",CValue::siBool,siPersistable | siAnimatable | siKeyable,L"",L"",false,CValue(),CValue(),CValue(),CValue());
	oCustomOperator.AddParameter(oPDef,oParam);

	oPDef = oFactory.CreateParamDef(L"Copies",CValue::siUInt4,siPersistable | siAnimatable | siKeyable,L"",L"",1,CValue(0),CValue(),CValue(1),CValue(1000));
	oCustomOperator.AddParameter(oPDef,oParam);

	//oCustomOperator.PutAlwaysEvaluate(true);
	oCustomOperator.PutAlwaysEvaluate(false);

#ifdef _DEBUG
	oCustomOperator.PutDebug(1);
#else
	oCustomOperator.PutDebug(0);
#endif

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_Init( CRef& in_ctxt )
{
	OperatorContext ctxt( in_ctxt );
#ifdef _DEBUG
	Application().LogMessage(L"PolyMeshDuplicator_Init called",siVerboseMsg);
#endif
	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_Term( CRef& in_ctxt )
{
	OperatorContext ctxt( in_ctxt );
#ifdef _DEBUG
	Application().LogMessage(L"PolyMeshDuplicator_Term called",siVerboseMsg);
#endif
	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_Update( CRef& in_ctxt )
{
	Application xsi;
	OperatorContext ctxt( in_ctxt );
	Primitive inPolyMeshPrim = ctxt.GetInputValue(0);
	Primitive outputPrim = ctxt.GetOutputTarget();
	PolygonMesh	inPolyMesh, output;
	CMeshBuilder meshBuilder;



	if (!inPolyMeshPrim.IsValid() || !outputPrim.IsValid())
		return CStatus::Fail;

	bool Mute = ctxt.GetParameterValue(L"Mute");
	LONG out_ncopies = ctxt.GetParameterValue(L"Copies");

	if (Mute)
	{
#ifdef _DEBUG
		xsi.LogMessage(L"PolyMesh Duplicator is mute",siVerboseMsg);
#endif
		return CStatus::OK;
	}


	// 
	// TODO: The Operator changes the XSI scene by changing the state of this variable
	// 

	inPolyMesh = inPolyMeshPrim.GetGeometry();
	output = outputPrim.GetGeometry();
	meshBuilder = output.GetMeshBuilder();

	//Application().LogMessage(L"inPolyMesh has " + CValue((LONG)inPolyMesh.GetNodes().GetCount()).GetAsText() + L" nodes",siVerboseMsg);

	MATH::CVector3Array	mesh_vertices, out_vertices;
	CLongArray			mesh_faceDescr, out_faceDescr;
	LONG				out_nverts, out_nfaces;	//, out_ncopies;
	LONG				in_nverts, in_nfaces;
	LONG				i, j, k, l, idx_offset;
	CStatus				getset_status;


	//xsi.LogMessage(inPolyMeshPrim.GetName() + L" has " + CString(mesh_vertices.GetCount()) + L" faces",siVerboseMsg);
	

	if (out_ncopies < 1)
	{
		// Nothing to do
#ifdef _DEBUG
		xsi.LogMessage(L"PolyMesh Duplicator : nothing to do, no copies requested",siVerboseMsg);
#endif

		out_vertices.Clear();
		out_faceDescr.Clear();
		output.Set(out_vertices, out_faceDescr);

		return CStatus::OK;
	}

	inPolyMesh.Get(mesh_vertices, mesh_faceDescr);
	try { 
		in_nverts = mesh_vertices.GetCount();	
	}
	catch(...){	xsi.LogMessage( L"Exception : mesh_vertices.GetCount()");	}

	out_nverts = out_ncopies*in_nverts;
#ifdef _DEBUG
    xsi.LogMessage( L"out_ncopies: " + CString(out_ncopies) );
    xsi.LogMessage( L"in_nverts: " + CString(in_nverts) );
    xsi.LogMessage( L"out_nverts: " + CString(out_nverts) );
#endif
	output.Get(out_vertices, out_faceDescr);
	if (out_vertices.GetCount()!=out_nverts)
		out_vertices.Resize(out_nverts);


	#pragma omp for
	for (j = 0; j < out_ncopies; ++j)
	{
		//xsi.LogMessage(L"Eval " + inPolyMeshPrim.GetName() + L" at time " + CString(shapeTime[j]) );
		//inPolyMeshPrim = inPolyMeshPrim.EvaluateAt(shapeTime[j]);	// EvaluateAt() doesn't seem to work

		// this is ignored inside an Op Update()
		//inPolyMesh = inPolyMeshPrim.GetGeometry(shapeTime[j], siConstructionModeSecondaryShape);
		// this is ignored inside an Op Update()
		//inPolyMesh = inPolyMeshPrim.GetGeometry(shapeTime[j]); 
		inPolyMesh = inPolyMeshPrim.GetGeometry();
		inPolyMesh.Get(mesh_vertices, mesh_faceDescr);

#ifdef _DEBUG
		if (mesh_vertices.GetCount()!=in_nverts)
		{
			xsi.LogMessage( L"ERROR : mesh_vertices["+CString(j)+L"].GetCount()!=in_nverts : " + CString(mesh_vertices.GetCount()) + L"!=" + CString(in_nverts));
		}
#endif

		for (k = j*in_nverts, i = 0; i < in_nverts; ++i, ++k)
		{
			out_vertices[k] = mesh_vertices[i];
		}	// for i end
	}	// for j end


	try {	
		in_nfaces = mesh_faceDescr.GetCount();	
	}
	catch(...) { xsi.LogMessage( L"Exception : mesh_faceDescr.GetCount()");	}
	out_nfaces = in_nfaces*out_ncopies;
#ifdef _DEBUG
    //xsi.LogMessage( L"in_nfaces: " + CString(in_nfaces) );
    //xsi.LogMessage( L"out_nfaces: " + CString(out_nfaces) );
#endif

	if (out_faceDescr.GetCount()!=out_nfaces)
	{
		out_faceDescr.Resize(out_nfaces);

		#pragma omp for
		for (j = 0; j < out_ncopies; ++j)
		{
			idx_offset = j*in_nverts;
			for (k = j*in_nfaces, i = 0; i < in_nfaces; )
			{
				out_faceDescr[k] = mesh_faceDescr[i];
				for (l = 1; l <= mesh_faceDescr[i]; ++l)
				{
					#ifdef	_DEBUG
					if (k+l >= out_nfaces)
						xsi.LogMessage( L"k+l >= out_nfaces : " + CString(k+l) + L" >= " + CString(out_nfaces) );
					#endif
					out_faceDescr[k+l] = mesh_faceDescr[i+l]+idx_offset;
				}
				k += l;
				i += l;
			}	// for i end
		}	// for j end

#ifdef _DEBUG
		//xsi.LogMessage( L"j < ncopies: " + CString(j) );
		//xsi.LogMessage( L"k: " + CString(k) );
		//xsi.LogMessage( L"i < in_nfaces : " + CString(i) );
		//xsi.LogMessage( L"out_faceDescr[] has " + CString(i) + L" items");

		//xsi.LogMessage(outputPrim.GetName() + L" has " + CString(out_nverts) + L" vertices",siVerboseMsg);
#endif

	}	// 	if (out_faceDescr.GetCount()!=out_nfaces) end

#ifdef _DEBUG
    //xsi.LogMessage( L"Calling 'output.Set()'" );
#endif
	try {	getset_status = output.Set(out_vertices, out_faceDescr);	}
	catch(...) {	xsi.LogMessage( L"Exception : output.Set()");	}
#ifdef _DEBUG
    xsi.LogMessage( L"PolyMesh.Set() returned with : " + getset_status.GetDescription() );
#endif

	//CRef output = ctxt.GetOutputTarget();
	return CStatus::OK;
}

//------------------------------------------------------------------
// Initialize the command named "PolyMeshSeparate"
//------------------------------------------------------------------
XSIPLUGINCALLBACK CStatus PolyMeshSeparate_Init( CRef& in_ctxt )
{
	// Construct a Context from the CRef
	Context ctxt( in_ctxt );

	// Get the Command object from the Context
	Command oCmd;
	oCmd = ctxt.GetSource();

	// Set some basic command properties
	oCmd.PutDescription(L"Converts a polygonMesh made by the Duplicator, to multiple objects.\n(Made for speed)");
	oCmd.PutTooltip(L"PolyMeshSeparate");

	// The command returns a value
	oCmd.EnableReturnValue(false);

	// Add an argument
	ArgumentArray oArgs;
	oArgs = oCmd.GetArguments();
	oArgs.Add(L"DuplicatedObject");

	return CStatus::OK;
}



// Given an X3DObject (e.g. a mesh or nurbs), fills in an array with all the operators connected to its
// primitive.  This function is suitable for re-use in any plug-in that wants to access construction history
void GetOperators( X3DObject in_ops, CString opName,  CRefArray & out_ops )
{
	Primitive primitive = in_ops.GetActivePrimitive() ;
	CComAPIHandler comPrimitive( primitive.GetRef() ) ;

	CComAPIHandler constructionHistory = comPrimitive.GetProperty( L"ConstructionHistory" ) ;

	// Currently there isn't a "Count" or "Item" property on the ConstructionHistory 
	// scripting object, so we use Filter to find all operators
	CValue valOperatorCollection  ;
	CValueArray args(3) ;
	args[1] = CValue( L"Operators" ) ;   // We want all operators (siOperatorFamily)
	constructionHistory.Call( L"Filter", valOperatorCollection, args ) ;


	// Now convert from a OperatorCollection object to a C++ CRefArray
	CComAPIHandler comOpColl = valOperatorCollection ;
	CValue cnt = comOpColl.GetProperty( L"Count" ) ;

	CRefArray ops;//( (LONG)cnt ) ;


	if (opName.IsEmpty())
	{
		CValue outOp;
		CValueArray itemsArgs;

		ops.Resize( (LONG)cnt );

		for ( LONG i=0 ; i<(LONG)cnt; i++ )
		{
			itemsArgs.Add( i );
			comOpColl.Invoke(L"Item", CComAPIHandler::PropertyGet, outOp, itemsArgs);

			ops[i] = outOp ;
			itemsArgs.Clear();
		}
	}
	else
	{
		CValue outOp;
		CValueArray itemsArgs;

		itemsArgs.Add( opName );
		comOpColl.Invoke(L"Item", CComAPIHandler::PropertyGet, outOp, itemsArgs);

		ops[0] = outOp ;
	}

	out_ops = ops ;
}



XSIPLUGINCALLBACK CStatus PolyMeshSeparate_Execute( CRef& in_ctxt )
{
	Application xsi;
	// Construct a Context from the CRef
	Context ctxt( in_ctxt );

	// Get the arguments from the Context
	CValueArray args = ctxt.GetAttribute(L"Arguments");
	CValue DuplicatedObjArg = args[0];

	X3DObject	dupObject = CRef(DuplicatedObjArg);
	X3DObject	oParent = dupObject.GetParent();
	Primitive	dupPrim = dupObject.GetActivePrimitive();
	Primitive	srcPrim;
	CRefArray	refArray;
	Operator	dupCustomOp;
	Port		customOpPort;
	bool		Mute;
	LONG		out_ncopies;

	PolygonMesh	dupPolyMesh, 
				srcPolyMesh;
	CMeshBuilder meshBuilder;
	X3DObject	out_temp;


	if (!dupPrim.IsValid())
		return CStatus::Fail;

	GetOperators(dupObject, L"PolyMeshDuplicator", refArray);
	dupCustomOp = refArray[0];


	Mute = dupCustomOp.GetParameterValue(L"Mute");
	out_ncopies = dupCustomOp.GetParameterValue(L"Copies");
	if (Mute || !out_ncopies)
	{
		xsi.LogMessage(L"PolyMesh Duplicator is mute, nothing to separate",siVerboseMsg);
		return CStatus::OK;
	}

	refArray = dupCustomOp.GetInputPorts();
	customOpPort = refArray[0];
	srcPrim = customOpPort.GetTarget();
	if (!srcPrim.IsValid())
	{
		xsi.LogMessage( L"Invalid input port of " + dupCustomOp.GetName(), siErrorMsg );
		return CStatus::BadVarType;
	}


	MATH::CVector3Array	dup_vertices, out_vertices;
	CLongArray			src_faceDescr;
	LONG				src_nverts;
	LONG				i, j, k;
	CStatus				status;
	UIToolkit kit = xsi.GetUIToolkit();
	ProgressBar bar = kit.GetProgressBar();


	//xsi.LogMessage(srcPolyMeshPrim.GetName() + L" has " + CString(mesh_vertices.GetCount()) + L" faces",siVerboseMsg);

	srcPolyMesh = srcPrim.GetGeometry();
	dupPolyMesh = dupPrim.GetGeometry();

	{	// begin block
		MATH::CVector3Array	src_vertices;	// we only need the src_vertices to get their count

		srcPolyMesh.Get(src_vertices, src_faceDescr);

		try { src_nverts = src_vertices.GetCount();		}
		catch(...){	xsi.LogMessage( L"Exception : src_vertices.GetCount()");	}
	}	// end block

	{	// begin block
		CLongArray	dup_faceDescr;	// needed only to call Get()
		
		dupPolyMesh.Get(dup_vertices, dup_faceDescr);
	}	// end block

	out_vertices.Resize(src_nverts);

	bar.PutMaximum( out_ncopies );
	bar.PutStep( 1 );
	bar.PutVisible( true );

	{
		Null	null_temp;

		status = oParent.AddNull(dupObject.GetName()+L"_parts", null_temp);
		oParent = null_temp;
	}

	k = 0;
	for (j = 0; j < out_ncopies && !bar.IsCancelPressed(); ++j)
	{
		bar.PutCaption( L"Separating remaining " + CString(out_ncopies - j) + L" parts..." );

		//#pragma omp for
		for (i = 0; i < src_nverts; ++i, ++k)
			out_vertices[i] = dup_vertices[k];

		status = oParent.AddPolygonMesh(out_vertices, src_faceDescr, L"part", out_temp);

		bar.Increment();
	}	// for j end

	oParent.PutLocalTranslation(dupObject.GetLocalTranslation());
	oParent.PutLocalRotation(dupObject.GetLocalRotation());
	oParent.PutLocalScaling(dupObject.GetLocalScaling());
	bar.PutVisible( false );

#ifdef _DEBUG
    //xsi.LogMessage( L"PolyMesh.Set() returned with : " + getset_status.GetDescription() );
#endif

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_DefineLayout( CRef& in_ctxt )
{
	Context ctxt( in_ctxt );
	PPGLayout oLayout;
	PPGItem oItem;
	oLayout = ctxt.GetSource();
	oLayout.Clear();
	oLayout.AddItem(L"Mute");
    oLayout.AddItem(L"Copies");
	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus PolyMeshDuplicator_PPGEvent( const CRef& in_ctxt )
{
	// This callback is called when events happen in the user interface
	// This is where you implement the "logic" code.
	
	// If the value of a parameter changes but the UI is not shown then this
	// code will not execute.  Also this code is not re-entrant, so any changes
	// to parameters inside this code will not result in further calls to this function
	
	Application app ;

	// The context object is used to determine exactly what happened
	// We don't use the same "PPG" object that is used from Script-based logic code 
	// but through the C++ API we can achieve exactly the same functionality.
	PPGEventContext ctxt( in_ctxt ) ;

	PPGEventContext::PPGEvent eventID = ctxt.GetEventID() ;

	if ( eventID == PPGEventContext::siOnInit )
	{
		// This event meant that the UI was just created.
		// It gives us a chance to set some parameter values.
		// We could even change the layout completely at this point.

		// For this event Source() of the event is the CustomOperator object

		CustomOperator prop = ctxt.GetSource() ;	

#ifdef _DEBUG
		app.LogMessage( L"OnInit called for " + prop.GetFullName(), siVerboseMsg ) ;
#endif

		/* If you regenerate the layout then call this:
		ctxt.PutAttribute(L"Refresh",true);
		*/
	}
	else if ( eventID == PPGEventContext::siOnClosed )
	{
		// This event meant that the UI was just closed by the user.

		// For this event Source() of the event is the CustomOperator object

		CustomOperator prop = ctxt.GetSource() ;	

#ifdef _DEBUG
		app.LogMessage( L"OnClosed called for " + prop.GetFullName(), siVerboseMsg ) ;
#endif
	}	
	else if ( eventID == PPGEventContext::siButtonClicked )
	{
		// If there are multiple buttons 
		// we can use this attribute to figure out which one was clicked.
		CValue buttonPressed = ctxt.GetAttribute( L"Button" ) ;	

#ifdef _DEBUG
		app.LogMessage( L"Button pressed: " + buttonPressed.GetAsText(), siVerboseMsg ) ;
#endif
	}
	else if ( eventID == PPGEventContext::siTabChange )
	{
		// We will be called when the PPG is first opened
		// and every time the tab changes

		// Retrieve the label of the tab that is now active
		CValue tabLabel = ctxt.GetAttribute( L"Tab" ) ;

#ifdef _DEBUG
		app.LogMessage( L"Tab changed to: " + tabLabel .GetAsText(), siVerboseMsg ) ;
#endif
	}
	else if ( eventID == PPGEventContext::siParameterChange )
	{
		// For this event the Source of the event is the parameter
		// itself
		Parameter changed = ctxt.GetSource() ;	
		CustomOperator prop = changed.GetParent() ;	
		CString   paramName = changed.GetScriptName() ; 

#ifdef _DEBUG
		app.LogMessage( L"Parameter Changed: " + paramName, siVerboseMsg ) ;
#endif
	}

	return CStatus::OK ;
}

