/*
 * Copy constructor. Creates a copy of this SpeciesReference.
 */
SpeciesReference::SpeciesReference (const SpeciesReference& orig) :
   SimpleSpeciesReference( orig                )
 , mStoichiometryMath    ( NULL                   )
{
  if (&orig == NULL)
  {
    throw SBMLConstructorException("Null argument to copy constructor");
  }
  else
  {
    mStoichiometry = orig.mStoichiometry ;
    mDenominator = orig.mDenominator   ;
    mConstant = orig.mConstant;
    mIsSetConstant = orig.mIsSetConstant;
    mIsSetStoichiometry = orig.mIsSetStoichiometry;
    mExplicitlySetStoichiometry = orig.mExplicitlySetStoichiometry;
    mExplicitlySetDenominator = orig.mExplicitlySetDenominator;

    if (orig.mStoichiometryMath != NULL)
    {
      mStoichiometryMath = new StoichiometryMath(*orig.getStoichiometryMath());
    mStoichiometryMath->connectToParent(this);
    }
  }
}
END_TEST


START_TEST ( test_StoichiometryMath_parent_add )
{
    StoichiometryMath *m = new StoichiometryMath(2, 4);
    SpeciesReference *sr = new SpeciesReference(2, 4);

    sr->setStoichiometryMath(m);

    delete m;

    fail_unless(sr == sr->getStoichiometryMath()->getParentSBMLObject());

    delete sr;
}
void checkSpeciesRefs(Model* model, ListOfSpeciesReferences* losr, set<string>& components, set<string>& tests,  const map<string, vector<double> >& results)
{
  for (unsigned int rp=0; rp<losr->size(); rp++) {
    SpeciesReference* sr = static_cast<SpeciesReference*>(losr->get(rp));
    if (sr->isSetStoichiometry() && sr->getStoichiometry() != 1) {
      tests.insert("NonUnityStoichiometry");
    }
    if (sr->isSetStoichiometryMath()) {
      tests.insert("NonUnityStoichiometry");
      components.insert("StoichiometryMath");
      if (variesIn(sr->getStoichiometryMath()->getMath(), model, results)) {
        tests.insert("AssignedVariableStoichiometry");
      }
      else {
        tests.insert("AssignedConstantStoichiometry");
      }
    }
    else if (sr->isSetId()) {
      double initialResult = 1;
      if (results.find(sr->getId()) != results.end()) {
        tests.insert("SpeciesReferenceOutput");
      }
      if (variesIn(sr->getId(), model, results)) {
        tests.insert("AssignedVariableStoichiometry");
        tests.insert("NonUnityStoichiometry");
      }
      else if (initialOverriddenIn(sr->getId(), model, results, tests)) {
        tests.insert("AssignedConstantStoichiometry");
        if (getInitialResultFor(sr->getId(), results, initialResult)) {
          if (initialResult != 1) {
            tests.insert("NonUnityStoichiometry");
          }
        }
        else {
          //Don't know what the actual initial result is, so we'll assume it's not 1.0.
          tests.insert("NonUnityStoichiometry");
        }
      }
      if (initialOverriddenIn(sr->getId(), model, results, tests)) {
        tests.insert("InitialValueReassigned");
      }
      if (foundInMath(sr->getId(), model)) {
        tests.insert("SpeciesReferenceInMath");
      }
    }
  }
}
/*
 * Copy constructor. Creates a copy of this SpeciesReference.
 */
SpeciesReference::SpeciesReference (const SpeciesReference& orig)
 : SimpleSpeciesReference( orig                )
 , mStoichiometry        ( orig.mStoichiometry )
 , mDenominator          ( orig.mDenominator   )
 , mStoichiometryMath    ( NULL                )
 , mConstant             ( orig.mConstant)
 , mIsSetConstant        ( orig.mIsSetConstant)
 , mIsSetStoichiometry   ( orig.mIsSetStoichiometry)
 , mExplicitlySetStoichiometry ( orig.mExplicitlySetStoichiometry)
 , mExplicitlySetDenominator   ( orig.mExplicitlySetDenominator)
{
  if (orig.mStoichiometryMath != NULL)
  {
    mStoichiometryMath = new StoichiometryMath(*orig.getStoichiometryMath());
    mStoichiometryMath->connectToParent(this);
  }
}
void
Model::convertStoichiometryMath()
{
  unsigned int n, j;
  Reaction * r;
  SpeciesReference *sr;
  unsigned int idCount = 0;
  char newid[15];
  std::string id;

  for (n = 0; n < getNumReactions(); n++)
  {
    r = getReaction(n);
    for (j = 0; j < r->getNumReactants(); j++)
    {
      sr = r->getReactant(j);
      if (sr->isSetStoichiometryMath())
      {
        if (!sr->isSetId())
        {
          sprintf(newid, "generatedId_%u", idCount);
          id.assign(newid);
          sr->setId(id);
          idCount++;
        }
        else
        {
          id = sr->getId();
        }
        sr->setConstant(false);

        AssignmentRule * ar = createAssignmentRule();
        ar->setVariable(id);
        if (sr->getStoichiometryMath()->isSetMath())
        {
          ar->setMath(sr->getStoichiometryMath()->getMath());
        }
      }
    }
    for (j = 0; j < r->getNumProducts(); j++)
    {
      sr = r->getProduct(j);
      if (sr->isSetStoichiometryMath())
      {
        if (!sr->isSetId())
        {
          sprintf(newid, "generatedId_%u", idCount);
          id.assign(newid);
          sr->setId(id);
          idCount++;
        }
        else
        {
          id = sr->getId();
        }

        sr->setConstant(false);
        AssignmentRule * ar = createAssignmentRule();
        ar->setVariable(id);
        if (sr->getStoichiometryMath()->isSetMath())
        {
          ar->setMath(sr->getStoichiometryMath()->getMath());
        }
      }
    }
  }
}
void dealWithAssigningL1Stoichiometry(Model & m, bool l2)
{
  //char newid[15];
  std::string id;

  for (unsigned int i = 0; i < m.getNumReactions(); i++)
  {
    Reaction *r = m.getReaction(i);
    unsigned int j;

    for (j = 0; j < r->getNumReactants(); j++)
    {
      SpeciesReference *sr = r->getReactant(j);
      // we do not get here unless the stoichiometryMath is an integer
      // or a rational 
      if (l2 == true && sr->isSetStoichiometryMath() == true)
      {
        const ASTNode* ast = sr->getStoichiometryMath()->getMath();
        if (ast->isInteger())
        {
          int num = ast->getInteger();
          sr->setStoichiometry(num);
          sr->setDenominator(1);
        }
        else
        {
          int num = ast->getNumerator();
          int denom = ast->getDenominator();
          sr->setStoichiometry(num);
          sr->setDenominator(denom);
        }
        sr->unsetStoichiometryMath();
      }
      else
      {
        sr->setStoichiometry(sr->getStoichiometry());
        sr->setDenominator(1);
      }
    }
    for (j = 0; j < r->getNumProducts(); j++)
    {
      SpeciesReference *sr = r->getProduct(j);
      // we do not get here unless the stoichiometryMath is an integer
      // or a rational 
      if (l2 == true && sr->isSetStoichiometryMath() == true)
      {
        const ASTNode* ast = sr->getStoichiometryMath()->getMath();
        if (ast->isInteger())
        {
          int num = ast->getInteger();
          sr->setStoichiometry(num);
          sr->setDenominator(1);
        }
        else
        {
          int num = ast->getNumerator();
          int denom = ast->getDenominator();
          sr->setStoichiometry(num);
          sr->setDenominator(denom);
        }
        sr->unsetStoichiometryMath();
      }
      else
      {
        sr->setStoichiometry(sr->getStoichiometry());
        sr->setDenominator(1);
      }
    }
  }
}
LIBSBML_CPP_NAMESPACE_USE

int
main (int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << endl << "Usage: printUnits filename" << endl << endl;
        return 1;
    }

    const char* filename   = argv[1];
    SBMLDocument* document = readSBML(filename);

    if (document->getNumErrors() > 0)
    {
        cerr << "Encountered the following SBML errors:" << endl;
        document->printErrors(cerr);
        return 1;
    }

    Model* model = document->getModel();

    if (model == 0)
    {
        cout << "No model present." << endl;
        return 1;
    }

    unsigned int i,j;
    for (i = 0; i < model->getNumSpecies(); i++)
    {
        Species* s = model->getSpecies(i);
        cout << "Species " << i << ": "
             << UnitDefinition::printUnits(s->getDerivedUnitDefinition()) << endl;
    }

    for (i = 0; i < model->getNumCompartments(); i++)
    {
        Compartment *c = model->getCompartment(i);
        cout << "Compartment " << i << ": "
             << UnitDefinition::printUnits(c->getDerivedUnitDefinition())
             << endl;
    }

    for (i = 0; i < model->getNumParameters(); i++)
    {
        Parameter *p = model->getParameter(i);
        cout << "Parameter " << i << ": "
             << UnitDefinition::printUnits(p->getDerivedUnitDefinition())
             << endl;
    }


    for (i = 0; i < model->getNumInitialAssignments(); i++)
    {
        InitialAssignment *ia = model->getInitialAssignment(i);
        cout << "InitialAssignment " << i << ": "
             << UnitDefinition::printUnits(ia->getDerivedUnitDefinition()) << endl;
        cout << "        undeclared units: ";
        cout << (ia->containsUndeclaredUnits() ? "yes\n" : "no\n");
    }

    for (i = 0; i < model->getNumEvents(); i++)
    {
        Event *e = model->getEvent(i);
        cout << "Event " << i << ": " << endl;

        if (e->isSetDelay())
        {
            cout << "Delay: "
                 << UnitDefinition::printUnits(e->getDelay()->getDerivedUnitDefinition()) << endl;
            cout << "        undeclared units: ";
            cout << (e->getDelay()->containsUndeclaredUnits() ? "yes\n" : "no\n");
        }

        for (j = 0; j < e->getNumEventAssignments(); j++)
        {
            EventAssignment *ea = e->getEventAssignment(j);
            cout << "EventAssignment " << j << ": "
                 << UnitDefinition::printUnits(ea->getDerivedUnitDefinition()) << endl;
            cout << "        undeclared units: ";
            cout << (ea->containsUndeclaredUnits() ? "yes\n" : "no\n");
        }
    }

    for (i = 0; i < model->getNumReactions(); i++)
    {
        Reaction *r = model->getReaction(i);

        cout << "Reaction " << i << ": " << endl;

        if (r->isSetKineticLaw())
        {
            cout << "Kinetic Law: "
                 << UnitDefinition::printUnits(r->getKineticLaw()->getDerivedUnitDefinition()) << endl;
            cout << "        undeclared units: ";
            cout << (r->getKineticLaw()->containsUndeclaredUnits() ? "yes\n" : "no\n");
        }

        for (j = 0; j < r->getNumReactants(); j++)
        {
            SpeciesReference *sr = r->getReactant(j);

            if (sr->isSetStoichiometryMath())
            {
                cout << "Reactant stoichiometryMath" << j << ": "
                     << UnitDefinition::printUnits(sr->getStoichiometryMath()->getDerivedUnitDefinition()) << endl;
                cout << "        undeclared units: ";
                cout << (sr->getStoichiometryMath()->containsUndeclaredUnits() ? "yes\n" : "no\n");
            }
        }

        for (j = 0; j < r->getNumProducts(); j++)
        {
            SpeciesReference *sr = r->getProduct(j);

            if (sr->isSetStoichiometryMath())
            {
                cout << "Product stoichiometryMath" << j << ": "
                     << UnitDefinition::printUnits(sr->getStoichiometryMath()->getDerivedUnitDefinition()) << endl;
                cout << "        undeclared units: ";
                cout << (sr->getStoichiometryMath()->containsUndeclaredUnits() ? "yes\n" : "no\n");
            }
        }
    }

    for (i = 0; i < model->getNumRules(); i++)
    {
        Rule *r = model->getRule(i);
        cout << "Rule " << i << ": "
             << UnitDefinition::printUnits(r->getDerivedUnitDefinition()) << endl;
        cout << "        undeclared units: ";
        cout << (r->containsUndeclaredUnits() ? "yes\n" : "no\n");
    }

    delete document;
    return 0;
}