END_TEST

START_TEST(test_FbcExtension_convert_and_write)
{
  string file(TestDataDirectory);
  file += "/fbc_ga_example.xml";

  SBMLDocument* document = readSBMLFromFile(file.c_str());
  document->printErrors();
  fail_unless(document->getNumErrors(LIBSBML_SEV_ERROR) == 0);
  fail_unless(document->getModel() != NULL);

  // convert to v2
  {
    ConversionProperties props;
    props.addOption("convert fbc v1 to fbc v2", true);
    props.addOption("strict", true);

    int result = document->convert(props);

    // ensure that all is well with the model
    fail_unless(result == LIBSBML_OPERATION_SUCCESS);
    fail_unless(document->getLevel() == 3);
    fail_unless(document->getVersion() == 1);
    fail_unless(document->getPlugin("fbc") != NULL);
    fail_unless(document->getPlugin("fbc")->getPackageVersion() == 2);
  }

  // ensure that all the v1 stuff is no longer there
  FbcModelPlugin* mplug = dynamic_cast<FbcModelPlugin*>(
    document->getModel()->getPlugin("fbc"));

  fail_unless(mplug != NULL);

  fail_unless(mplug->isSetStrict());
  fail_unless(mplug->getNumGeneAssociations() == 0);
  fail_unless(mplug->getNumFluxBounds() == 0);


  delete document;
}
Model* CompModelPlugin::flattenModel() const
{
  //First make a copy of our parent (the model to be flattened):
  const Model* parent = static_cast<const Model*>(getParentSBMLObject());
  
  if (parent==NULL) {
    return NULL;
  }
  //doc needs to be non-const so that the error messages can be updated.  Otherwise, nothing changes.
  SBMLDocument* doc = const_cast<SBMLDocument*>(getSBMLDocument());
  if (doc==NULL) {
    return NULL;
  }

  //Set the original document so that it can find the model definitions 
  //and external model definitions while we flatten.
  Model* flat = parent->clone();
  flat->setSBMLDocument(doc);
  CompModelPlugin* flatplug = 
    static_cast<CompModelPlugin*>(flat->getPlugin(getPrefix()));

  // Now instantiate its submodels and 
  // follow all renaming/deletion/replacement rules.
  vector<const Model*> submods;
  int success = flatplug->instantiateSubmodels();

  if (success != LIBSBML_OPERATION_SUCCESS) {
    //instantiateSubmodels sets its own error messages.
    delete flat;
    return NULL;
  }

  //Now start the aggregation process.  
  //This goes from the bottom up, calling 'appendFrom' iteratively 
  //(from the plugin).
  for (unsigned int sm=0; sm<flatplug->getNumSubmodels(); sm++) 
  {
    Model* submodel = flatplug->getSubmodel(sm)->getInstantiation();
    if (submodel==NULL) {
      //getInstantiation should be calling a cached value by now, but if not, it will set its own error messages.
      delete flat;
      return NULL;
    }
    CompModelPlugin* submodplug = 
      static_cast<CompModelPlugin*>(submodel->getPlugin(getPrefix()));

    if (submodplug != NULL) {
      //Strip the ports from the submodel, as we no longer need them.
      while (submodplug->getNumPorts() > 0) 
      {
        delete submodplug->removePort(0);
      }
    }
    success = flat->appendFrom(submodel);
    if (success != LIBSBML_OPERATION_SUCCESS) {
      string error = "Unable to flatten model in CompModelPlugin::flattenModel: appending elements from the submodel '" + submodel->getId() + "' to the elements of the parent model failed.";
      doc->getErrorLog()->logPackageError("comp", CompModelFlatteningFailed, getPackageVersion(), getLevel(), getVersion(), error);
      delete flat;
      return NULL;
    }
#ifdef LIBSBML_HAS_PACKAGE_FBC
    // for an fbc v2 model we need to check that the model element 
    // in the flat document has the fbc:strict attribute
    // note this can happen if the parent document did not have fbc but
    // included a submodel from an external document that did
    if (SBMLExtensionRegistry::isPackageEnabled("fbc"))
    {
      FbcModelPlugin *mplugin = static_cast<FbcModelPlugin*>
        (flat->getPlugin("fbc"));
      if (mplugin != NULL && mplugin->getPackageVersion() == 2
        && mplugin->isSetStrict() == false)
      {
        mplugin->setStrict(false);
      }
    }
#endif // LIBSBML_HAS_PACKAGE_FBC

  }

  // Now we clear the saved referenced elements in the local Port objects, 
  // but point them to the new object if necessary.
  flatplug->resetPorts();

  // Next, strip the package info from 'flat'.  
  // We're going to remove everything but the Ports:
  flatplug->mListOfSubmodels.clear();
  flatplug->clearReplacedElements();
  flatplug->unsetReplacedBy();
  
  List* allelements = flat->getAllElements();
  
  vector<SBase*> nonReplacedElements;
  
  for (unsigned int el=0; el<allelements->getSize(); el++) 
  {
    SBase* element = static_cast<SBase*>(allelements->get(el));
    int type = element->getTypeCode();
    if (!(type==SBML_COMP_REPLACEDBY ||
          type==SBML_COMP_REPLACEDELEMENT ||
          type==SBML_COMP_SBASEREF)) 
    {
            nonReplacedElements.push_back(element);
    }
  }

  // delete the list
  delete allelements;

  for (unsigned int el=0; el<nonReplacedElements.size(); el++) 
  {
    SBase* element = nonReplacedElements[el];
    CompSBasePlugin* elplug = 
      static_cast<CompSBasePlugin*>(element->getPlugin(getPrefix()));
    if (elplug != NULL) 
    {
      elplug->clearReplacedElements();
      elplug->unsetReplacedBy();
    }
  }



  //Finally, unset the document again.
  flat->setSBMLDocument(NULL);

  return flat;
}