text: //-*****************************************************************************
  //
  // Copyright (c) 2009-2012,
  //  Sony Pictures Imageworks Inc. and
  //  Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
  //
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  // *       Redistributions of source code must retain the above copyright
  // notice, this list of conditions and the following disclaimer.
  // *       Redistributions in binary form must reproduce the above
  // copyright notice, this list of conditions and the following disclaimer
  // in the documentation and/or other materials provided with the
  // distribution.
  // *       Neither the name of Sony Pictures Imageworks, nor
  // Industrial Light & Magic, nor the names of their contributors may be used
  // to endorse or promote products derived from this software without specific
  // prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  //
  //-*****************************************************************************
  #ifndef Alembic_Prman_ArbAttrUtil_h
  #define Alembic_Prman_ArbAttrUtil_h
  
  #include <ri.h>
  
  #include <Alembic/AbcGeom/All.h>
  #include <boost/smart_ptr.hpp>
  #include <set>
  
  using namespace Alembic::AbcGeom;
  
  //-*****************************************************************************
  class ParamListBuilder
  {
  public:
      
      ~ParamListBuilder();
      
      void add( const std::string & declaration, RtPointer value,
                ArraySamplePtr sampleToRetain = ArraySamplePtr() );
      
      // used for conversion of DoubleGeomParams
      // NOTE: In addition to performing the conversion, this also makes and
      // returns an internal copy of the data. Even in cases in which you're
      // starting with float data, this can be usful if you need to manipulate
      // the values after-the-fact.
      template <typename T>
      std::vector<RtFloat> * addAsFloat( const std::string & declaration, const T * length,
              size_t numValues );
      
      template <typename T>
      std::vector<RtInt> * addAsInt( const std::string & declaration, const T * length,
              size_t numValues );
      
      //returns the start of the current vector and pushes a new one
      RtPointer finishStringVector();
      
      //If retainLocally is false, it's expected that "value" remain alive
      //The common case is out of a StringArraySamplePtr which is provided
      //as sampleToRetain in the add method
      void addStringValue( const std::string & value,
                                bool retainLocally = false );
  
      RtInt n();
      RtToken* nms();
      RtPointer* vals();
  
  private:
      std::vector<RtToken> m_outputDeclarations;
      std::vector<RtPointer> m_values;
      std::vector<ArraySamplePtr> m_retainedSamples;
  
      std::vector<RtString> m_retainedStrings;
      
      typedef boost::shared_ptr<std::vector<RtString> > SharedRtStringVector;
      std::vector<SharedRtStringVector> m_convertedStringVectors;
      
      typedef boost::shared_ptr<std::vector<RtFloat> > SharedRtFloatVector;
      std::vector<SharedRtFloatVector> m_convertedFloatVectors;
      
      typedef boost::shared_ptr<std::vector<RtInt> > SharedRtIntVector;
      std::vector<SharedRtIntVector> m_convertedIntVectors;
      
  };
  
  //-*****************************************************************************
  
  template <typename T>
  std::vector<RtFloat> * ParamListBuilder::addAsFloat( const std::string & declaration,
          const T * value, size_t length)
  {
      SharedRtFloatVector convertedValues( new std::vector<RtFloat> );
      convertedValues->reserve( length );
      
      convertedValues->insert( convertedValues->end(), value, value + length );
      m_convertedFloatVectors.push_back( convertedValues );
      
      add( declaration, &( ( *convertedValues )[0] ) );
      
      return &(*convertedValues);
  }
  
  //-*****************************************************************************
  
  template <typename T>
  std::vector<RtInt> * ParamListBuilder::addAsInt( const std::string & declaration,
          const T * value, size_t length)
  {
      SharedRtIntVector convertedValues( new std::vector<RtInt> );
      convertedValues->reserve( length );
      
      convertedValues->insert( convertedValues->end(), value, value + length );
      m_convertedIntVectors.push_back( convertedValues );
      
      add( declaration, &( ( *convertedValues )[0] ) );
      
      return &(*convertedValues);
  }
  
  
  
  //-*****************************************************************************
  std::string GetPrmanScopeString( GeometryScope scope );
  
  //-*****************************************************************************
  template <typename T>
  void AddGeomParamToParamListBuilder( ICompoundProperty & parent,
                                               const PropertyHeader &propHeader,
                                               ISampleSelector &sampleSelector,
                                               const std::string &rmanBaseType,
                                               ParamListBuilder &ParamListBuilder,
                                               size_t baseArrayExtent = 1,
                                               const std::string & overrideName = ""
                                             )
  {
      T param( parent, propHeader.getName() );
  
      if ( !param.valid() )
      {
          //TODO error message?
          return;
      }
  
      std::string rmanType = GetPrmanScopeString( param.getScope() ) + " ";
  
      rmanType += rmanBaseType;
  
      size_t arrayExtent = baseArrayExtent * param.getArrayExtent();
      if (arrayExtent > 1)
      {
          std::ostringstream buffer;
          buffer << "[" << arrayExtent << "]";
          rmanType += buffer.str();
      }
  
      rmanType += " " + (
              overrideName.empty() ? propHeader.getName() : overrideName );
  
  
      typename T::prop_type::sample_ptr_type valueSample =
              param.getExpandedValue( sampleSelector ).getVals();
      
      
      ParamListBuilder.add( rmanType, (RtPointer)valueSample->get(),
              valueSample );
      
  }
  
  
  //-*****************************************************************************
  
  // NOTE: In addition to performing the conversion, this also makes and
  // returns an internal copy of the data. Even in cases in which you're
  // starting with float data, this can be usful if you need to manipulate
  // the values after-the-fact. The AddGeomParamToParamListBuilder codepath 
  // normally avoids a copy of the data as the GeomParam samples can be handed
  // to RenderMan directly. 
  template <typename T, typename podT>
  std::vector<RtFloat> * AddGeomParamToParamListBuilderAsFloat( ICompoundProperty & parent,
                                               const PropertyHeader &propHeader,
                                               ISampleSelector &sampleSelector,
                                               const std::string &rmanBaseType,
                                               ParamListBuilder &ParamListBuilder,
                                               const std::string & overrideName = ""
                                             )
  {
      T param( parent, propHeader.getName() );
  
      if ( !param.valid() )
      {
          //TODO error message?
          return 0;
      }
  
      std::string rmanType = GetPrmanScopeString( param.getScope() ) + " ";
  
      rmanType += rmanBaseType;
      
      size_t dataTypeExtent = T::prop_type::traits_type::dataType().getExtent();
      
      // If rmanBaseType is something other than float, the assumption is that the
      // specified rman type is in agreement with the dataTypeExtent -- i.e. color = 3
      // If it's just float, we want to make sure that our data footprint matches
      // that of the incoming type in RenderMan terms.
      size_t baseArrayExtent = (rmanBaseType == "float") ? dataTypeExtent: 1;
      
      size_t arrayExtent = baseArrayExtent * param.getArrayExtent();
      if (arrayExtent > 1)
      {
          std::ostringstream buffer;
          buffer << "[" << arrayExtent << "]";
          rmanType += buffer.str();
      }
  
      rmanType += " " + (
              overrideName.empty() ? propHeader.getName() : overrideName );
  
  
      typename T::prop_type::sample_ptr_type valueSample =
              param.getExpandedValue( sampleSelector ).getVals();
      
      
      //always grab the natural extent of the incoming data for the internal copy
      int rawExtent = dataTypeExtent * param.getArrayExtent();
      
      return ParamListBuilder.addAsFloat( rmanType,
              reinterpret_cast<const podT *>( valueSample->get() ),
                      valueSample->size() * rawExtent );
  
  }
  
  //-*****************************************************************************
  
  // NOTE: In addition to performing the conversion, this also makes and
  // returns an internal copy of the data. Even in cases in which you're
  // starting with int data, this can be usful if you need to manipulate
  // the values after-the-fact. The AddGeomParamToParamListBuilder codepath 
  // normally avoids a copy of the data as the GeomParam samples can be handed
  // to RenderMan directly. 
  template <typename T, typename podT>
  std::vector<RtInt> * AddGeomParamToParamListBuilderAsInt( ICompoundProperty & parent,
                                               const PropertyHeader &propHeader,
                                               ISampleSelector &sampleSelector,
                                               ParamListBuilder &ParamListBuilder,
                                               const std::string & overrideName = ""
                                             )
  {
      T param( parent, propHeader.getName() );
  
      if ( !param.valid() )
      {
          //TODO error message?
          return 0;
      }
      
      //Unlike RtFloat, "int" is the only RenderMan type that takes something
      //compatible with RtInt. We can assume anything sent here is intended to be
      //written to RenderMan as int[n].
      const std::string rmanBaseType("int");
      
      std::string rmanType = GetPrmanScopeString( param.getScope() ) + " ";
  
      rmanType += rmanBaseType;
      
      size_t dataTypeExtent = T::prop_type::traits_type::dataType().getExtent();
      
      
  
      size_t arrayExtent = dataTypeExtent * param.getArrayExtent();
      if (arrayExtent > 1)
      {
          std::ostringstream buffer;
          buffer << "[" << arrayExtent << "]";
          rmanType += buffer.str();
      }
  
      rmanType += " " + (
              overrideName.empty() ? propHeader.getName() : overrideName );
  
  
      typename T::prop_type::sample_ptr_type valueSample =
              param.getExpandedValue( sampleSelector ).getVals();
      
      
      return ParamListBuilder.addAsInt( rmanType,
              reinterpret_cast<const podT *>( valueSample->get() ),
                      valueSample->size() * arrayExtent );
  
  }
  
  
  
  
  
  //-*****************************************************************************
  
  void AddArbitraryGeomParams( ICompoundProperty &parent,
                               ISampleSelector &sampleSelector,
                               ParamListBuilder &ParamListBuilder,
                               const std::set<std::string> * excludeNames = NULL
                             );
  
  
  //-*****************************************************************************
  
  
  //[array, scalar], [as float, as int, as string];
  
  
  template <typename propT, typename podT>
  void AddArrayPropertyAsFloatToParamListBuilder(
          ICompoundProperty & parent, 
          const PropertyHeader &propHeader,
          ISampleSelector &sampleSelector,
          const std::string & name,
          const std::string & rmanBaseType,
          ParamListBuilder & paramListBuilder)
  {
      propT prop(parent, propHeader.getName());
      
      size_t dataTypeExtent = propT::traits_type::dataType().getExtent();
      
      typename propT::sample_ptr_type sample = prop.getValue(sampleSelector);
      
      std::ostringstream buffer;
      buffer << rmanBaseType;
      
      size_t baseArrayExtent = (rmanBaseType == "float") ? dataTypeExtent: 1;
      size_t arrayExtent = baseArrayExtent * sample->size();
      if (arrayExtent > 1)
      {
          buffer << "[" << arrayExtent << "]";
      }
      
      buffer << " " << name;
      
      paramListBuilder.addAsFloat<podT>(
              buffer.str(), reinterpret_cast<const podT *>( sample->get() ),
                      sample->size() * dataTypeExtent);
  }
  
  
  template <typename propT, typename podT>
  void AddScalarPropertyAsFloatToParamListBuilder(
          ICompoundProperty & parent, 
          const PropertyHeader &propHeader,
          ISampleSelector &sampleSelector,
          const std::string & name,
          const std::string & rmanBaseType,
          ParamListBuilder &paramListBuilder)
  {
      propT prop(parent, propHeader.getName());
      
      typename propT::value_type scalarValue;
      prop.get(scalarValue, sampleSelector);
      
      std::ostringstream buffer;
      buffer << rmanBaseType;
      
      size_t dataTypeExtent =
              propT::traits_type::dataType().getExtent();
      
      if (rmanBaseType == "float" && dataTypeExtent > 1)
      {
          buffer << "[" << dataTypeExtent << "] ";
      }
      buffer << " " << name;
      
      paramListBuilder.addAsFloat<podT>(
              buffer.str(), reinterpret_cast<const podT *>(&scalarValue),
                      dataTypeExtent);
  }
  
  
  // template <typename propT, typename podT>
  // void AddArrayPropertyAsIntToParamListBuilder(
  //         ICompoundProperty & parent, 
  //         const PropertyHeader &propHeader,
  //         ISampleSelector &sampleSelector,
  //         const std::string & name,
  //         ParamListBuilder &ParamListBuilder);
  
  template <typename propT, typename podT>
  void AddScalarPropertyAsIntToParamListBuilder(
          ICompoundProperty & parent, 
          const PropertyHeader &propHeader,
          ISampleSelector &sampleSelector,
          const std::string & name,
          ParamListBuilder &paramListBuilder)
  {
      propT prop(parent, propHeader.getName());
      
      
      typename propT::value_type scalarValue;
      prop.get(scalarValue, sampleSelector);
      
      std::ostringstream buffer;
      buffer << "int";
      
      size_t dataTypeExtent =
              propT::traits_type::dataType().getExtent();
      
      if (dataTypeExtent > 1)
      {
          buffer << "[" << dataTypeExtent << "]";
      }
      buffer << " " << name;
      
      paramListBuilder.addAsInt<podT>(
              buffer.str(), reinterpret_cast<const podT *>(&scalarValue),
                      dataTypeExtent);
  }
  
  template <typename propT>
  void AddArrayPropertyAsStringToParamListBuilder(
          ICompoundProperty & parent, 
          const PropertyHeader &propHeader,
          ISampleSelector &sampleSelector,
          const std::string & name,
          ParamListBuilder &paramListBuilder)
  {
      propT prop(parent, propHeader.getName());
      
      
      StringArraySamplePtr valueSample = prop.getValue(sampleSelector);
      
      std::ostringstream buffer;
      buffer << "string";
      
      if (valueSample->size() > 1)
      {
          buffer << "[" << valueSample->size() << "]";
      }
      
      buffer << " " << name;
      
      for ( size_t i = 0; i < valueSample->size(); ++i )
      {
          paramListBuilder.addStringValue( (*valueSample)[i] );
      }
      
      RtPointer dataStart = paramListBuilder.finishStringVector();
      
      paramListBuilder.add(buffer.str(), dataStart, valueSample);
  }
  
  
  
  template <typename propT>
  void AddScalarPropertyAsStringToParamListBuilder(
          ICompoundProperty & parent, 
          const PropertyHeader &propHeader,
          ISampleSelector &sampleSelector,
          const std::string & name,
          ParamListBuilder &paramListBuilder)
  {
      propT prop(parent, propHeader.getName());
      
      typename propT::value_type scalarValue;
      prop.get(scalarValue, sampleSelector);
      
      std::ostringstream buffer;
      buffer << "string";
      
      size_t dataTypeExtent =
              propT::traits_type::dataType().getExtent();
      
      if (dataTypeExtent > 1)
      {
          buffer << "[" << dataTypeExtent << "]";
      }
      buffer << " " << name;
      
      
      for (size_t i = 0; i < dataTypeExtent; ++i)
      {
          paramListBuilder.addStringValue(
                  reinterpret_cast<std::string*>(&scalarValue)[i], true);
      }
      
      paramListBuilder.add(buffer.str(),
              paramListBuilder.finishStringVector());
  }
  
  
  
  
  #endif
  