//*****************************************************************************
/*!
   \file xsi_clusterproperty.h
   \brief ClusterProperty and CClusterPropertyElementArray class declarations.

   © Copyright 1998-2002 Avid Technology, Inc. and its licensors. All rights
   reserved. This file contains confidential and proprietary information of
   Avid Technology, Inc., and is subject to the terms of the SOFTIMAGE|XSI
   end user license agreement (or EULA).
*/
//*****************************************************************************

#if (_MSC_VER > 1000) || defined(SGI_COMPILER)
#pragma once
#endif

#ifndef __XSICLUSTERPROPERTY_H__
#define __XSICLUSTERPROPERTY_H__

#include <xsi_property.h>
#include <xsi_status.h>
#include <xsi_doublearray.h>
#include <xsi_longarray.h>
#include <xsi_bitarray.h>
#include <xsi_floatarray.h>

namespace XSI {

class CClusterPropertyElementArray;


//*****************************************************************************
/*! \class ClusterProperty xsi_clusterproperty.h
   \brief Represents a property of a cluster.

	Values contained by the cluster property can be accessed with ClusterProperty::GetElements or
	ClusterProperty::GetValues.

	ClusterProperty::GetElements returns a CClusterPropertyElementArray object. This object can be
	used to access the entire value array with CClusterPropertyElementArray::GetArray or individual
	values via CClusterPropertyElementArray::GetItems. GetIems returns an array of double values.

	ClusterProperty::GetValues is typically used to access the data on ClusterProperty objects
	generated by the CGeometryAccessor object. This method is more efficient than the one above as
	it doesn't need any intermediate objects to access the values. All values are returned in one
	single array of the size of the geometry elements. The array is indexed with cluster element
	indices. ClusterProperty::GetValues allows you to perform fast iteration over the array by using
	a CBitArray object and skipping the cluster elements that are not part of the cluster property.
	ClusterProperty::GetValues is especially suited for writing exporter applications as it is
	designed to access large data sets more easily and faster than ClusterProperty::GetElements.

	\sa ClusterProperty::GetValues, CGeometryAccessor, CClusterPropertyElementArray, Cluster

	\eg Using ClusterProperty::GetValues
	\code
		using namespace XSI;
		Application app;
		Model root = app.GetActiveSceneRoot();

		X3DObject myCube;
		root.AddGeometry( L"Cube", L"MeshSurface", L"", myCube );

		// install the texture support object
		CValueArray args(4);
		args[0] = CValue( CString(L"Image") );
		args[1] = CValue(myCube);
		args[2] = CValue((short)1);
		args[3] = CValue(false);

		CStatus st;
		CValue outArg;
		st = app.ExecuteCommand( L"BlendInPresets", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"BlendInPresets failed" );
			return;
		}

		args[0] = CValue(myCube);
		args[1] = CValue((LONG)siTxtUV);
		args[2] = CValue((LONG)siTxtDefaultSpherical);
		args[3] = CValue(CString(L"Texture_Support"));

		st = app.ExecuteCommand( L"CreateTextureSupport", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"CreateTextureSupport failed" );
			return;
		}

		// get the uv data on the cube
		PolygonMesh pm = myCube.GetActivePrimitive().GetGeometry();
		CGeometryAccessor ga = pm.GetGeometryAccessor();

		CRefArray uvs = ga.GetUVs();
		LONG nUVs = uvs.GetCount();

		// iterate over the uv data
		for ( LONG i=0; i<nUVs; i++ )
		{
			ClusterProperty uv(uvs[i]);

			app.LogMessage( uv.GetName() );

			// get the values
			CFloatArray uvValues;
			uv.GetValues( uvValues );

			// log the values
			LONG nValues = uvValues.GetCount();
			for ( LONG idxElem=0; idxElem<nValues; idxElem += 3)
			{
				CString X(uvValues[idxElem]);
				CString Y(uvValues[idxElem+1]);
				CString W(uvValues[idxElem+2]);
				app.LogMessage( CString(idxElem/3) + L": " + X + L" "+ Y + L" "+ W );
			}
		}

		// iterate over the uv data using a CBitArray object
		for ( LONG i=0; i<nUVs; i++ )
		{
			ClusterProperty uv(uvs[i]);

			app.LogMessage( uv.GetName() );

			// get the values and the element set
			CBitArray elemSet;
			CFloatArray uvValues;
			uv.GetValues( uvValues, elemSet );

			// log the values by iterating over the cluster elements adressed by
			// the property
			LONG idxElem = elemSet.GetIterator();
			while ( elemSet.GetNextTrueBit(idxElem) )
			{
				LONG nIdx = idxElem*3;
				CString X(uvValues[nIdx]);
				CString Y(uvValues[nIdx+1]);
				CString W(uvValues[nIdx+2]);
				app.LogMessage( CString(idxElem) + L": " + X + L" "+ Y + L" "+ W );
			}
		}

		// Expected results for both loops:
		//'INFO : Texture_Support
		//'INFO : 0: 0.125 0.304087 0
		//'INFO : 1: 0.125 0.695913 0
		//'INFO : 2: -0.125 0.695913 0
		//'INFO : 3: -0.125 0.304087 0
		//'INFO : 4: 0.125 0.304087 0
		//'INFO : 5: 0.875 0.304087 0
		//'INFO : 6: 0.625 0.304087 0
		//'INFO : 7: 0.375 0.304087 0
		//'INFO : 8: 0.125 0.304087 0
		//'INFO : 9: 0.375 0.304087 0
		//'INFO : 10: 0.375 0.695913 0
		//'INFO : 11: 0.125 0.695913 0
		//'INFO : 12: 0.875 0.304087 0
		//'INFO : 13: 0.875 0.695913 0
		//'INFO : 14: 0.625 0.695913 0
		//'INFO : 15: 0.625 0.304087 0
		//'INFO : 16: 0.125 0.695913 0
		//'INFO : 17: 0.375 0.695913 0
		//'INFO : 18: 0.625 0.695913 0
		//'INFO : 19: 0.875 0.695913 0
		//'INFO : 20: 0.375 0.304087 0
		//'INFO : 21: 0.625 0.304087 0
		//'INFO : 22: 0.625 0.695913 0
		//'INFO : 23: 0.375 0.695913 0
	\endcode

	\eg
	\code
		using namespace XSI;
		Application app;
		Model root = app.GetActiveSceneRoot();

		X3DObject myCube;
		root.AddGeometry( L"Cube", L"MeshSurface", L"", myCube );

		// install the texture support object
		CValueArray args(4);
		args[0] = CValue( CString(L"Image") );
		args[1] = CValue(myCube);
		args[2] = CValue((short)1);
		args[3] = CValue(false);

		CStatus st;
		CValue outArg;
		st = app.ExecuteCommand( L"BlendInPresets", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"BlendInPresets failed" );
			return;
		}

		args[0] = CValue(myCube);
		args[1] = CValue((LONG)siTxtUV);
		args[2] = CValue((LONG)siTxtDefaultSpherical);
		args[3] = CValue(CString(L"Texture_Support"));

		st = app.ExecuteCommand( L"CreateTextureSupport", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"CreateTextureSupport failed" );
			return;
		}

		// get the sample point cluster
		Geometry geom(myCube.GetActivePrimitive().GetGeometry());

		CRefArray samplePoints;
		geom.GetClusters().Filter(	siSampledPointCluster,
									CStringArray(),
									L"",
									samplePoints );

		Cluster cluster(samplePoints.GetItem(0));
		ClusterProperty UVWProp(myCube.GetMaterial().GetCurrentUV());

		app.LogMessage( L"Cluster property class ID: " +
						UVWProp.GetClassIDName() );
		app.LogMessage( L"Cluster property type: " + UVWProp.GetType() );
		app.LogMessage( L"Cluster property name: " + UVWProp.GetName() );
	\endcode
 */
//*****************************************************************************

class SICPPSDKDECL ClusterProperty : public Property
{
public:
	/*! Default constructor. */
	ClusterProperty();

	/*! Default destructor. */
	~ClusterProperty();

	/*! Constructor.
	\param in_ref constant reference object.
	*/
	ClusterProperty(const CRef& in_ref);

	/*! Copy constructor.
	\param in_obj constant class object.
	*/
	ClusterProperty(const ClusterProperty& in_obj);

	/*! Returns true if a given class type is compatible with this API class.
	* \param in_ClassID class type.
	* \return true if the class is compatible, false otherwise.
	*/
	bool IsA( siClassID in_ClassID) const;

	/*! Returns the type of the API class.
	\return The class type.
	*/
	siClassID GetClassID() const;

	/*! Creates an object from another object. The newly created object is set to
	empty if the input object is not compatible.
	\param in_obj constant class object.
	\return The new ClusterProperty object.
	*/
	ClusterProperty& operator=(const ClusterProperty& in_obj);

	/*! Creates an object from a reference object. The newly created object is
	set to empty if the input reference object is not compatible.
	\param in_ref constant class object.
	\return The new ClusterProperty object.
	*/
	ClusterProperty& operator=(const CRef& in_ref);

	/*! Returns an array of geometry indices represented by the cluster.
	\return The new CClusterPropertyElementArray object.
	*/
	CClusterPropertyElementArray GetElements() const;

	/*! Returns the number of values per element in the cluster property data. For instance, a uvw
	cluster property returns 3(uvw) and in the case of a vertex color cluster property it returns 4(rgba).
	\return The number of values per element.
	\since 5.0
	*/
	LONG GetValueSize() const;

	/*! Returns the type of the cluster property.
	\return Cluster property type.
	\since 5.0
	*/
	siClusterPropertyType GetPropertyType() const;

	/*! Returns the internal type of the cluster property elements which represents the data. In general,
	all elements are represented internally as float. However. types such as the vertex color type is
	represented as short (rgba). For instance, you can use the internal data type if you need to compress
	the memory allocated for storing vertex color values.
	\return Data type.
	\sa ClusterProperty::GetValues
	\since 5.0
	*/
	CValue::DataType GetInternalType() const;

	/*! Returns an array of cluster element values. The number of elements in the array matches the number
	of geometry elements (N), the array size is N * ClusterProperty::GetSizeValue.
	\retval out_data Array of float values.

	note Unlike SetValues, GetValues can be used in any context. However, the 
	output data cannot be used with SetValues unless this ClusterProperty 
	instance has been created with CClusterPropertyBuilder
			
	\return CStatus::OK
	\return CStatus::Fail
	\sa CGeometryAccessor::GetClusterProperties, Material::GetClusterProperties
	\since 5.0
	*/
	CStatus GetValues( CFloatArray& out_data ) const;

	/*! Returns an array of cluster element values. The number of elements in the array matches the number
	of geometry elements (N), the array size is N * ClusterProperty::GetSizeValue. The function also returns
	a bit array of size N which identifies the indices of the geometry elements set in the array. You can use
	the bit array items to quickly identify what are the geometry elements affected by the cluster property.

	\retval out_data Array of float values.
	\retval out_elemIdx Array of element index flags.
	
	note Unlike SetValues, GetValues can be used in any context. However, the 
	output data cannot be used with SetValues unless this ClusterProperty 
	instance has been created with CClusterPropertyBuilder
	
	\return CStatus::OK
	\return CStatus::Fail
	\sa CGeometryAccessor::GetClusterProperties, Material::GetClusterProperties
	\since 5.0
	*/
	CStatus GetValues( CFloatArray& out_data, CBitArray& out_elemIdx ) const;

	/*! Sets the geometry elements exposed by this cluster property. This function sets the geometry elements
	in the order specified by \c in_pValues starting from index 0. The values are set directly on the property
	and the operation is not undoable.

	For large data sets, you can use an offset index to set the values in chunks. The offset is used for indexing
	into the cluster elements which allows you to optimize the memory allocation required for setting the element
	values.

	\note SetValues must be used with ClusterProperty objects returned by CClusterPropertyBuilder methods
	such as CClusterPropertyBuilder::AddUV and CClusterPropertyBuilder::AddVertexColor. This function is 
	typically used in the context of import/export. Because you cannot build clusters in a custom operator, 
	SetValues cannot be used in this particular context.
	
	\param in_pValues Array of size \c in_nValues * ClusterProperty::GetSizeValue containing the element values.
	\param in_nValues This value corresponds to the number of cluster elements to set and doesn't correspond to
		the number of items in \c in_pValues. The number of values in \c in_pValues corresponds to the cluster
		property data size (specified by ClusterProperty::GetSizeValue) * \c in_nValues.
	\param in_nOffset Specifies the starting index in the cluster elements.

	\return CStatus::OK Success.
	\return CStatus::InvalidArgument Returns an error if \c in_nElements is greater than the number of cluster
		elements or smaller than zero. Also returns an error if	\c in_pValues is invalid.
	\sa CClusterPropertyBuilder, CGeometryAccessor::GetClusterProperties, Material::GetClusterProperties
	\since 5.0
	*/
	CStatus SetValues(
		const float*	in_pValues,
		LONG			in_nValues,
		LONG			in_nOffset=0);

	/*! Sets the geometry element exposed by this cluster property. This function sets the values directly on the
	property and the operation is not undoable.

	\note SetValues must be used with ClusterProperty objects returned by CClusterPropertyBuilder methods
	such as CClusterPropertyBuilder::AddUV and CClusterPropertyBuilder::AddVertexColor. This function is 
	typically used in the context of import/export. Because you cannot build clusters in a custom operator, 
	SetValues cannot be used in this particular context.

	\param in_pElements Array of size \c in_nElements containing the geometry elements to set.
	\param in_pValues Array of size \c in_nElements * ClusterProperty::GetValueSize containing the values.
	\param in_nElements Number of elements in \c in_pElements.

	\return CStatus::OK Success.
	\return CStatus::InvalidArgument Returns an error if \c in_nElements is greater than the number of cluster
		elements or smaller than zero. Also returns an error if	\c in_pElements or \c in_pValues are invalid.
	\sa CClusterPropertyBuilder, CGeometryAccessor::GetClusterProperties, Material::GetClusterProperties
	\since 5.0
	*/
	CStatus SetValues(
		const LONG*		in_pElements,
		const float*	in_pValues,
		LONG			in_nElements);

	private:
	ClusterProperty * operator&() const;
	ClusterProperty * operator&();
};

//*****************************************************************************
/*! \class CClusterPropertyElementArray xsi_clusterproperty.h
	\brief An array of cluster property elements in a ClusterProperty object.

	The cluster property maps the cluster element indices to property values (such as RGB vertex color). The value
	of the element in a cluster property element array is the cluster property value.

	You can set elements using CClusterPropertyElementArray::PutItems and this is accessible from an operator.
	Unlike CClusterElementArray, a CClusterPropertyElementArray object allows editing of element values.

	The following lists all cluster properties along with their data type values returned through
	CClusterPropertyElementArray::GetItem:

		\li %Vertex color property: array of RGBA values

		\li %Envelope weight property: array containing the weight value for each deformer

		\li Weight map property: array of only 1 value which is the weight value

		\li UV property: array of UVW values

	\warning This specialized array is returned by ClusterProperty::GetElements, it is not meant to be created
		and modified in user-defined functions. If you want to add and remove arbitrary items to a
		collection, you must use a CRefArray instead.

	\sa ClusterProperty, Cluster

	\eg
	\code
		using namespace XSI;
		Application app;
		Model root = app.GetActiveSceneRoot();

		X3DObject myCube;
		root.AddGeometry( L"Cube", L"MeshSurface", L"", myCube );

		// install the texture support object
		CValueArray args(4);
		args[0] = CValue( CString(L"Image") );
		args[1] = CValue(myCube);
		args[2] = CValue((short)1);
		args[3] = CValue(false);

		CStatus st;
		CValue outArg;
		st = app.ExecuteCommand( L"BlendInPresets", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"BlendInPresets failed" );
			return;
		}

		args[0] = CValue(myCube);
		args[1] = CValue((LONG)siTxtUV);
		args[2] = CValue((LONG)siTxtDefaultSpherical);
		args[3] = CValue(CString(L"Texture_Support"));

		st = app.ExecuteCommand( L"CreateTextureSupport", args, outArg );
		if ( st.GetCode() != CStatus::OK )
		{
			app.LogMessage( L"CreateTextureSupport failed" );
			return;
		}

		// get the sample point cluster and print out its UVW values
		Geometry geom(myCube.GetActivePrimitive().GetGeometry());

		CRefArray samplePoints;
		geom.GetClusters().Filter(siSampledPointCluster, CStringArray(), L"",
			samplePoints );

		Cluster cluster(samplePoints.GetItem(0));
		ClusterProperty UVWProp(myCube.GetMaterial().GetCurrentUV());

		CClusterPropertyElementArray uvwElementArray(UVWProp.GetElements());

		CDoubleArray uvwArray(uvwElementArray.GetArray());

		for ( LONG i=0; i< uvwArray.GetCount(); i += uvwElementArray.GetValueSize())
		{
			app.LogMessage( L"UVW: " +
				CValue(uvwArray[i]).GetAsText() + L"," +
				CValue(uvwArray[i+1]).GetAsText() + L"," +
				CValue(uvwArray[i+2]).GetAsText() );
		}
	\endcode
 */
//*****************************************************************************

class SICPPSDKDECL CClusterPropertyElementArray
{
	public:
	/*! Default constructor */
	CClusterPropertyElementArray();

	/*! Copy constructor.
	\param in_array constant CClusterPropertyElementArray object.
	*/
	CClusterPropertyElementArray(const CClusterPropertyElementArray& in_array);

	/*!Default destructor */
	virtual ~CClusterPropertyElementArray();

	/*! Creates an array from a reference object.
	\param in_array constant CClusterPropertyElementArray object.
	\return The new CClusterPropertyElementArray object.
	*/
	CClusterPropertyElementArray& operator=(
		const CClusterPropertyElementArray& in_array);

	/*! Returns an array of element values which contains CClusterPropertyElementArray::GetCount() *
	CClusterPropertyElementArray::GetValueSize() double values. The array is laid out so that all values
	for an element are contiguous.
	\return Array of element values
	*/
	CDoubleArray GetArray() const;

	/*! Sets the values on the underlying ClusterProperty.
	\param in_values Array of element values
	\return CStatus::OK success
	\return CStatus::Fail failure
	*/
	CStatus PutArray(const CDoubleArray& in_values);

	/*! Returns the number of cluster element values
	\return The number of cluster element values.
	*/
	LONG GetCount() const;

	/*! Returns the cluster element values at a given index.
	\param in_index Element index.
	\return Array of element values. The array will be empty if the index is greater than the number
		of elements or smaller than zero.
	*/
	CDoubleArray GetItem( LONG in_index ) const;

	/*! Sets the cluster element values at a given index.
	\param in_index Element index.
	\param in_values Array of element values.
	\return CStatus::OK success
	\return CStatus::Fail failure
	\return CStatus::InvalidArgument The index is greater than the number of elements or smaller than zero.
	*/
	CStatus PutItem( LONG in_index, const CDoubleArray& in_values );

	/*! Returns cluster element values by indices.
	\param in_indices Array of indices
	\return Array of element values. The array will be empty if an index is greater than the number
		of elements or smaller than zero.
	*/
	CDoubleArray GetItemsByIndex( const CLongArray& in_indices ) const;

	/*! Sets cluster element values by indices.
	\param in_indices Array of indices
	\param in_values Array of element values
	\return CStatus::OK success
	\return CStatus::Fail failure
	\return CStatus::InvalidArgument If an index is greater than the number of elements or smaller than zero.
	*/
	CStatus PutItemsByIndex( const CLongArray& in_indices,
		const CDoubleArray& in_values );

	/*! Returns the number of values per element in the cluster. For instance, a uvw cluster property returns
	3(uvw) and in the case of a vertex color cluster property it returns 4(rgba).
	\return The number of values per element.
	*/
	LONG GetValueSize() const;

	private:
	void*	m_ptr;
	bool	m_bFlag;
};

};

#endif // __XSICLUSTERPROPERTY_H__
