/*
 * Logs a constraint failure to the validator for the given SBML object.
 * The parameter message is used instead of the constraint's member
 * variable msg.
 */
void
VConstraint::logFailure (const SBase& object, const std::string& message)
{
  std::string pkg = object.getPackageName();
  unsigned int pkgVersion = object.getPackageVersion();
  if (mId > 99999 && pkg == "core")
  {
    // we are dealing with a core object that is logging errors 
    // relating to a package
    // need to work out which pkg

    unsigned int offset = (unsigned int)(floor((double)mId/100000.0)) * 100000;

    if (offset == 9900000)
    {
      // we are dealing with the strict units validator
      mId = mId - offset;
    }
    else
    {
      // it is possible that the object does not have a direct plugin
      // it may the child of an object that does
      // so lets cut straight to the parent document
      const SBMLDocument * doc = object.getSBMLDocument();
      if (doc != NULL)
      {
        for (unsigned int i = 0; i < doc->getNumPlugins(); i++)
        {
          const SBMLExtension * ext = doc->getPlugin(i)->getSBMLExtension();

          if (ext->getErrorIdOffset() == offset)
          {
            pkg = doc->getPlugin(i)->getPackageName();
            pkgVersion = doc->getPlugin(i)->getPackageVersion();
            break;
          }
        }
      }
    }
  }

  SBMLError error = SBMLError( mId, object.getLevel(), object.getVersion(),
			       message, object.getLine(), object.getColumn(),
             LIBSBML_SEV_ERROR, LIBSBML_CAT_SBML, pkg, pkgVersion);

  if (error.getSeverity() != LIBSBML_SEV_NOT_APPLICABLE)
    mValidator.logFailure(error);

/*    ( SBMLError( mId, object.getLevel(), object.getVersion(),
                 message, object.getLine(), object.getColumn(),
                 LIBSBML_SEV_ERROR, LIBSBML_CAT_SBML ));*/

}
LIBSBML_EXTERN
void 
copySBaseAttributes(const SBase& source,SBase& target)
{
    target.setMetaId(source.getMetaId());
//    target.setId(source.getId());
//    target.setName(source.getName());
    target.setSBMLDocument(const_cast<SBMLDocument*>(source.getSBMLDocument()));
    target.setSBOTerm(source.getSBOTerm());
    if(source.isSetAnnotation())
    {
      target.setAnnotation(new XMLNode(*const_cast<SBase&>(source).getAnnotation()));
    }
    if(source.isSetNotes())
    {
      target.setNotes(new XMLNode(*const_cast<SBase&>(source).getNotes()));
    }
    if (source.getSBMLNamespaces())
    {
      target.setSBMLNamespaces(source.getSBMLNamespaces());
    }
    List* pCVTerms=target.getCVTerms();
    // first delete all the old CVTerms
    if(pCVTerms)
    {
      while(pCVTerms->getSize()>0)
      {
        CVTerm* object=static_cast<CVTerm*>(pCVTerms->remove(0));
        delete object;
      }
      // add the cloned CVTerms from source
      if(source.getCVTerms()!=NULL)
      {
          unsigned int i=0,iMax=source.getCVTerms()->getSize();
          while(i<iMax)
          {
              target.addCVTerm(static_cast<CVTerm*>(static_cast<CVTerm*>(source.getCVTerms()->get(i))->clone()));
              ++i;
          }
      }
    }
}
int 
Submodel::instantiate()
{
  SBMLDocument* doc = getSBMLDocument();
  SBMLDocument* rootdoc = doc;
  if (doc==NULL) 
  {
    return LIBSBML_OPERATION_FAILED;
  }

  CompSBMLDocumentPlugin* docplugin = 
    static_cast<CompSBMLDocumentPlugin*>(doc->getPlugin(getPrefix()));
  if (docplugin==NULL)
  {
    return LIBSBML_OPERATION_FAILED;
  }

  SBase* parent  = getParentSBMLObject();
  string parentmodelname = "";
  string parentURI = "";
  set<string> uniqueModels;
  while (parent != NULL && parent->getTypeCode() != SBML_DOCUMENT) {
    if (parent->getTypeCode() == SBML_COMP_SUBMODEL) {
      const Submodel* parentsub = static_cast<const Submodel*>(parent);
      uniqueModels.insert(parentsub->mInstantiationOriginalURI + "::" + parentsub->getModelRef());
      if (parentURI=="") {
        parentURI=parentsub->mInstantiationOriginalURI;
      }
    }
    if (parent->getTypeCode() == SBML_MODEL ||
      parent->getTypeCode() == SBML_COMP_MODELDEFINITION)
    {
      if (parentmodelname == "") {
        parentmodelname = parent->getId();
      }
    }
    rootdoc = parent->getSBMLDocument();
    parent = parent->getParentSBMLObject();
  }

  if (mInstantiatedModel != NULL) 
  {
    delete mInstantiatedModel;
    mInstantiatedModel = NULL;
    mInstantiationOriginalURI.clear();
  }

  if (!hasRequiredAttributes()) {
    string error = "Instantiation error in Submodel::instantiate:  ";
    if (!isSetId()) {
      error += "A submodel in model '" + getParentModel(this)->getId() + "' does not have an 'id' attribute.";
    }
    else if (!isSetModelRef()) {
      error += "The submodel '" + getId() + "' does not have a 'modelRef' attribute.";
    }
    rootdoc->getErrorLog()->logPackageError("comp", CompSubmodelAllowedAttributes, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
    return LIBSBML_INVALID_OBJECT;
  }

  SBase* origmodel = docplugin->getModel(getModelRef());
  
  if (origmodel==NULL) {
    string error = "In Submodel::instantiate, unable to instantiate submodel '" + getId() + "' because the referenced model ('" + getModelRef() +"') does not exist.";
    rootdoc->getErrorLog()->logPackageError("comp", CompSubmodelMustReferenceModel, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
    return LIBSBML_INVALID_OBJECT;
  }
  ExternalModelDefinition* extmod;
  SBMLDocument* origdoc = NULL;
  string newmodel = parentURI + "::" + getModelRef();
  
  set<pair<string, string> > parents;
  switch(origmodel->getTypeCode()) 
  {
  case SBML_MODEL:
  case SBML_COMP_MODELDEFINITION:
    origdoc = origmodel->getSBMLDocument();
    mInstantiatedModel = static_cast<Model*>(origmodel)->clone();
    if (uniqueModels.insert(newmodel).second == false) {
      //Can't instantiate this model, because we are already a child of it.
      string error = "Error in Submodel::instantiate:  cannot instantiate submodel '" + getId() + "' in model '" + parentmodelname + "' because it references the model '" + getModelRef() + "', which is already an ancestor of the submodel.";
      rootdoc->getErrorLog()->logPackageError("comp", CompSubmodelCannotReferenceSelf, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
      return LIBSBML_OPERATION_FAILED;
    }
    mInstantiationOriginalURI = parentURI;
    break;
  case SBML_COMP_EXTERNALMODELDEFINITION:
    extmod = static_cast<ExternalModelDefinition*>(origmodel);
    if (extmod==NULL) 
    {
      //No error message:  it should be impossible, if origmodel has the type code 'external model definition', for it to not be castable to an external model definition.
      mInstantiatedModel = NULL;
      mInstantiationOriginalURI = "";
      return LIBSBML_OPERATION_FAILED;
    }
    mInstantiatedModel = extmod->getReferencedModel(rootdoc, parents);
    if (mInstantiatedModel == NULL) 
    {
      string error = "In Submodel::instantiate, unable to instantiate submodel '" + getId() + "' because the external model definition it referenced (model '" + getModelRef() +"') could not be resolved.";
      rootdoc->getErrorLog()->logPackageError("comp", CompSubmodelMustReferenceModel, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
      mInstantiationOriginalURI = "";
      return LIBSBML_OPERATION_FAILED;
    }
    mInstantiationOriginalURI = extmod->getSource();
    origdoc = mInstantiatedModel->getSBMLDocument();
    newmodel = extmod->getSource() + "::" + getModelRef();
    if (uniqueModels.insert(newmodel).second == false) {
      //Can't instantiate this model, because we are already a child of it.
      string error = "Error in Submodel::instantiate:  cannot instantiate submodel '" + getId() + "' in model '" + parentmodelname + "' because it references the model '" + getModelRef() + "', which is already an ancestor of the submodel.";
      rootdoc->getErrorLog()->logPackageError("comp", CompSubmodelCannotReferenceSelf, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
      mInstantiatedModel = NULL;
      mInstantiationOriginalURI = "";
      return LIBSBML_OPERATION_FAILED;
    }
    mInstantiatedModel = mInstantiatedModel->clone();
    mInstantiationOriginalURI = extmod->getSource();
    break;
  default:
    //Should always be one of the above, unless someone extends one of the above and doesn't tell us.
    string error = "Instantiation error in Submodel::instantiate:  unable to parse the model '" + origmodel->getId() + "', as it was not of the type 'model' 'modelDefinition', or 'externalModelDefinition'.  The most likely cause of this situation is if some other package extended one of those three types, but the submodel code was not updated.";
    rootdoc->getErrorLog()->logPackageError("comp", CompUnresolvedReference, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
    mInstantiatedModel = NULL;
    mInstantiationOriginalURI = "";
    return LIBSBML_OPERATION_FAILED;
  }
  
  if (mInstantiatedModel==NULL) 
  {
    string error = "Instantiation error in Submodel::instantiate:  unable to create a valid copy of model '" + getModelRef() + "'.";
    rootdoc->getErrorLog()->logPackageError("comp", CompModelFlatteningFailed, getPackageVersion(), getLevel(), getVersion(), error, getLine(), getColumn());
    mInstantiationOriginalURI = "";
    return LIBSBML_OPERATION_FAILED;
  }

  mInstantiatedModel->connectToParent(this);
  mInstantiatedModel->setSBMLDocument(origdoc);
  mInstantiatedModel->enablePackage(getPackageURI(), getPrefix(), true);
  CompModelPlugin* instmodplug = 
    static_cast<CompModelPlugin*>(mInstantiatedModel->getPlugin(getPrefix()));
  if (instmodplug == NULL)
  {
    mInstantiatedModel->enablePackageInternal(getPackageURI(), getPrefix(), true);
  }

  // call all registered callbacks
  std::vector<ModelProcessingCallbackData*>::iterator it = mProcessingCBs.begin();
  while(it != mProcessingCBs.end())
  {
    ModelProcessingCallbackData* current = *it;
    int result = current->cb(mInstantiatedModel, rootdoc->getErrorLog(), current->data);
    if (result != LIBSBML_OPERATION_SUCCESS)
      return result;
    ++it;
  }

  
  CompModelPlugin* origmodplug = 
    static_cast<CompModelPlugin*>(rootdoc->getModel()->getPlugin(getPrefix()));

  instmodplug = 
    static_cast<CompModelPlugin*>(mInstantiatedModel->getPlugin(getPrefix()));
  
  if (instmodplug == NULL)
    return LIBSBML_OPERATION_SUCCESS;

  // if we have a transformer specified, then we need to propagate it, so it can
  // be used
  if (origmodplug->isSetTransformer())
  {
    if (instmodplug != NULL)
      instmodplug->setTransformer(origmodplug->getTransformer());
  }

  
  for (unsigned int sub=0; sub<instmodplug->getNumSubmodels(); sub++) 
  {
    Submodel* instsub = instmodplug->getSubmodel(sub);
    int ret = instsub->instantiate();
    if (ret != LIBSBML_OPERATION_SUCCESS) {
      //'instantiate' already sets its own error messages.
      delete mInstantiatedModel;
      mInstantiatedModel = NULL;
      mInstantiationOriginalURI = "";
      return ret;
    }
  }

  return LIBSBML_OPERATION_SUCCESS;
}