FromHoudiniGeometryConverter::Convertability FromHoudiniCurvesConverter::canConvert( const GU_Detail *geo )
{
	const GA_PrimitiveList &primitives = geo->getPrimitiveList();
	
	unsigned numPrims = geo->getNumPrimitives();
	GA_Iterator firstPrim = geo->getPrimitiveRange().begin();
	if ( !numPrims || !compatiblePrimitive( primitives.get( firstPrim.getOffset() )->getTypeId() ) )
	{
		return Inapplicable;
	}
	
	const GEO_Curve *firstCurve = (const GEO_Curve*)primitives.get( firstPrim.getOffset() );
	bool periodic = firstCurve->isClosed();
	unsigned order = firstCurve->getOrder();
	
	for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it )
	{
		const GA_Primitive *prim = primitives.get( it.getOffset() );
		if ( !compatiblePrimitive( prim->getTypeId() ) )
		{
			return Inapplicable;
		}
		
		const GEO_Curve *curve = (const GEO_Curve*)prim;
		if ( curve->getOrder() != order )
		{
			return Inapplicable;
		}
		
		if ( curve->isClosed() != periodic )
		{
			return Inapplicable;
		}
	}
	
	// is there a single named shape?
	GA_ROAttributeRef attrRef = geo->findPrimitiveAttribute( "name" );
	if ( attrRef.isValid() && attrRef.isString() )
	{
		const GA_Attribute *nameAttr = attrRef.getAttribute();
		const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple();
		GA_StringTableStatistics stats;
		tuple->getStatistics( nameAttr, stats );
		if ( stats.getEntries() < 2 )
		{
			return Ideal;
		}
	}
	
	return Suitable;
}
ObjectPtr FromHoudiniGroupConverter::doConversion( ConstCompoundObjectPtr operands ) const
{
	GU_DetailHandleAutoReadLock readHandle( handle() );
	const GU_Detail *geo = readHandle.getGdp();
	if ( !geo )
	{
		return 0;
	}
	
	size_t numResultPrims = 0;
	size_t numOrigPrims = geo->getNumPrimitives();
	
	GroupPtr result = new Group();
	
	if ( operands->member<const IntData>( "groupingMode" )->readable() == NameAttribute )
	{
		GA_ROAttributeRef attributeRef = geo->findPrimitiveAttribute( "name" );
		if ( attributeRef.isInvalid() || !attributeRef.isString() )
		{
			GU_Detail ungroupedGeo( (GU_Detail*)geo );
			GA_PrimitiveGroup *ungrouped = static_cast<GA_PrimitiveGroup*>( ungroupedGeo.createInternalElementGroup( GA_ATTRIB_PRIMITIVE, "FromHoudiniGroupConverter__ungroupedPrimitives" ) );
			ungrouped->toggleRange( ungroupedGeo.getPrimitiveRange() );
			
			VisibleRenderablePtr renderable = 0;
			doGroupConversion( &ungroupedGeo, ungrouped, renderable, operands );
			if ( renderable )
			{
				Group *group = runTimeCast<Group>( renderable );
				if ( group )
				{
					const Group::ChildContainer &children = group->children();
					for ( Group::ChildContainer::const_iterator it = children.begin(); it != children.end(); ++it )
					{
						result->addChild( *it );
					}
				}
				else
				{
					result->addChild( renderable );
				}
			}
			
			return result;
		}
		
		GU_Detail groupGeo( (GU_Detail*)geo );
		
		AttributePrimIdGroupMap groupMap;
		regroup( &groupGeo, groupMap, attributeRef );
		
		for ( AttributePrimIdGroupMapIterator it=groupMap.begin(); it != groupMap.end(); ++it )
		{
			convertAndAddPrimitive( &groupGeo, it->second, result, operands, it->first.first );
		}
	}
	else
	{
		for ( GA_GroupTable::iterator<GA_ElementGroup> it=geo->primitiveGroups().beginTraverse(); !it.atEnd(); ++it )
		{
			GA_PrimitiveGroup *group = static_cast<GA_PrimitiveGroup*>( it.group() );
			if ( group->getInternal() || group->isEmpty() )
			{
				continue;
			}

			VisibleRenderablePtr renderable = 0;
			numResultPrims += doGroupConversion( geo, group, renderable, operands );
			if( !renderable )
			{
				continue;
			}
			
			renderable->blindData()->member<StringData>( "name", false, true )->writable() = group->getName().toStdString();
			result->addChild( renderable );
		}

		if ( numOrigPrims == numResultPrims )
		{
			return result;
		}

		GU_Detail ungroupedGeo( (GU_Detail*)geo );
		GA_PrimitiveGroup *ungrouped = static_cast<GA_PrimitiveGroup*>( ungroupedGeo.createInternalElementGroup( GA_ATTRIB_PRIMITIVE, "FromHoudiniGroupConverter__ungroupedPrimitives" ) );
		for ( GA_GroupTable::iterator<GA_ElementGroup> it=geo->primitiveGroups().beginTraverse(); !it.atEnd(); ++it )
		{
			*ungrouped |= *static_cast<GA_PrimitiveGroup*>( it.group() );
		}
		ungrouped->toggleRange( ungroupedGeo.getPrimitiveRange() );

		if ( ungrouped->isEmpty() )
		{
			return result;
		}

		VisibleRenderablePtr renderable = 0;
		doGroupConversion( &ungroupedGeo, ungrouped, renderable, operands );
		if ( renderable )
		{
			result->addChild( renderable );
		}
	}
	
	return result;
}
FromHoudiniGeometryConverter::Convertability FromHoudiniGroupConverter::canConvert( const GU_Detail *geo )
{
	const GA_PrimitiveList &primitives = geo->getPrimitiveList();
	
	// are there multiple primitives?
	unsigned numPrims = geo->getNumPrimitives();
	if ( numPrims < 2 )
	{
		return Admissible;
	}
	
	// are there mixed primitive types?
	GA_Iterator firstPrim = geo->getPrimitiveRange().begin();
	GA_PrimitiveTypeId firstPrimId = primitives.get( firstPrim.getOffset() )->getTypeId();
	for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it )
	{
		if ( primitives.get( it.getOffset() )->getTypeId() != firstPrimId )
		{
			return Ideal;
		}
	}
	
	// are there multiple named shapes?
	GA_ROAttributeRef attrRef = geo->findPrimitiveAttribute( "name" );
	if ( attrRef.isValid() && attrRef.isString() )
	{
		const GA_Attribute *nameAttr = attrRef.getAttribute();
		const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple();
		if ( tuple->getTableEntries( nameAttr ) > 1 )
		{
			return Ideal;
		}
	}
	
	// are the primitives split into groups?
	UT_PtrArray<const GA_ElementGroup*> primGroups;
	geo->getElementGroupList( GA_ATTRIB_PRIMITIVE, primGroups );
	if ( primGroups.isEmpty() )
	{
		return Admissible;
	}
	
	bool externalGroups = false;
	for ( unsigned i=0; i < primGroups.entries(); ++i )
	{
		const GA_ElementGroup *group = primGroups[i];
		if ( group->getInternal() )
		{
			continue;
		}
		
		if ( group->entries() == numPrims )
		{
			return Admissible;
		}
		
		externalGroups = true;
	}
	
	if ( externalGroups )
	{
		return Ideal;
	}
	
	return Admissible;
}