END_TEST

START_TEST (test_comp_sbase)
{
  SBMLNamespaces sbmlns(3,1,"comp",1);
  Parameter param(&sbmlns);
  CompSBasePlugin* pplugin = static_cast<CompSBasePlugin*>(param.getPlugin("comp"));

  fail_unless(pplugin->getNumReplacedElements()==0);
  ReplacedElement* re = pplugin->createReplacedElement();
  re->setMetaId("re1");
  fail_unless(pplugin->getNumReplacedElements()==1);
  fail_unless(pplugin->addReplacedElement(NULL)==LIBSBML_INVALID_OBJECT);
  ReplacedElement re2(3, 1);
  re2.setMetaId("re2");
  fail_unless(pplugin->addReplacedElement(&re2)==LIBSBML_INVALID_OBJECT);
  re2.setDeletion("ID1");
  fail_unless(pplugin->addReplacedElement(&re2)==LIBSBML_INVALID_OBJECT);
  re2.setSubmodelRef("mod1");
  fail_unless(pplugin->addReplacedElement(&re2)==LIBSBML_OPERATION_SUCCESS);
  ReplacedElement* reref = pplugin->getReplacedElement(1);
  fail_unless(reref != NULL);
  fail_unless(reref->getMetaId()=="re2");
  reref = pplugin->getReplacedElement(0);
  fail_unless(reref != NULL);
  fail_unless(reref->getMetaId()=="re1");
  re->setDeletion("ID1");
  fail_unless(reref->getDeletion()=="ID1");
  fail_unless(pplugin->removeReplacedElement(3)==NULL);
  fail_unless(pplugin->removeReplacedElement(0)==re);
  fail_unless(pplugin->getReplacedElement(1)==NULL);

  fail_unless(pplugin->isSetReplacedBy()==false);
  ReplacedBy* rb = pplugin->createReplacedBy();
  fail_unless(rb != NULL);
  fail_unless(pplugin->isSetReplacedBy()==true);
  fail_unless(pplugin->setReplacedBy(NULL)==LIBSBML_OPERATION_SUCCESS);
  fail_unless(pplugin->isSetReplacedBy()==false);
  ReplacedBy rb2(3,1);
  fail_unless(pplugin->setReplacedBy(&rb2)==LIBSBML_INVALID_OBJECT);
  rb2.setIdRef("ID1");
  fail_unless(pplugin->setReplacedBy(&rb2)==LIBSBML_INVALID_OBJECT);
  rb2.setSubmodelRef("mod1");
  fail_unless(pplugin->setReplacedBy(&rb2)==LIBSBML_OPERATION_SUCCESS);
  fail_unless(pplugin->unsetReplacedBy()==LIBSBML_OPERATION_SUCCESS);
}
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;
    }
  }

  // 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;
}