void addControllersToModifierV2(const std::string& modkey, const std::string& modname, std::vector<AbcProp>& props, 
                              const std::string& file, const std::string& identifier, const std::string& category, alembic_importoptions &options, INode* pNode)
{
   ESS_PROFILE_FUNC();

   std::stringstream evalStream;
   std::string nodeName("$");
   if(pNode){
      evalStream<<GET_MAXSCRIPT_NODE(pNode);
      nodeName = std::string("mynode2113");
   }

   for(int i=0; i<props.size(); i++){
      std::string propName = props[i].name;
      std::string& val = props[i].displayVal;
      bool& bConstant = props[i].bConstant;

      const AbcA::DataType& datatype = props[i].propHeader.getDataType();
      const AbcA::MetaData& metadata = props[i].propHeader.getMetaData();

      if(datatype.getPod() == AbcA::kFloat32POD){

         std::stringstream propStream;
         propStream<<propName;
         if(datatype.getExtent() == 1){
            addFloatControllerV2(evalStream, options, nodeName, modkey, propName, file, identifier, category, propStream.str());
         }
         else if(datatype.getExtent() == 3){

            std::stringstream xStream, yStream, zStream;

            xStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".x";
            yStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".y";
            zStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".z";

            addFloatControllerV2(evalStream, options, nodeName, modkey, propName + std::string("x"), file, identifier, category, xStream.str());
            addFloatControllerV2(evalStream, options, nodeName, modkey, propName + std::string("y"), file, identifier, category, yStream.str());
            addFloatControllerV2(evalStream, options, nodeName, modkey, propName + std::string("z"), file, identifier, category, zStream.str());
         }
      }
      else if(datatype.getPod() == AbcA::kInt32POD){
         std::stringstream propStream;
         propStream<<propName;
         if(datatype.getExtent() == 1){
            addFloatControllerV2(evalStream, options, nodeName, modkey, propName, file, identifier, category, propStream.str());
         }
      }
      else{
         
      }
      evalStream<<"\n";
   }

  
   //ESS_LOG_WARNING(evalStream.str());

   ExecuteMAXScriptScript( EC_UTF8_to_TCHAR((char*)evalStream.str().c_str()));
}
void importMetadata(INode* pNode, AbcG::IObject& iObj)
{
  ESS_PROFILE_FUNC();

  AbcG::IObject* metadataChild = NULL;

  if (getCompoundFromObject(iObj).getPropertyHeader(".metadata") == NULL) {
    return;
  }

  Abc::IStringArrayProperty metaDataProp =
      Abc::IStringArrayProperty(getCompoundFromObject(iObj), ".metadata");
  Abc::StringArraySamplePtr ptr = metaDataProp.getValue(0);
  // for(unsigned int i=0;i<ptr->size();i++){
  //	const char* name = ptr->get()[i].c_str();
  //	Abc::StringArraySamplePtr ptr = metaDataProp.getValue(0);
  //
  //}

  std::stringstream exeBuffer;

  exeBuffer << GET_MAXSCRIPT_NODE(pNode);
  exeBuffer << "ImportMetadata mynode2113";

  exeBuffer << "#(";

  for (int i = 0; i < ptr->size() - 1; i++) {
    exeBuffer << "\"";
    exeBuffer << ptr->get()[i];
    exeBuffer << "\", ";
  }
  exeBuffer << "\"";
  exeBuffer << ptr->get()[ptr->size() - 1];
  exeBuffer << "\")";

  exeBuffer << " \n";

  ExecuteMAXScriptScript(EC_UTF8_to_TCHAR((char*)exeBuffer.str().c_str()));

  // delete[] szBuffer;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// AlembicImport_vis
///////////////////////////////////////////////////////////////////////////////////////////////////
void AlembicImport_SetupVisControl(std::string const &file,
                                   std::string const &identifier,
                                   AbcG::IObject &obj, INode *pNode,
                                   alembic_importoptions &options)
{
  if (!pNode) {
    return;
  }

  AbcG::IVisibilityProperty visibilityProperty = getAbcVisibilityProperty(obj);

  bool isConstant = true;
  if (visibilityProperty.valid()) {
    isConstant = visibilityProperty.isConstant();
  }

  if (isConstant) {
    Animatable *pAnimatable = pNode->SubAnim(0);

    if (pAnimatable &&
        pAnimatable->ClassID() == ALEMBIC_VISIBILITY_CONTROLLER_CLASSID) {
      pNode->DeleteSubAnim(0);
    }

    alembic_fillvis_options visFillOptions;
    visFillOptions.pIObj = &obj;
    visFillOptions.dTicks = 0;
    visFillOptions.bOldVisibility = true;
    AlembicImport_FillInVis(visFillOptions);
    BOOL bVis = visFillOptions.bVisibility ? TRUE : FALSE;
    float fBool = bVis ? 1.0f : 0.0f;
    pNode->SetVisibility(0, fBool);
  }
  else {
    // Create the xform modifier
    AlembicVisibilityController *pControl =
        static_cast<AlembicVisibilityController *>(
            GetCOREInterface()->CreateInstance(
                CTRL_FLOAT_CLASS_ID, ALEMBIC_VISIBILITY_CONTROLLER_CLASSID));

    // Set the alembic id
    TimeValue t = GET_MAX_INTERFACE()->GetTime();

    TimeValue zero(0);

    // Set the alembic id
    pControl->GetParamBlockByID(0)->SetValue(
        GetParamIdByName(pControl, 0, "path"), zero,
        EC_UTF8_to_TCHAR(file.c_str()));
    pControl->GetParamBlockByID(0)->SetValue(
        GetParamIdByName(pControl, 0, "identifier"), zero,
        EC_UTF8_to_TCHAR(identifier.c_str()));
    pControl->GetParamBlockByID(0)->SetValue(
        GetParamIdByName(pControl, 0, "time"), zero, 0.0f);
    pControl->GetParamBlockByID(0)->SetValue(
        GetParamIdByName(pControl, 0, "muted"), zero, FALSE);

    // Add the modifier to the node
    pNode->SetVisController(pControl);

    if (!isConstant) {
      std::stringstream controllerName;
      controllerName << GET_MAXSCRIPT_NODE(pNode);
      controllerName << "mynode2113.visibility.controller.time";
      AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
    }
  }
}
Modifier* createDisplayModifier(std::string modkey, std::string modname, std::vector<AbcProp>& props, INode* pNode)
{
   ESS_PROFILE_FUNC();

   //the script assumes a single object is selected
   std::stringstream evalStream;
   std::string nodeName("$");
   if(pNode){
      evalStream<<GET_MAXSCRIPT_NODE(pNode);
      nodeName = std::string("mynode2113");
   }
   evalStream<<"propModifier = EmptyModifier()"<<"\n";
   evalStream<<"propModifier.name = \""<<modkey<<"\""<<"\n";
   evalStream<<"modCount = "<<nodeName<<".modifiers.count"<<"\n";
   evalStream<<"addmodifier "<<nodeName<<" propModifier before:modCount"<<"\n";
   evalStream<<nodeName<<".modifiers[\""<<modkey<<"\"].enabled = false"<<"\n";

   evalStream<<"propAttribute = attributes propAttribute"<<"\n";
   evalStream<<"("<<"\n";

   evalStream<<"parameters propAttributePRM1 rollout:propAttributeRLT1"<<"\n";
   evalStream<<"("<<"\n";
   for(int i=0; i<props.size(); i++){
      std::string& name = props[i].name;
      std::string& val = props[i].displayVal;
      bool& bConstant = props[i].bConstant;

      const AbcA::DataType& datatype = props[i].propHeader.getDataType();
      const AbcA::MetaData& metadata = props[i].propHeader.getMetaData();

      if(datatype.getPod() == AbcA::kInt32POD && datatype.getExtent() == 1){
         evalStream<<name<<" type:#integer animatable:true ui:e"<<name<<" default:"<<val;
      }
      else if(datatype.getPod() == AbcA::kFloat32POD && datatype.getExtent() == 1){
         evalStream<<name<<" type:#float animatable:true ui:e"<<name<<" default:"<<val;
      }
      else if(datatype.getPod() == AbcA::kFloat32POD && datatype.getExtent() == 3){
         //if(metadata.get("interpretation") == std::string("rgb")){
         //   evalStream<<name<<" type:#color animatable:false ui:e"<<name<<" default:(["<<val<<"] as color)"; 
         //}
         //else{
            //evalStream<<name<<" type:#point3 animatable:false ui:e"<<name<<" ["<<val<<"]"; 
            evalStream<<name<<"x type:#float animatable:true ui:e"<<name<<"x default:300\n";
            evalStream<<name<<"y type:#float animatable:true ui:e"<<name<<"y default:300\n";
            evalStream<<name<<"z type:#float animatable:true ui:e"<<name<<"z default:300\n";
         //}
      }
      else{
         evalStream<<name<<" type:#string animatable:false ui:e"<<name<<" default:\""<<val<<"\"";
      }
      evalStream<<"\n";
   }
   evalStream<<")"<<"\n";
   
   evalStream<<"rollout propAttributeRLT1 \""<<modname<<"\""<<"\n";
   evalStream<<"("<<"\n";

   for(int i=0; i<props.size(); i++){
      std::string& name = props[i].name;
      bool& bConstant = props[i].bConstant;
      const AbcA::DataType& datatype = props[i].propHeader.getDataType();
      const AbcA::MetaData& metadata = props[i].propHeader.getMetaData();
      const int nSize = (const int) props[i].displayVal.size();

      if(datatype.getPod() == AbcA::kInt32POD && datatype.getExtent() == 1){
         evalStream<<"label lbl"<<name<<" \""<<name<<"\" align:#left fieldWidth:140\n";
         evalStream<<"spinner e"<<name<<" \"\" type:#integer range:[-9999999,9999999,0] align:#left labelOnTop:true\n";
      }
      else if(datatype.getPod() == AbcA::kFloat32POD && datatype.getExtent() == 1){
         evalStream<<"label lbl"<<name<<" \""<<name<<"\" align:#left fieldWidth:140\n";
         evalStream<<"spinner e"<<name<<" \"\" type:#float range:[-9999999,9999999,0]align:#left labelOnTop:true\n";
      }
      else if(datatype.getPod() == AbcA::kFloat32POD && datatype.getExtent() == 3){

         evalStream<<"label lbl"<<name<<" \""<<name<<"\" align:#left fieldWidth:140\n";
         evalStream<<"spinner e"<<name<<"x\"\" across:3 type:#float range:[-9999999,9999999,0] fieldWidth:39 readOnly:true\n";
         evalStream<<"spinner e"<<name<<"y\"\" type:#float range:[-9999999,9999999,0] fieldWidth:39\n";
         evalStream<<"spinner e"<<name<<"z\"\" type:#float range:[-9999999,9999999,0] fieldWidth:39\n";
      }
      else{
         //TODO: better fit for large strings
         evalStream<<"edittext e"<<name<<" \" "<<name<<"\" fieldWidth:140 ";
         if(nSize > 140){
            evalStream<<"height:54";
         }
         evalStream<<" labelOnTop:true";
         if(bConstant) evalStream<<" readOnly:true";
      }

      evalStream<<"\n";
   }

   //evalStream<<"on propAttributePRM1 changed do\n";
   //evalStream<<"("<<"\n";

   //for(int i=0; i<props.size(); i++){
   //   std::string& name = props[i].name;

   //   const AbcA::DataType& datatype = props[i].propHeader.getDataType();
   //   const AbcA::MetaData& metadata = props[i].propHeader.getMetaData();

   //   evalStream<<"e"<<name<<".text = "<<name<<" as string";
   //   evalStream<<"\n";
   //}

   //evalStream<<")"<<"\n";
   evalStream<<")"<<"\n";
   evalStream<<")"<<"\n";


   evalStream<<"custattributes.add "<<nodeName<<".modifiers[\""<<modkey<<"\"] propAttribute baseobject:false"<<"\n";

   

   //ESS_LOG_WARNING(evalStream.str());

   evalStream<<nodeName<<".modifiers[\""<<modkey<<"\"]\n";
   FPValue fpVal;
   ExecuteMAXScriptScript( EC_UTF8_to_TCHAR((char*)evalStream.str().c_str()), 0, &fpVal);
   Modifier* pMod = (Modifier*)fpVal.r;
   return pMod;
}
int AlembicImport_Camera(const std::string& path, AbcG::IObject& iObj,
                         alembic_importoptions& options, INode** pMaxNode)
{
    const std::string& identifier = iObj.getFullName();

    if (!AbcG::ICamera::matches(iObj.getMetaData())) {
        return alembic_failure;
    }
    AbcG::ICamera objCamera = AbcG::ICamera(iObj, Abc::kWrapExisting);
    if (!objCamera.valid()) {
        return alembic_failure;
    }
    bool isConstant = objCamera.getSchema().isConstant();

    TimeValue zero(0);

    INode* pNode = *pMaxNode;
    CameraObject* pCameraObj = NULL;
    if (!pNode) {
        // Create the camera object and place it in the scene
        GenCamera* pGenCameraObj =
            GET_MAX_INTERFACE()->CreateCameraObject(FREE_CAMERA);
        if (pGenCameraObj == NULL) {
            return alembic_failure;
        }
        pGenCameraObj->Enable(TRUE);
        pGenCameraObj->SetConeState(TRUE);
        pGenCameraObj->SetManualClip(TRUE);

        IMultiPassCameraEffect* pCameraEffect =
            pGenCameraObj->GetIMultiPassCameraEffect();
        const int TARGET_DISTANCE = 0;
        pCameraEffect->GetParamBlockByID(0)->SetValue(TARGET_DISTANCE, zero, FALSE);

        pCameraObj = pGenCameraObj;

        Abc::IObject parent = iObj.getParent();
        std::string name = removeXfoSuffix(parent.getName().c_str());
        pNode = GET_MAX_INTERFACE()->CreateObjectNode(
                    pGenCameraObj, EC_UTF8_to_TCHAR(name.c_str()));
        if (pNode == NULL) {
            return alembic_failure;
        }
        *pMaxNode = pNode;
    }
    else {
        Object* obj = pNode->EvalWorldState(zero).obj;

        if (obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0))) {
            pCameraObj = reinterpret_cast<CameraObject*>(
                             obj->ConvertToType(zero, Class_ID(SIMPLE_CAM_CLASS_ID, 0)));
        }
        else if (obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0))) {
            pCameraObj = reinterpret_cast<CameraObject*>(
                             obj->ConvertToType(zero, Class_ID(LOOKAT_CAM_CLASS_ID, 0)));
        }
        else {
            return alembic_failure;
        }
    }

    // Fill in the mesh
    //   alembic_fillcamera_options dataFillOptions;
    //   dataFillOptions.pIObj = &iObj;
    //   dataFillOptions.pCameraObj = pCameraObj;
    //   dataFillOptions.dTicks =  GET_MAX_INTERFACE()->GetTime();
    // AlembicImport_FillInCamera(dataFillOptions);

    // printAnimatables(pCameraObj);

    Interval interval = FOREVER;

    AlembicFloatController* pControl = NULL;
    {
        std::string prop("horizontalFOV");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 0);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 0) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.FOV.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("FocusDistance");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 1, 0, 1);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 1, 0, 1) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName
                    << "mynode2113.MultiPass_Effect.focalDepth.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("NearClippingPlane");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 2);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 2) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.nearclip.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("FarClippingPlane");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 3);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 3) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.farclip.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }

    // if(assignControllerToLevel1SubAnim(createFloatController(path, identifier,
    // std::string("FocusDistance")), pCameraObj, 0, 1) && !isConstant){
    //	AlembicImport_ConnectTimeControl( "$.targetDistance.controller.time",
    // options );
    //}

    createCameraModifier(path, identifier, pNode);

    // Add the new inode to our current scene list
    SceneEntry* pEntry = options.sceneEnumProc.Append(
                             pNode, pCameraObj, OBTYPE_CAMERA, &std::string(iObj.getFullName()));
    options.currentSceneList.Append(pEntry);

    // Set the visibility controller
    AlembicImport_SetupVisControl(path, identifier, iObj, pNode, options);

    importMetadata(pNode, iObj);

    return 0;
}