#!/usr/bin/python
# sbml2mod.py

# Updated: 18/2/21

import sys,io,libsbml,traceback

__doc__="""sbml2mod version 3.1.1.1

Copyright (C) 2005-2010, Darren J Wilkinson
 d.j.wilkinson@ncl.ac.uk
 http://www.staff.ncl.ac.uk/d.j.wilkinson/

Includes modifications by:
  Jeremy Purvis (jep@thefoldingproblem.com)
  Carole Proctor (c.j.proctor@ncl.ac.uk)
  Mark Muldoon (m.muldoon@man.ac.uk)
  Lukas Endler (lukas@ebi.ac.uk)
 
This is GNU Free Software (General Public License)

Module for converting SBML to SBML-shorthand model files, version 3.1.1
Typical usage:
>>> from sbml2mod import Parser
>>> p=Parser(sbmldoc)
>>> p.parseStream(sys.stdout)

Raises error "ParseError" on a fatal parsing error.
"""

ParseError="Parsing error"


class Parser(object):
    """Parser class
Has constructor:
 Parser(sbmldoc)
where sbmldoc is a libsbml sbmldocument object,
and the following public methods:
 parseStream(outStream)
 parse()
"""

    def __init__(self,d):
        self.d=d
        self.m=d.getModel()

    def parseStream(self,outS):
        """parseStream(outStream)
parses SBML model and writes SBML-shorthand to outStream"""
        outS.write('@model:')
        outS.write(str(self.d.getLevel())+'.')
        outS.write(str(self.d.getVersion())+'.')
        self.mangle=10*self.d.getLevel()+self.d.getVersion()
        outS.write('0='+self.m.getId())
        if (self.m.getName()!=""):
            outS.write(' "'+self.m.getName()+'"')
        outS.write('\n')
        # level 3 global units...
        if (self.mangle>=30):
            first=True
            if self.m.isSetSubstanceUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('s='+str(self.m.getSubstanceUnits()))
            if self.m.isSetTimeUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('t='+str(self.m.getTimeUnits()))
            if self.m.isSetVolumeUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('v='+str(self.m.getVolumeUnits()))
            if self.m.isSetAreaUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('a='+str(self.m.getAreaUnits()))
            if self.m.isSetLengthUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('l='+str(self.m.getLengthUnits()))
            if self.m.isSetExtentUnits():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('e='+str(self.m.getExtentUnits()))
            if self.m.isSetConversionFactor():
                if first==True:
                    outS.write(' ')
                    first=False
                else:
                    outS.write(',')
                outS.write('c='+str(self.m.getConversionFactor()))
            if (first==False):
                outS.write('\n')
        n=self.m.getNumUnitDefinitions()
        if (n>0):
            outS.write('@units\n')
            for i in range(n):
                ud=self.m.getUnitDefinition(i)
                outS.write(' '+ud.getId())
                for j in range(ud.getNumUnits()):
                    if (j==0):
                        outS.write('=')
                    else:
                        outS.write('; ')
                    u=ud.getUnit(j)
                    kind=u.getKind()
                    outS.write(libsbml.UnitKind_toString(kind))
                    e=u.getExponent()
                    m=u.getMultiplier()
                    o=u.getOffset()
                    s=u.getScale()
                    first=True
                    if (e!=1):
                        if first==True:
                            outS.write(':')
                            first=False
                        else:
                            outS.write(',')
                        outS.write('e='+str(e))
                    if (m!=1):
                        if first==True:
                            outS.write(':')
                            first=False
                        else:
                            outS.write(',')
                        outS.write('m='+str(m))
                    if (o!=0):
                        if first==True:
                            outS.write(':')
                            first=False
                        else:
                            outS.write(',')
                        outS.write('o='+str(o))
                    if (s!=0):
                        if first==True:
                            outS.write(':')
                            first=False
                        else:
                            outS.write(',')
                        outS.write('s='+str(s))
                if (ud.isSetName()):
                    outS.write(' "'+ud.getName()+'"')
                outS.write('\n')
        if self.m.getNumCompartments():
            outS.write('@compartments\n')
            for c in self.m.getListOfCompartments():
                outS.write(' '+c.getId())
                if (c.isSetOutside()):
                    outS.write('<'+c.getOutside())
                if (c.isSetSize()):
                    outS.write('='+str(c.getSize()))
                if (c.isSetName()):
                    outS.write(' "'+c.getName()+'"')
                outS.write('\n')
        if self.m.getNumSpecies():
            outS.write('@species\n')
            for s in self.m.getListOfSpecies():
                outS.write(' '+s.getCompartment()+':')
                if (s.isSetInitialConcentration()):
                    outS.write('['+s.getId()+']='+str(s.getInitialConcentration()))
                elif (s.isSetInitialAmount()):
                    outS.write(s.getId()+'='+str(s.getInitialAmount()))
                else:
                    outS.write('['+s.getId()+']='+str(s.getInitialConcentration()))
                if (s.getHasOnlySubstanceUnits()):
                    outS.write('s')
                if (s.getBoundaryCondition()):
                    outS.write('b')
                if (s.getConstant()):
                    outS.write('c')
                if (s.isSetName()):
                    outS.write(' "'+s.getName()+'"')
                outS.write('\n')
        if self.m.getNumParameters():
            outS.write('@parameters\n')
            for p in self.m.getListOfParameters():
                outS.write(' '+p.getId()+'=')
                outS.write(str(p.getValue()))
                c=p.getConstant()
                if(c!=1):
                    outS.write('v')
                if (p.isSetName()):
                    outS.write(' "'+p.getName()+'"')
                outS.write('\n')
        if self.m.getNumRules():
            outS.write('@rules\n')
            for r in self.m.getListOfRules():
                if (r.isAssignment()):
                    outS.write(' ' + r.getVariable() + ' = ' + r.getFormula())
                    outS.write('\n')
                elif (r.isRate()):
                    outS.write(' @rate:' + r.getVariable() + ' = ' + r.getFormula())
                    outS.write('\n')
                else:
                    # probably an algebraic rule!
                    sys.stderr.write("Unsupported rule type\n")
                    raise(ParseError)
        if self.m.getNumReactions():
            outS.write('@reactions\n')
            for r in self.m.getListOfReactions():
                if (r.getReversible()):
                    outS.write('@rr=')
                else:
                    outS.write('@r=')
                outS.write(r.getId())
                if (r.isSetName()):
                    outS.write(' "'+r.getName()+'"')
                outS.write('\n ')
                for j in range(r.getNumReactants()):
                    sr=r.getReactant(j)
                    if (j>0):
                        outS.write('+')
                    sto=sr.getStoichiometry()
                    if (sto!=1):
                        outS.write(str(int(sto)))
                    outS.write(sr.getSpecies())
                outS.write(' -> ')
                for j in range(r.getNumProducts()):
                    sr=r.getProduct(j)
                    if (j>0):
                        outS.write('+')
                    sto=sr.getStoichiometry()
                    if (sto!=1):
                        outS.write(str(int(sto)))
                    outS.write(sr.getSpecies())
                for j in range(r.getNumModifiers()):
                    sr=r.getModifier(j)
                    if (j==0):
                        outS.write(' : ')
                        outS.write(sr.getSpecies())
                    else:
                        outS.write(', ')
                        outS.write(sr.getSpecies())
                outS.write('\n')
                if (r.isSetKineticLaw()):
                    kl=r.getKineticLaw()
                    outS.write(' '+kl.getFormula())
                    for k in range(kl.getNumParameters()):
                        if (k==0):
                            outS.write(' : ')
                        else:
                            outS.write(', ')
                        p=kl.getParameter(k)
                        outS.write(p.getId()+'='+str(p.getValue()))
                    outS.write('\n')
        if self.m.getNumEvents():
            outS.write('@events\n')
            for e in self.m.getListOfEvents():
                outS.write(" "+e.getId()+"= ")
                trig=e.getTrigger()
                trigger=libsbml.formulaToString(trig.getMath())
                outS.write(trigger)
                if e.isSetDelay():
                    outS.write(' ; ')
                    dmath=e.getDelay()
                    delay=libsbml.formulaToString(dmath.getMath())
                    outS.write(delay)
                outS.write(' : ')
                nea=e.getNumEventAssignments()
                for eai in range(nea):
                    if (eai>0):
                        if (self.mangle>=23):
                            outS.write('; ')
                        else:
                            outS.write(', ')
                    ea=e.getEventAssignment(eai)
                    outS.write(ea.getVariable()+"=")
                    m=ea.getMath()
                    mf=libsbml.formulaToString(m)
                    outS.write(mf)
                if e.isSetName():
                    outS.write(' "'+e.getName()+'"')
                outS.write("\n")


    def parse(self):
        """parse()
Parses the SBML model and returns a string containing the
corresponding SBML-shorthand"""
        outS=io.StringIO()
        self.parseStream(outS)
        return outS.getvalue()

def main():
    argc=len(sys.argv)
    try:
        if (argc==1):
            s=sys.stdin
        else:
            try:
                s=open(sys.argv[1],"r")
            except:
                sys.stderr.write('Error: failed to open file: ')
                sys.stderr.write(sys.argv[1]+'\n')
                sys.exit(1)
        string=s.read()
        r=libsbml.SBMLReader()
        d=r.readSBMLFromString(string)
        # print d
        p=Parser(d)
        p.parseStream(sys.stdout)
    except:
        traceback.print_exc(file=sys.stderr)
        sys.stderr.write('\n\n Unknown parsing error!\n')
        sys.exit(1)


if __name__=='__main__':
    main()
        
# eof

