"""
PySCeS - Python Simulator for Cellular Systems (http://pysces.sourceforge.net)

Copyright (C) 2004-2014 B.G. Olivier, J.M. Rohwer, J.-H.S Hofmeyr all rights reserved,

Brett G. Olivier (bgoli@users.sourceforge.net)
Triple-J Group for Molecular Cell Physiology
Stellenbosch University, South Africa.

Permission to use, modify, and distribute this software is given under the
terms of the PySceS (BSD style) license. See LICENSE.txt that came with
this distribution for specifics.

NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
Brett G. Olivier
"""

from version import __version__

import os, copy, re, time
from getpass import getuser
from xml.etree import ElementTree
import StringIO
import itertools

CurrentDirectory = os.getcwd() # temporary thing

# this is a PySCeS specific hack
from pysces import output_dir as CurrentDirectory

from InfixParser import MyInfixParser
InfixParser = MyInfixParser()
InfixParser.buildlexer()
InfixParser.buildparser(debug=0, debugfile=os.path.join(CurrentDirectory, 'infix.dbg'),
	tabmodule=os.path.join(CurrentDirectory, 'infix_tabmodule'))
InfixParser.setNameStr('self.', '()')

#print 'CurrentDirectory', CurrentDirectory

class CoreToPsc(object):
    core = None
    name = ''
    header_block = ''
    fixed_block = ''
    compartment_block = ''
    function_block = ''
    reaction_block = ''
    species_block = ''
    parameter_block = ''
    rrule_block = ''
    arule_block = ''
    event_block = ''
    notes_block = ''
    SPECIES_CURRENT_VALUE = False
    FIXED_SPECIES_CURRENT_VALUE = False
    PARAMETER_CURRENT_VALUE = False
    time = None
    pw_symbols = None
    __DEBUG__ = False


    # future
    compartment_block = ''

    def __init__(self, core):
        self.core = core
        self.name = self.core.name.replace('.psc','')
        self.name = self.core.name.replace('.xml','')
        self.pw_symbols = [pw.name for pw in self.core.piecewise_functions]

    def setHeader(self, s=''):
        out = '# Generated by PySCeS %s (%s)\n' % (__version__,time.strftime("20%y-%m-%d %H:%M"))
        if len(s) > 0:
            out += '\n# '
        ccount = 0
        for c in range(len(s)):
            ccount += 1
            if ccount >= 60 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                out += s[c]+'\n# '
                ccount = 0
            else:
                out += s[c]
        out += ' \n# Keywords\n'
        kk = self.core.__KeyWords__.keys()
        kk.sort()
        for key in kk:
            if self.core.__KeyWords__[key] != None:
                if key == 'ModelType':
                    pass #TODO
                    ##  out += '%s: ' % (key)
                    ##  for mt in range(len(self.core.__KeyWords__[key])):
                        ##  out += '%s,' % (self.core.__KeyWords__[key][mt])
                    ##  out = out[:-1] + '\n'
                else:
                    out += '%s: %s\n' % (key, self.core.__KeyWords__[key])
        out += ' \n# GlobalUnitDefinitions\n'
        for key in self.core.__uDict__.keys():
            if key in ['substance','volume','time','length','area']:
                k1, k2 = key[0].upper(), key[1:]
                k = k1 + k2
                v = self.core.__uDict__[key]
                out += 'Unit%s: %s, %s, %s, %s\n' % (k, v['kind'], v['multiplier'],\
                                                       v['scale'], v['exponent'])
        out += ' \n'
        self.header_block = out

    def setFixed(self):
        out = ''
        fcnt = 0
        for f in self.core.species:
            if f.fixed:
                fcnt += 1
                out += '%s ' % f.name
        if fcnt > 0:
            out = 'FIX: ' + out
            out += '\n \n'
        self.fixed_block = out

    def setCompartments(self):
        out = ''
        if len(self.core.compartments) > 0:
            out += '# Compartments\n'
            for c in self.core.compartments:
                ra = ''
                if c.area != None:
                    ra = ', '+ c.area
                out += 'Compartment: %s, %s, %s %s\n' % (c.name, c.size, c.dimensions, ra)
            out += ' \n'
        self.compartment_block = out

    def setFunctions(self):
        out = ''
        start = True
        for f in self.core.functions:
            if start:
                out = '# Function definitions\n'
                start = False
            out += 'Function: %s' % f.name
            for a in f.args:
                out += ', %s ' % a
            out += ' {\n%s\n}\n' % f.formula
        if out != '':
            out += ' \n'
        self.function_block = out

    def setReactions(self):
        out = '# Reactions'
        for r in self.core.reactions:
            C = ''
            if not r.hasCompartment() and len(self.core.compartments) == 1:
                print 'Info: single compartment model: locating \"%s\" in default compartment' % r.name
                C = '@%s' % self.core.compartments[0].getName()
            elif not r.hasCompartment() and len(self.core.compartments) > 1:
                print 'Info: multiple compartments defined but reaction %s is not located in any of them.' % r.name
            elif r.hasCompartment():
                C = '@%s' % r.getCompartment().getName()

            rout = '\n%s%s:\n    ' % (r.name, C)
            cnt = 0

            # we assume that multistoich_enabled will only be true if NewCore.netStoich is False
            ##  print r.hasSubstrates()
            ##  print r.hasProducts()

            if not r.multistoich_enabled:
                for lh in r.hasSubstrates():
                    ##  print '\nFuck'
                    ##  print lh
                    ##  print r.stoichiometry
                    if cnt != 0:
                        rout += ' + '
                    if abs(r.stoichiometry[lh]) == 1.0:
                        rout += '%s' % lh
                    else:
                        rout += '{%s}%s' % (abs(r.stoichiometry[lh]), lh)
                    cnt += 1
                if len(r.hasSubstrates()) == 0:
                    rout += '$pool'
                if r.reversible:
                    rout += ' = '
                else:
                    rout += ' > '
                cnt = 0

                for rh in r.hasProducts():
                    ##  print '\nFuck2'
                    ##  print rh
                    ##  print r.stoichiometry
                    if cnt != 0:
                        rout += ' + '
                    if abs(r.stoichiometry[rh]) == 1.0:
                        rout += '%s' % rh
                    else:
                        rout += '{%s}%s' % (abs(r.stoichiometry[rh]), rh)
                    cnt += 1
                if len(r.hasProducts()) == 0:
                    rout += '$pool'
            else:
                LHS = ''
                RHS = ''
                REVS = ''
                for lh in r.multistoich:
                    if lh[1] < 0.0:
                        if abs(lh[1]) == 1.0:
                            LHS += '%s' % lh[0]
                        else:
                            LHS += '{%s}%s' % ((lh[1]), lh[0])
                        LHS += ' + '
                    else:
                        if abs(lh[1]) == 1.0:
                            RHS += '%s' % lh[0]
                        else:
                            RHS += '{%s}%s' % (abs(lh[1]), lh[0])
                        RHS += ' + '

                if LHS == '':
                    LHS += '$pool'
                else:
                    LHS = LHS[:-3]
                if RHS == '':
                    RHS += '$pool'
                else:
                    RHS = RHS[:-3]
                if r.reversible:
                    REVS += ' = '
                else:
                    REVS += ' > '
                rout = '%s%s %s %s' % (rout, LHS, REVS, RHS)


            # if we have piecewise symbols test if they are in the formula
            # and replace them with the piecewise expression
            formula = r.formula
            if len(self.pw_symbols) > 0:
                pwreplace = 'piecewise('
                for pwd in self.pw_symbols:
                    if pwd in r.formula:
                        for pwc in self.core.__piecewises__[pwd]:
                            if pwc != 'other':
                                pwreplace +=\
                                '%s,%s,' % (self.core.__piecewises__[pwd][pwc][0],self.core.__piecewises__[pwd][pwc][1])
                        if self.core.__piecewises__[pwd]['other'] != None:
                            pwreplace += '%s) ' % self.core.__piecewises__[pwd]['other']
                        else:
                            pwreplace = pwreplace[:-1] + ')'
                        formula = formula.replace(pwd, pwreplace)
            rout += '\n    %s\n' % formula
            if len(r.hasModifiers()) > 0:
                rout += '# %s has modifier(s): ' % r.name
                for m in r.hasModifiers():
                    rout += '%s ' % m
                rout += ' \n'
            out += rout
        out += ' \n'
        self.reaction_block = out

    def setSpecies(self):
        out = ''
        fixed = ''
        var = ''
        for f in self.core.species:
            C = ''
            if not f.hasCompartment() and len(self.core.compartments) == 1:
                print 'Info: single compartment model %s is in default compartment' % f.name
                C = '@%s' % self.core.compartments[0].getName()
            elif not f.hasCompartment() and len(self.core.compartments) > 1:
                print 'Warning: multiple compartments defined but species %s is not located in any of them.' % f.name
            elif f.hasCompartment():
                C = '@%s' % f.getCompartment().getName()

            if not f.fixed:
                if not self.SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                var += '%s%s = %s\n' % (f.name, C, value)
            else:
                if not self.FIXED_SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                fixed += '%s%s = %s\n' % (f.name, C, value)
        out += '# Fixed species\n'
        out += fixed
        out += ' \n'
        out += '# Variable species\n'
        out += var
        out += ' \n'
        self.species_block = out

    def setParameters(self):
        out = '# Parameters\n'
        processed = []
        def setP(p2, out2, proced):
            if not self.PARAMETER_CURRENT_VALUE:
                # take into account that parameters can be compartments now as well
                # brett 2008
                if hasattr(p2, 'value_initial') and p2.value_initial != None:
                    value = p2.value_initial
                else:
                    value = p2()
            else:
                value = p2()
            if p2.name in proced:
                # out2 += '# '
                pass
            else:
                out2 += '%s = %s' % (p2.name, value)
            if not hasattr(p2, 'code_string'):
                if p2.name in proced:
                    pass
                    # out2 += '\t# (initialised)'
                else:
                    out2 += '\n'
            else:
                if p2.name in proced:
                    pass
                    # out2 += '\t# (initialised)\n'
                else:
                    out2 += '\t# (rule)\n'
            return out2

        for r in self.core.reactions:
            ##  out += '# %s parameters\n' % r.name
            for p in r.parameters:
                if p.name not in self.core.hasCompartments():
                    out = setP(p, out, processed)
                    processed.append(p.name)
            ##  out += '\n'
        ##  if len(processed) != len(self.core.hasGlobalParameters()):
        ##  out += '# Additional (global) parameters\n'
        for p in self.core.hasGlobalParameters():
            if p not in processed:
                p = self.core.get(p)
                out = setP(p, out, processed)
                processed.append(p.name)
        out += ' \n'
        self.parameter_block = out

    def setAssignmentRules(self):
        out = ''
        start = True
        for p in self.core.global_parameters + self.core.species:
            if hasattr(p, 'type') and getattr(p, 'type') == 'assignment':
                if start:
                    out = '# Assignment rules\n'
                    start = False

                # if we have piecewise symbols test if they are in the formula
                # and replace them with the piecewise expression
                formula = p.formula
                if len(self.pw_symbols) > 0:
                    pwreplace = ' piecewise('
                    for pwd in self.pw_symbols:
                        if pwd in p.formula:
                            for pwc in self.core.__piecewises__[pwd]:
                                if pwc != 'other':
                                    pwreplace +=\
                                    '%s,%s,' % (self.core.__piecewises__[pwd][pwc][0],self.core.__piecewises__[pwd][pwc][1])
                            if self.core.__piecewises__[pwd]['other'] != None:
                                pwreplace += '%s) ' % self.core.__piecewises__[pwd]['other']
                            else:
                                pwreplace = pwreplace[:-1] + ')'
                            formula = formula.replace(pwd, pwreplace)
                out += '!F %s = %s\n' % (p.name, formula)
        if out != '':
            out += ' \n'
        self.arule_block = out

    def setRateRules(self):
        out = ''
        start = True
        if len(self.core.rate_rules) > 0:
            for rr in self.core.rate_rules:
                if start:
                    out = '# Rate rules\n'
                    start = False
                out += 'RateRule: %s {\n%s\n}\n' % (rr.name, rr.formula)
            out += '\n'
        self.rrule_block = out

    def setEvents(self):
        out = ''
        start = True
        for ev in self.core.events:
            if start:
                out = '# Event definitions\n'
                start = False
            ##  formula = ev.code_string.split('=',1)
            formula = ev.formula
            out += 'Event: %s, %s, %s \n{\n' % (ev.name, formula, ev.delay)
            for ass in ev.assignments:
                out += '%s = %s\n' % (ass.variable.name, ass.formula)
            out += '}\n'
        if out != '':
            out += ' \n'
        self.event_block = out

    def setNotes(self, s=''):
        if len(s) > 1:
            out = '# Notes\n# '
            out += '"""%s\n"""\n' % s
            ##  ccount = 0
            ##  for c in range(len(s)):
                ##  ccount += 1
                ##  if ccount >= 55 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                    ##  out += s[c]+'\n# '
                    ##  ccount = 0
                ##  else:
                    ##  out += s[c]
            ##  out += ' \n'
            self.notes_block = out
        else:
            self.notes_block = '\n'

    def write(self, filename=None, directory=None, getstrbuf=False):
        if filename == None:
            filename = self.name+'.psc'
        if directory != None:
            assert os.path.exists(directory), '\n%s does not exist.' % directory
            filename = os.path.join(directory, filename)
        print 'Writing file: %s' % filename
        outF = file(filename, 'w')
        outF.write(self.header_block)
        outF.write(self.fixed_block)
        outF.write(self.compartment_block)
        outF.write(self.function_block)
        outF.write(self.reaction_block)
        outF.write(self.rrule_block)
        outF.write(self.arule_block)
        outF.write(self.event_block)
        outF.write(self.species_block)
        outF.write(self.parameter_block)
        outF.write(self.notes_block)
        outF.flush()
        outF.close()
        if getstrbuf:
            fb = StringIO.StringIO()
            outF = file(filename, 'r')
            fb.write(outF.read())
            outF.flush()
            outF.close()
            return fb


class PscToCore(object):
    ModelDir = None
    WorkDir = None

    # old
    ModelFile = None
    _PysMod__InitStrings = None
    InitParams = None #suspect
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    ##  __function_forced_str__ = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    __nDict__ = None

    __functions__ = None
    __rules__ = None
    time = None
    __events__ = None
    __uDict__ = None


    def __init__(self):
        try:
            from pysces.PyscesParse import PySCeSParser
            self.pscParser = PySCeSParser(debug=0)
        except:
            print "\nYou need PySCeS installed to use this module"


    def getPSCFileFromDisk(self, ModelFile, ModelDir=None, WorkDir=None):
        """
        find and set up an existing psc file for parsing
        """

        if ModelFile[-4:] != '.psc':
            print "Assuming .psc extension"
            ModelFile += '.psc'
        if ModelDir == None:
            # this will probably change in future - bgoli
            ModelDir = CurrentDirectory
        if WorkDir == None:
            WorkDir = CurrentDirectory
        assert os.path.exists(os.path.join(ModelDir, ModelFile)), \
            '\nFile %s does not exist' % os.path.join(ModelDir, ModelFile)
        setattr(self, 'ModelFile', ModelFile)
        setattr(self, 'ModelDir', ModelDir)
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)

    def getPSCFileFromString(self, ModelString, WorkDir=None):
        """
        set up a temporary psc file for parsing from model string
        """
        if WorkDir == None:
            WorkDir = CurrentDirectory
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)
        setattr(self, 'ModelDir', WorkDir)
        ModelFile = '%s.psc' % time.time()
        Mfile = file(os.path.join(WorkDir,ModelFile),'w')
        Mfile.write(ModelString)
        Mfile.close()
        setattr(self, 'ModelFile', ModelFile)


    def getParsedModel(self):
        """
        Parse the input file associated with the PySCeS model instance and assign the basic model attributes
        """

        self.pscParser.ParsePSC(self.ModelFile,self.ModelDir,self.WorkDir)

        # from __nDict__
        self._PysMod__InitStrings = [s.replace('self.','') for s in self.pscParser.InitStrings]
        for s in self.pscParser.InitStrings:
            exec(s)
        self.InitParams = [s.replace('self.','') for s in self.pscParser.InitParams]
        self.fixed_species = copy.copy(self.pscParser.fixed_species)
        self.species = copy.copy(self.pscParser.species)
        self.parameters = copy.copy(self.pscParser.parameters)
        self.reactions = copy.copy(self.pscParser.reactions)
        self.modifiers = copy.copy(self.pscParser.modifiers)
        self.__functions__ = copy.copy(self.pscParser.functions)

        #print self.modifiers
        self._Function_forced = self.pscParser.ForceFunc.replace('self.','')
        self._Function_forced = self.pscParser.ForceFunc.replace('scipy.','')
        self._Function_time = self.pscParser.TimeFunc.replace('self.','')
        self._Function_user = self.pscParser.UserFunc.replace('self.','')
        self._Function_init = self.pscParser.InitFunc.replace('self.','')
        self.__nDict__ = self.pscParser.NetworkDict.copy()
        self.__sDict__ = self.pscParser.sDict.copy()
        self.__compartments__ = self.pscParser.Compartments.copy()
        self.__uDict__ = self.pscParser.uDict.copy()
        # new

    def getModel(self):
        return self

# TODO: PIECEWISE WRITING

class CoreToSBML(object):
    core = None
    name = None
    SBML = None
    level = 2
    version = 1
    model = None
    document = None
    time = None
    __events__ = None
    __DEBUG__ = False

    NumpyToMathML = {
        'numpy.greater_equal' : 'geq',
        'numpy.greater'       : 'gt',
        'numpy.less_equal'    : 'leq',
        'numpy.less'          : 'lt',
        'numpy.not_equal'     : 'neq',
        'numpy.equal'         : 'eq',
        'numpy.arcsinh'       : 'arcsinh',
        'numpy.arccosh'       : 'arccosh',
        'numpy.arctanh'       : 'arctanh',
        'numpy.arcsin'        : 'arcsin',
        'numpy.arccos'        : 'arccos',
        'numpy.arctan'        : 'arctan',
        'numpy.sinh'          : 'sinh',
        'numpy.cosh'          : 'cosh',
        'numpy.tanh'          : 'tanh',
        'numpy.sin'           : 'sin',
        'numpy.cos'           : 'cos',
        'numpy.tan'           : 'tan',
        'math.log10'          : 'log',
        'math.log'            : 'ln',
        'numpy.floor'         : 'floor',
        'numpy.ceil'          : 'ceil',
        'numpy.sqrt'          : 'sqrt',
        'math.sqrt'           : 'sqrt',
        'math.exp'            : 'exp',
        'operator.eq'      : 'eq',
        'operator.ne'      : 'neq',
        'operator.gt'      : 'gt',
        'operator.ge'      : 'geq',
        'operator.lt'      : 'lt',
        'operator.le'      : 'leq',
        'operator.eq'      : 'equal',
        'operator.ne'      : 'neq',
        'self._piecewise_' : 'piecewise',
        '_piecewise_'      : 'piecewise',
        'operator.not_'    : 'not'
    }

    NumpyToMathMLkeys = [
        'numpy.greater_equal' ,
        'numpy.greater'       ,
        'numpy.less_equal'    ,
        'numpy.less'          ,
        'numpy.not_equal'     ,
        'numpy.equal'         ,
        'numpy.arcsinh'       ,
        'numpy.arccosh'       ,
        'numpy.arctanh'       ,
        'numpy.arcsin'        ,
        'numpy.arccos'        ,
        'numpy.arctan'        ,
        'numpy.sinh'          ,
        'numpy.cosh'          ,
        'numpy.tanh'          ,
        'numpy.sin'           ,
        'numpy.cos'           ,
        'numpy.tan'           ,
        'math.log10'          ,
        'math.log'            ,
        'numpy.floor'         ,
        'numpy.ceil'          ,
        'numpy.sqrt'          ,
        'math.sqrt'           ,
        'math.exp'            ,
        'operator.eq',
        'operator.ne',
        'operator.gt',
        'operator.ge',
        'operator.lt',
        'operator.le',
        'operator.eq',
        'operator.ne',
        'self._piecewise_',
        '_piecewise_',
        'operator.not_'
    ]

    def __init__(self, core):
        self.core = core
        self.name = self.core.getName().replace('.psc','').replace('.xml','')
        try:
            import libsbml as SBML
            self.SBML = SBML
        except Exception, e:
            print e
            print 'Posix sbml load error'
            self.SBML = None

    def createModel(self):
        """
        Create an SBML model and document uses the class attributes:

         - *self.level* [default=2] SBML level
         - *self.version* [default=4] SBML version

        and creates:

         - self.model and SBML model
         - self.document an SBML document

        """
        if self.SBML.getLibSBMLVersion() < 40000:
            self.model = self.SBML.Model()
            self.model.setName(self.name)
            self.document = self.SBML.SBMLDocument()
            self.document.setLevelAndVersion(self.level, self.version)
        else:
            self.model = self.SBML.Model(self.level, self.version)
            self.model.setName(self.name)
            self.document = self.SBML.SBMLDocument(self.level, self.version)

    def setCompartments(self):
        if len(self.core.compartments) < 1:
            print 'Warning: no compartments defined adding one called \"Cell\"'
            self.core.addOneCompartment('Cell', 1.0, 3)
        for cs in self.core.compartments:
            comp_def = self.model.createCompartment()
            comp_def.setId(cs.name)
            comp_def.setName(cs.name)
            comp_def.setVolume(float(cs.size))

    def setDescription(self, txt=None):
        '''
        Sets the model description as a <note> containing `txt` in an HTML paragraph on the model object.

         - *txt* [default=None] the text to insert as the description

        '''

        if txt == None:
            txt = self.core.getDescription()
        else:
            self.core.setDescription(txt)
        notes = ''
        notes = '<body xhtml="http://www.w3.org/1999/xhtml>'
        notes += '<p><span style="font-family: Courier New,Courier,monospace;">%s</span></p>\n' % txt
        notes += '<p><span style="font-family: Courier New,Courier,monospace;"><a href="http://pysces.sourceforge.net">PySCeS</a> (%s) generated model (%s)</span></p>' % (__version__, self.core.getName())
        notes += '</body>'
        self.model.setNotes(notes)

    # TODO: update this to handle composite units
    def setUnits(self):
        """
        Adds the unit definitions to the SBML model object

        """
        ud = self.core.getGlobalUnits()
        for un in ud.keys():
            # for now just the basic stuff
            if un in ['substance','volume','time','length','area']:
                vdef = self.model.createUnitDefinition()
                vdef.setId(un)
                vdef.setName(un)
                vu = self.model.createUnit()
                vu.setKind(self.SBML.UnitKind_forName(ud[un]['kind']))
                vu.setMultiplier(ud[un]['multiplier'])
                vu.setScale(int(ud[un]['scale']))
                vu.setExponent(int(ud[un]['exponent']))
                vu.setOffset(0)

        # new super cool way of doing arbitrary unit definitions
        """
        ud = {'area': {0 : {'exponent': 2, 'kind': 'metre', 'multiplier': 1.0, 'scale': 0}},
                    'length': {0 : {'exponent': 1, 'kind': 'metre', 'multiplier': 1.0, 'scale': 0}},
                    'substance': {0 : {'exponent': 1, 'kind': 'mole', 'multiplier': 1.0, 'scale': 0}},
                    'time': {0 : {'exponent': 1, 'kind': 'second', 'multiplier': 1.0, 'scale': 0}},
                    'volume': {0 : {'exponent': 1, 'kind': 'litre', 'multiplier': 1.0, 'scale': 0}},
                    'mmol_per_gDW_per_hr': {0 : {'exponent': 1, 'kind': 'mole', 'multiplier': 1.0, 'scale': -3},
                                            1 : {'exponent': -1, 'kind': 'gram', 'multiplier': 1.0, 'scale': 0},
                                            2 : {'exponent': -1, 'kind': 'second', 'multiplier': 0.00027777, 'scale': 0}
                                          }
                    }

        for un in ud.keys():
            vdef = model.createUnitDefinition()
            vdef.setId(un)
            vdef.setName(un)
            for u in range(len(ud[un].keys())):
                vu = model.createUnit()
                vu.setKind(libsbml.UnitKind_forName(ud[un][u]['kind']))
                vu.setMultiplier(ud[un][u]['multiplier'])
                vu.setScale(int(ud[un][u]['scale']))
                vu.setExponent(int(ud[un][u]['exponent']))
                vu.setOffset(0)
        """

    def setSpecies(self):
        """
        Add the species definitions to the SBML model object:

        """
        for spe in self.core.species:
            s = self.model.createSpecies()
            s.setId(spe.name)
            s.setName(spe.name)
            if not spe.hasCompartment() and len(self.core.compartments) == 1:
                print 'Warning: species %s does not have a compartment locating it in \"%s\"' % (spe.getName(), self.core.compartments[0].getName())
                spe.setCompartment(self.core.compartments[0])
            elif not spe.hasCompartment() and len(self.core.compartments) >= 1:
                raise UserWarning, "%s does not have a compartment and there are more than 1 to choose from!" % spe.getName()
            s.setCompartment(spe.getCompartment().getName())
            if spe.name in self.core.hasFixedSpecies():
                s.setBoundaryCondition(True)
                s.setConstant(True)
            else:
                s.setBoundaryCondition(False)
            ##  print 'FUCK (%s) %s' % (spe.getName(), spe())
            if spe() == None:
                print 'Warning, species %s has not been initialised setting to 1.0' % spe.getName()
            if spe.isAmount():
                s.setInitialAmount(spe())
                s.setHasOnlySubstanceUnits(True)
            else:
                s.setInitialConcentration(spe())
                s.setHasOnlySubstanceUnits(False)

    def setParameters(self):
        for par in self.core.global_parameters:
            p = self.model.createParameter()
            p.setId(par.name)
            p.setName(par.name)
            p.setValue(par())
            # first attempt, check for a formula ... could be done with introspection
            if hasattr(par, 'formula'):
                p.setConstant(False)
                r = self.model.createAssignmentRule()
                r.setVariable(par.name)
                formula = par.code_string.split('=')[1].replace('self.','').replace('()','')
                formula = self.infixPSC2SBML(formula)
                r.setFormula(par.formula)

    def infixPSC2SBML(self, infix):
        """replace NumPy infix with libSBMl infix"""

        # symbol replacement parse (sometimes simple is better)
        infix2 = str(infix)
        for f in self.NumpyToMathMLkeys:
            infix2 = infix2.replace(f, self.NumpyToMathML[f])
        return infix2

    def astSetCSymbolTime(self, ast):
        """set ASTNode type <cn> _TIME_ </cn> to <csymbol> time </csymbol>"""
        strBuf = StringIO.StringIO()
        mathMLin = self.SBML.writeMathMLToString(ast)
        strBuf.write(mathMLin)
        strBuf.seek(0)
        etree = ElementTree.parse(strBuf)
        root = etree.getroot()
        counter = itertools.count(1)

        def idxNode(node,idx=0):
            if node.text != None and node.text.strip() == '_TIME_':
                # <csymbol encoding="text" definitionURL="http://www.sbml.org/sbml/symbols/time"> t </csymbol>
                node.text = node.text.replace('_TIME_','time')
                node.tag = "{http://www.sbml.org/sbml/symbols/time}csymbol"
                node.attrib.update({'encoding':'text'})
                node.attrib.update({'definitionURL':'http://www.sbml.org/sbml/symbols/time'})

            children = node.getchildren()
            for child in range(len(children)):
                idxNode(children[child], counter.next())

        idxNode(root, idx=0)

        strBuf = StringIO.StringIO()
        etree.write(strBuf)
        strBuf.seek(0)
        mathMLout = strBuf.read()
        return self.SBML.readMathMLFromString(mathMLout)

    def setRules(self):
        """Set rate rules"""
        for rule in self.core.rate_rules:
            RR = self.model.createRateRule()
            RR.setVariable(rule.getName())
            # replace PySCeS infix with libSBML infix
            form = rule.code_string.split('=')[1].replace('self.','').replace('()','')
            form = self.infixPSC2SBML(form)
            req_replacements = {}
            for symb in rule._names:
                if symb in self.core.hasReactions():
                    req_replacements.update({symb : '(%s)' % self.infixPSC2SBML(self.core.get(symb).code_string.split('=')[1].replace('self.','').replace('()',''))})
            if len(req_replacements.keys())  > 0:
                InfixParser.setNameStr('', '')
                InfixParser.SymbolReplacements = req_replacements
                InfixParser.parse(form)
                form = InfixParser.output
            ASTnode = self.SBML.parseFormula(form)
            assert ASTnode != None, "ERROR: unable to parse formula (%s) to AST" % form
            if self.__DEBUG__: print 'Adding RateRule: %s = %s' % (rule.getName(),form)
            # set _TIME_ ASTnode tag to <csymbol> time </csymbol>
            ASTnode = self.astSetCSymbolTime(ASTnode)
            RR.setMath(ASTnode)
            # after creating the rule set the parameter to be non constant
            rpar = self.model.getParameter(rule.getName())
            if rpar == None:
                rpar = self.model.getCompartment(rule.getName())
            if rpar == None:
                rpar = self.model.getSpecies(rule.getName())
            if rpar != None:
                rpar.setConstant(False)

    def setEvents(self):
        """Set events"""
        for ev in self.core.events:
            if self.__DEBUG__: print 'Adding event: %s' % ev.getName()
            EV = self.model.createEvent()
            EV.setName(ev.getName())
            EV.setId(ev.getName())

            ##  print ev.formula
            # replace PySCeS infix with libSBML infix
            form = ev.code_string.split('=')[1].replace('self.','').replace('()','')
            form = self.infixPSC2SBML(form)
            if self.__DEBUG__: print '\tTrigger: %s' % form
            print '\tTrigger: %s' % form
            ASTnode = self.SBML.parseFormula(form)
            assert ASTnode != None, "ERROR: unable to parse formula (%s) to AST" % form
            # set _TIME_ ASTnode tag to <csymbol> time
            ASTnode = self.astSetCSymbolTime(ASTnode)
            # tr = self.SBML.Trigger(ASTnode)
            tr = self.SBML.Trigger(self.level, self.version)
            tr.setMath(ASTnode)
            EV.setTrigger(tr)
            for ass in ev.assignments:
                if self.__DEBUG__: print '\tAssignment: %s = %s' % (ass.getName(), ass.formula)
                form = self.infixPSC2SBML(ass.formula)
                ASTnode = self.SBML.parseFormula(form)
                # eass = self.SBML.EventAssignment(ass.getName(), ASTnode)
                eass = self.SBML.EventAssignment(self.level, self.version)
                eass.setMath(ASTnode)
                EV.addEventAssignment(eass)
            if ev.delay != 0:
                dform = self.infixPSC2SBML(ev.delay)
                ASTnodeD = self.SBML.parseFormula(dform)
                ASTnodeD = self.astSetCSymbolTime(ASTnodeD)
                D = self.SBML.Delay(ASTnodeD)
                EV.setDelay(D)

    def setReactions(self):
        for rxn in self.core.reactions:
            # print 'Adding reaction:', rxn.name
            SBML_R = self.model.createReaction()
            SBML_R.setId(rxn.name)
            SBML_R.setName(rxn.name)
            for s in rxn.substrates:
                ##  print '\t' + rxn.name +' has substrate: ' + s.name + ' (%s)' % abs(rxn.stoichiometry[s.name])
                if self.SBML.getLibSBMLVersion() < 40000:
                    sref = self.SBML.SpeciesReference(s.name, abs(rxn.stoichiometry[s.name]))
                else:
                    sref = self.SBML.SpeciesReference(self.level, self.version)
                    sref.setStoichiometry(abs(rxn.stoichiometry[s.name]))
                    sref.setSpecies(s.name)
                SBML_R.addReactant(sref)
            for p in rxn.products:
                ##  print '\t' + rxn.name +' has product: ' + p.name + ' (%s)' % abs(rxn.stoichiometry[p.name])
                if self.SBML.getLibSBMLVersion() < 40000:
                    pref = self.SBML.SpeciesReference(p.name, abs(rxn.stoichiometry[p.name]))
                else:
                    pref = self.SBML.SpeciesReference(self.level, self.version)
                    pref.setStoichiometry(abs(rxn.stoichiometry[p.name]))
                    pref.setSpecies(p.name)
                SBML_R.addProduct(pref)
            if not rxn.multistoich_enabled:
                for m in rxn.modifiers:
                    self.model.createModifier().setSpecies(m.name)
            else:
                AllReagents = [r[0] for r in rxn.multistoich]
                for m in rxn.modifiers:
                    if m.name not in AllReagents:
                        # print '\t' + rxn.name +' has modifier: ' + m.name
                        self.model.createModifier().setSpecies(m.name)
            formula = rxn.code_string.split('=')[1].replace('self.','').replace('()','')
            formula = self.infixPSC2SBML(formula)
            if self.SBML.getLibSBMLVersion() < 40000:
                sb_kl = self.SBML.KineticLaw(formula)
            else:
                sb_kl = self.SBML.KineticLaw(self.level, self.version)
                sb_kl.setFormula(formula)
            SBML_R.setKineticLaw(sb_kl)

            if rxn.reversible:
                SBML_R.setReversible(True)
            else:
                SBML_R.setReversible(False)

    def getSBMLmodel(self):
        return self.model

    def getSBMLdocument(self):
        self.document.setModel(self.model)
        return self.document

    def getSBML(self):
        return self.getSBMLdocument().toSBML()

    def getSBMLFileAsStrBuf(self):
        try: UseR = getuser()
        except: UseR = ''
        fb = StringIO.StringIO()
        h1 = '<?xml version="1.0" encoding="utf-8"?>\n'
        h1 += '<!-- Created with PySCeS ('+ __version__ + ') on ' + time.strftime("%a, %d %b %Y %H:%M:%S") + ' by '+UseR+' -->\n'
        fb.write(h1 + self.getSBML())
        return fb

    def writeSBML2file(self, filename=None, directory=None):
        """
        Writes the SBML model to file:

         - *filename* [default=None] the output filename, default is <model_name>.xml
         - *directory* [default=None] by default use filename otherwise join, <dir><filename>

        """
        if filename == None:
            filename = self.name+'.xml'
        if directory != None:
            assert os.path.exists(directory), '\n%s does not exist.' % directory
            filename = os.path.join(directory, filename)
        print 'Writing file: %s' % filename

        try: UseR = getuser()
        except: UseR = ''
        h1 = '<?xml version="1.0" encoding="utf-8"?>\n'
        h1 += '<!-- Created with PySCeS ('+ __version__ + ') on ' + time.strftime("%a, %d %b %Y %H:%M:%S") + ' by '+UseR+' -->\n'
        F = file(filename, 'w')
        F.write(h1 + self.getSBML())
        F.flush()
        F.close()
        print 'Model %s exported as: %s' % (self.name, filename)


class SbmlToCore(object):
    SBML = None
    level = 2
    sbml_string = None
    sbml_file = None
    model = None
    document = None
    core = None
    # old
    ModelFile = None
    InitParams = None
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    compartments = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    __nDict__ = None
    __functions__ = None
    __rules__ = None
    time = None
    __eDict__ = None
    notes = None
    IS_SINGLE_COMPARTMENT = False
    SPECIES_IN_AMOUNTS = False
    __reserved__ = None
    __sDict__ = None
    __InitDict__ = None
    __KeyWords__ = None
    __Errors__ = None
    __piecewises__ = None
    __error_sleep_time__ = 1
    _ecount = None
    __uDict__ = None
    __DEBUG__ = False
    COMP_FUDGE_FACTOR = 1.0

    def __init__(self):
        try:
            import libsbml as SBML
            self.SBML = SBML
        except Exception, e:
            print e
            print 'SBML load error'
            self.SBML = None
        assert self.SBML != None, '\nNo SBML library available'

        self.__reserved__ = {'lambda':'Lambda'}
        self.__KeyWords__ = {'Description': 'ModelDescription',
                              'Modelname': 'PySCeSModel',
                              'Species_In_Conc': True,
                              'Output_In_Conc' : True}
        self.__piecewises__ = {}

    def setReservedTerm(self, term, replacement):
        self.__reserved__.update({term : replacement})

    def getSbmlStringFromDisk(self, sbml, Dir=None):
        if sbml[-4:] != '.xml':
            print "Assuming .xml extension"
            sbml += '.xml'
        if Dir == None:
            Dir = CurrentDirectory
        assert os.path.exists(os.path.join(Dir, sbml)), \
            '\nFile %s does not exist' % os.path.join(Dir, sbml)
        self.sbml_file = os.path.join(Dir, sbml)
        sbmlF = file(self.sbml_file, 'r')
        self.sbml_string = sbmlF.read()
        sbmlF.close()

    def getSbmlStringFromString(self, sbml_string):
        self.sbml_string = sbml_string
        self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')

    def getSbmlModel(self, document=None):
        r = self.SBML.SBMLReader()
        if document == None:
            self.document = r.readSBMLFromString(self.sbml_string)
        else:
            self.document = document
            self.sbml_string = self.document.toSBML()
            self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')
        self.model = self.document.getModel()
        if self.model.getId() != 'untitled':
            self.__KeyWords__['Modelname'] = self.model.getId()
        else:
            self.__KeyWords__['Modelname'] = self.model.getName()
        self.__KeyWords__['Description'] = self.model.getName()

    def getUnits(self):
        self.__uDict__ = {'substance': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'mole'},
                          'volume': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'litre'},
                          'time': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'second'},
                          'length': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'},
                          'area': {'exponent': 2, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'}
                          }
        for ud in self.model.getListOfUnitDefinitions():
            self.__uDict__.update({ud.getId() : {}})
            if ud.getNumUnits() == 1:
                u = ud.getListOfUnits()[0]
                self.__uDict__[ud.getId()].update({'multiplier' : u.getMultiplier(),
                                                    'exponent' : u.getExponent(),
                                                    'scale' : u.getScale(),
                                                    'kind' : self.SBML.UnitKind_toString(u.getKind())
                                                    })
            else:
                for u in ud.getListOfUnits():
                    self.__uDict__[ud.getId()].update({self.SBML.UnitKind_toString(u.getKind()) :
                                                       {'multiplier' : u.getMultiplier(),
                                                        'exponent' : u.getExponent(),
                                                        'scale' : u.getScale(),
                                                        'kind' : self.SBML.UnitKind_toString(u.getKind())
                                                        }})

    def getEvents(self):
        self.__eDict__ = {}
        for ev in self.model.getListOfEvents():
            name = self.getId(ev)
            trigger = ev.getTrigger()
            triggerf = self.SBML.formulaToString(trigger.getMath())

            # check for csymbol time
            csymb = r'<csymbol encoding="text" definitionURL="http://www.sbml.org/sbml/symbols/time">.*<'
            hasTimeS = re.search(csymb, self.SBML.writeMathMLToString(trigger.getMath()))
            tSymb = None
            if hasTimeS != None:
                tSymb = hasTimeS.group()[hasTimeS.group().find('>'):]
                tSymb = tSymb.replace('>','').replace('<','').strip()
                print 'csymbol time defined as \"%s\" in event %s' % (tSymb,trigger.getId())

            delay = ev.getDelay()
            if delay != None:
                delay = self.SBML.formulaToString(ev.getDelay().getMath())
            else:
                delay = 0.0

            self.__eDict__.update({name : {'name' : name,
                                    'trigger' : triggerf,
                                    'delay' : delay,
                                    'assignments' : {},
                                    'tsymb' : tSymb
                                    }
                           })
            for a in ev.getListOfEventAssignments():
                self.__eDict__[name]['assignments'].update({a.getVariable() :
                                            self.SBML.formulaToString(a.getMath())
                                            })

    def checkParsedInfix(self):
        assert len(InfixParser.SymbolErrors) == 0, '\nUndefined symbols:\n%s' % InfixParser.SymbolErrors
        assert InfixParser.LexOK, '\nLexer Failure:\n%s' % InfixParser.LexErrors
        assert InfixParser.ParseOK, '\nParser Failure:\n%s' % InfixParser.ParseErrors

    def getId(self, e):
        name = e.getId()
        if name in self.__reserved__:
            print '%s is a reserved symbol, replacing with %s' % (name, self.__reserved__[name])
            self.__Errors__.update({self._ecount.next() : 'Reserved symbol %s replaced with %s' % (name, self.__reserved__[name])})
            name = self.__reserved__[name]
        return name

    def updatePiecewiseDict(self, piecewises):
        if len(piecewises.keys()) > 0:
            for p in piecewises:
                if len(piecewises[p].keys()) == 2:
                    piecewises[p][0].reverse()
                self.__piecewises__.update({p : piecewises[p]})

    def getParsedModel(self):
        init_fixed = {}
        init_var = {}
        init_par = {}
        self.__compartments__ = {}
        # new stuff
        self.__functions__ = {}
        self.SPECIES_IN_AMOUNTS = False
        self.__sDict__ = {}
        self.__InitDict__ = {}
        self.__Errors__ = {}
        self._ecount = itertools.count()

        # Initialise compartment volumes as a parameter - brett 20050908
        for comp in self.model.getListOfCompartments():
            cont = comp.getOutside()
            if cont == '': cont = None
            cSize = float(comp.getSize())
            self.__compartments__.update({self.getId(comp):{'name':self.getId(comp),
                                                        ##  'volume': float(comp.getVolume()),
                                                        'size': cSize,
                                                        'dimensions' : int(comp.getSpatialDimensions()),
                                                        'compartment': cont,
                                                        'area' : None,
                                                        'fullName' : comp.getName()
                                                            }})
        self.COMP_FUDGE_FACTOR = 1.0
        startFudging = 1.0e-6
        I_AM_FUDGING = False
        tmp = min([abs(self.__compartments__[c]['size']) for c in self.__compartments__.keys()])
        if tmp < startFudging:
            self.COMP_FUDGE_FACTOR = tmp # sneaky b%*))_)%$^&*@rds

        for c in self.__compartments__.keys():
            if self.COMP_FUDGE_FACTOR < startFudging:
                newsize = self.__compartments__[c]['size']/self.COMP_FUDGE_FACTOR
                print 'INFO: Rescaling compartment with size %s to %s' % (self.__compartments__[c]['size'], newsize)
                self.__compartments__[c]['size'] = newsize
                self.__compartments__[c].update({'scale' : self.COMP_FUDGE_FACTOR})
                I_AM_FUDGING = True

        if len(self.__compartments__) == 1:
            self.IS_SINGLE_COMPARTMENT = True
        else:
            self.IS_SINGLE_COMPARTMENT = False

        if len(self.model.getListOfSpecies()) < 1:
            self.__Errors__.update({self._ecount.next() : 'No free species!? ... help I\'m confused!'})
        for i in self.model.getListOfSpecies():
            specname = self.getId(i)
            if i.getHasOnlySubstanceUnits():
                if not self.SPECIES_IN_AMOUNTS:
                    self.SPECIES_IN_AMOUNTS = True
        if self.SPECIES_IN_AMOUNTS:
            self.__KeyWords__['Species_In_Conc'] = False
        else:
            self.__KeyWords__['Species_In_Conc'] = True
            self.__KeyWords__['Output_In_Conc'] = True

        for i in self.model.getListOfSpecies():
            specname = self.getId(i)
            if self.__DEBUG__: print '%s has only substance units: %s' % (specname, i.getHasOnlySubstanceUnits())
            IS_CONC = i.isSetInitialConcentration()
            IS_AMNT = i.isSetInitialAmount()
            Conc = float(i.getInitialConcentration())
            Amount = float(i.getInitialAmount())
            if self.SPECIES_IN_AMOUNTS or IS_AMNT:
                if I_AM_FUDGING:
                    Conc = Amount/self.COMP_FUDGE_FACTOR
                else:
                    Conc = Amount
            fxd = None
            if i.getBoundaryCondition() or i.getConstant():
                if i.getConstant() and not i.getBoundaryCondition():
                    print '%s is set as constant, assuming: BoundaryCondition = True' % specname
                init_fixed.setdefault(specname, Conc)
                fxd = True
            else:
                init_var.setdefault(specname, Conc)
                fxd = False

            self.__sDict__.update({specname : {
                                    'name' : specname,
                                    'initial' : Conc,
                                    'compartment' : i.getCompartment(),
                                    'fixed' : fxd,
                                    'isamount' : i.getHasOnlySubstanceUnits(),
                                    'fullName' : i.getName()
                                    }})

        reactions = self.model.getListOfReactions()

        NetworkDict = dict([(i, dict.fromkeys(['Params',
                              'RateEq',
                              'Reagents',
                              'AllReagents',
                              'Modifiers',
                              'Type'])) for i in [self.getId(j) for j in reactions]])

        # Add global parameters
        Punits = []
        if len(self.model.getListOfParameters()) > 0:
            for xp in self.model.getListOfParameters():
                if xp.isSetUnits():
                    Punits.append(self.getId(xp))
                init_par.update({self.getId(xp) : float(xp.getValue())})
        if len(Punits) > 0:
            self.__Errors__.update({self._ecount.next() : 'Parameter units ignored for parameters:\n%s' % Punits})

        # add any function definitions
        if self.model.getNumFunctionDefinitions() > 0:
            for fnc in self.model.getListOfFunctionDefinitions():
                name = self.getId(fnc)
                func = self.SBML.formulaToString(fnc.getMath()).replace('lambda','')[1:-1]
                args = []
                for ar in range(fnc.getNumArguments()):
                    arg = func[:func.find(',')]
                    func = func[func.find(',')+1:]
                    args.append(arg)
                args = [s.strip() for s in args]

                func = func.strip()
                csymb = r'<csymbol encoding="text" definitionURL="http://www.sbml.org/sbml/symbols/time">.*<'
                hasTimeS = re.search(csymb, self.SBML.writeMathMLToString(fnc.getMath()))
                tSymb = None
                if hasTimeS != None:
                    tSymb = hasTimeS.group()[hasTimeS.group().find('>'):]
                    tSymb = tSymb.replace('>','').replace('<','').strip()
                    print 'csymbol time defined as \"%s\" in formula %s' % (tSymb,self.getId(fnc))
                    SRs = {tSymb:'_TIME_'}
                    SRs.update(self.__reserved__)
                    InfixParser.SymbolReplacements = SRs
                InfixParser.setNameStr('', '')
                InfixParser.parse(func)
                p_names = InfixParser.names
                func = InfixParser.output
                self.checkParsedInfix()
                # update piecewise dict ... don't ask!
                self.updatePiecewiseDict(InfixParser.piecewises)

                self.__functions__.update({name : {
                                            'args' : args,
                                            'formula' : func,
                                            'name' : name }
                                        })

        # ======= per reaction stuff ========
        hasFast = []
        Punits = []
        delayignore = []
        for i in reactions:
            rDict = NetworkDict[self.getId(i)]
            rDict.update({'name':self.getId(i)})
            rDict.update({'fullName':i.getName()})
            j = i.getKineticLaw()
            # ignore fast and warn
            if i.isSetFast():
                hasFast.append(self.getId(i))

            par = []
            if j != None:
                req = j.getFormula()
                p_names = None
                # check for csymbol time
                csymb = r'<csymbol encoding="text" definitionURL="http://www.sbml.org/sbml/symbols/time">.*<'
                hasTimeS = re.search(csymb, self.SBML.writeMathMLToString(j.getMath()))
                tSymb = None
                if hasTimeS != None:
                    tSymb = hasTimeS.group()[hasTimeS.group().find('>'):]
                    tSymb = tSymb.replace('>','').replace('<','').strip()
                    print 'csymbol time defined as \"%s\" in reaction %s' % (tSymb,self.getId(i))
                #if there are local parameters hash them to R_P
                if len(j.getListOfParameters()) > 0:
                    InfixParser.setNameStr('@', '@')
                    if hasTimeS != None:
                        SRs = {tSymb:'_TIME_'}
                        SRs.update(self.__reserved__)
                        InfixParser.SymbolReplacements = SRs
                    else:
                        InfixParser.SymbolReplacements = self.__reserved__
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    # catch delay and friends (ie check for shitty rate equations)
                    self.checkParsedInfix()
                    req = InfixParser.output
                    if InfixParser.DelayRemoved:
                        delayignore.append(self.getId(i))
                    if self.__DEBUG__: print 'Setting local parameter:'
                    for k in j.getListOfParameters():
                        if k.isSetUnits() and self.getId(k) not in Punits:
                            Punits.append(self.getId(k))
                        #TODO: replace with a regular expression based strategy
                        req = req.replace('@'+self.getId(k)+'@', self.getId(i) + '_' + self.getId(k))
                        par.append(self.getId(i) + '_' + self.getId(k))
                        init_par.setdefault(self.getId(i) + '_' + self.getId(k), k.getValue())
                        # local parameters are hashed with reaction name
                        if self.__DEBUG__: print '%s_%s' %  (self.getId(i), self.getId(k)),
                    if self.__DEBUG__: print ''
                    req = req.replace('@','')
                else:
                    InfixParser.setNameStr('', '')
                    if hasTimeS != None:
                        SRs = {tSymb:'_TIME_'}
                        SRs.update(self.__reserved__)
                        InfixParser.SymbolReplacements = SRs
                    else:
                        InfixParser.SymbolReplacements = self.__reserved__
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    req = InfixParser.output
                    if InfixParser.DelayRemoved:
                        delayignore.append(self.getId(i))
                    self.checkParsedInfix()

                # update piecewise dict ... don't ask!
                self.updatePiecewiseDict(InfixParser.piecewises)
            else:
                print 'WARNING: No rate equation for reaction %s in file: %s' % (self.getId(i), self.sbml_file)
                req = None

            rDict['Params'] = par
            rDict['RateEq'] = req
            rDict['compartment'] = None

            Substrates = []
            Products = []

            for k in i.getListOfReactants():
                species = k.getSpecies()
                stoich = -k.getStoichiometry()
                Substrates.append((species,stoich))
                # kill/collect stoichiometrymath for future processing
                smath = k.getStoichiometryMath()
                if smath != None:
                    self.__Errors__.update({self._ecount.next() : 'StoichiometryMath (%s) not supported and ignored' % k.getSpecies()})

            for k in i.getListOfProducts():
                species = k.getSpecies()
                stoich = k.getStoichiometry()
                Products.append((species,stoich))
                # kill/collect stoichiometrymath for future processing
                smath = k.getStoichiometryMath()
                if smath != None:
                    self.__Errors__.update({self._ecount.next() : 'StoichiometryMath (%s) not supported and ignored' % k.getSpecies()})

            # work with net stoichiometries
            rDict['Reagents'] = {}
            rDict['AllReagents'] = Substrates+Products
            rtmp = rDict['Reagents']

            for sp in Substrates+Products:
                if not rtmp.has_key(sp[0]):
                    rtmp.update({sp[0] : sp[1]})
                else:
                    rtmp[sp[0]] += sp[1]
            for r in rtmp.keys():
                if abs(rtmp[r]) < 1.0e-14:
                    rtmp.pop(r)
            if i.getReversible() == True:
                t = 'Rever'
            else:
                t = 'Irrev'
            rDict['Type'] = t
            NetworkDict[self.getId(i)].update(rDict)

            mods = []
            if len(i.getListOfModifiers()) > 0:
                for m in i.getListOfModifiers():
                    # TODO: this should be done with SBML object.attributes 20110523
                    msbml = m.toSBML().split('species=\"')[1].split('\"')[0]
                    mods.append(msbml)
            else:
                mods = []
            rDict['Modifiers'] = mods
            #print "Modifiers", rDict['Modifiers']
        if len(hasFast) > 0:
            self.__Errors__.update({self._ecount.next() : 'Fast attribute ignored for reactions:\n%s' % hasFast})
        if len(Punits) > 0:
            self.__Errors__.update({self._ecount.next() : 'Parameter units ignored for (local) parameters:\n%s' % Punits})
        if len(delayignore) > 0:
            self.__Errors__.update({self._ecount.next() : 'delay function removed in reactions:\n%s' % delayignore})

        del hasFast, Punits, delayignore

        # ======= Add Other Model components ========
        self.getEvents()
        self.getRules()

        self.notes = ''
        if self.model.getNotes() is not None:
            self.notes = self.model.getNotesString().replace('\n','\n# ')

        self.ModelFile = self.model.getId()
        if len(self.ModelFile) == 0:
            self.ModelFile = self.model.getName()

        InitStrings = []

        for s in init_var.keys():
            self.__InitDict__.update({s : float(init_var[s])})
            setattr(self, s, float(init_var[s]))
        for s in init_fixed.keys():
            self.__InitDict__.update({s : float(init_fixed[s])})
            setattr(self, s, float(init_fixed[s]))
        for s in init_par.keys():
            self.__InitDict__.update({s : float(init_par[s])})
            setattr(self, s, float(init_par[s]))

        self.InitParams = init_par.keys()
        self.fixed_species = init_fixed.keys()
        self.species = init_var.keys()
        self.parameters = init_par.keys()
        self.reactions = NetworkDict.keys()
        self.modifiers = []
        for r in NetworkDict.keys():
            self.modifiers.append((r, NetworkDict[r]['Modifiers']))
        if len(self.__rules__.keys()) > 0:
            self._Function_forced = ''
            for r in self.__rules__.keys():
                self._Function_forced += self.__rules__[r]['name'] + ' = ' +\
                self.__rules__[r]['formula'] + '\n'
        else:
            self._Function_forced = 'pass\n'
        self._Function_time = ''
        self._Function_user = ''
        self._Function_init = ''
        self.__nDict__ = NetworkDict

        if len(self.__Errors__.keys()) > 0:
            print '\n*******************************************************************'
            ##  print 'Errors encountered in SBML translated!'
            print 'Issues encountered in SBML translation (model processed anyway)'
            print 'SBML source: %s' % self.sbml_file
            print '*******************************************************************\n'
            ekeys = self.__Errors__.keys()
            ekeys.sort()
            for e in ekeys:
                print self.__Errors__[e],'\n'
            print '*******************************************************************\n'
            time.sleep(self.__error_sleep_time__)

    def getRules(self):
        self.__rules__ = {}
        for rule in self.model.getListOfRules():
            rtype = 'unknown'
            if rule.isAssignment():
                rtype = 'assignment'
            elif rule.isRate():
                rtype = 'rate'
            elif rule.isAlgebraic():
                rtype = 'algebraic'
                self.__Errors__.update({self._ecount.next() : 'Algebraic rule (%s) ignored' % rule.getFormula()})

            assert rtype in ['assignment', 'rate', 'algebraic'], '\n%s rules currently not supported.' % rtype

            csymb = r'<csymbol encoding="text" definitionURL="http://www.sbml.org/sbml/symbols/time">.*<'
            hasTimeS = re.search(csymb, self.SBML.writeMathMLToString(rule.getMath()))
            tSymb = None
            formula = rule.getFormula()
            p_names = None
            if hasTimeS != None:
                tSymb = hasTimeS.group()[hasTimeS.group().find('>'):]
                tSymb = tSymb.replace('>','').replace('<','').strip()
                print 'csymbol time defined as \"%s\" in rule %s' % (tSymb, self.getId(rule))
                InfixParser.setNameStr('', '')
                SRs = {tSymb:'_TIME_'}
                SRs.update(self.__reserved__)
                InfixParser.SymbolReplacements = SRs
                InfixParser.parse(formula)
                p_names = InfixParser.names
                formula = InfixParser.output
                pws = InfixParser.piecewises
                self.checkParsedInfix()
            else:
                InfixParser.setNameStr('', '')
                InfixParser.SymbolReplacements = self.__reserved__
                InfixParser.parse(formula)
                p_names = InfixParser.names
                formula = InfixParser.output
                pws = InfixParser.piecewises
                self.checkParsedInfix()

            # update piecewise dict ... don't ask!
            self.updatePiecewiseDict(InfixParser.piecewises)

            self.__rules__.update({rule.getVariable() :
                                                {'name' : rule.getVariable(),
                                                'formula' : formula,
                                                'type' : rtype,
                                                '_names' : p_names
                                                }
                                        })

    def removeCompartmentFromKineticLaw(self, kl):
        strBuf = StringIO.StringIO()
        mathMLin = self.SBML.writeMathMLToString(kl.getMath())
        strBuf.write(mathMLin)
        strBuf.seek(0)

        etree = ElementTree.parse(strBuf)
        root = etree.getroot()

        node_idx = {}
        counter = itertools.count(1)
        self._comp_idx = []
        def idxNode(node,idx=0):
            ##  print 'node.tag', node.tag
            node.attrib.update({'idx':str(idx)})
            node_idx.update({idx:node})
            if node.text != None and node.text.strip() in self.__compartments__.keys():
                node.attrib.update({'compartment':'1'})
                self._comp_idx.append(idx)
            else:
                node.attrib.update({'compartment':'0'})

            children = node.getchildren()
            for child in range(len(children)):
                children[child].attrib.update({'parent':str(idx)})
                ##  print '\tchild.tag', children[child].tag
                idxNode(children[child], counter.next())

        idxNode(root, idx=0)

        if len(self._comp_idx) > 0:
            for comp_idx in self._comp_idx:
                cParent = int(node_idx[comp_idx].attrib['parent'])
                if len(node_idx[cParent].getchildren()) == 3 and\
                node_idx[cParent].getchildren()[0].tag == '{http://www.w3.org/1998/Math/MathML}times':
                    for c in node_idx[cParent].getchildren():
                        if c.tag != '{http://www.w3.org/1998/Math/MathML}times' and c.text.strip() not in self.__compartments__.keys():
                            cParentParent = int(node_idx[cParent].attrib['parent'])
                            for cp in node_idx[cParentParent].getchildren():
                                if cp.attrib['idx'] == node_idx[cParent].attrib['idx']:
                                    cidx = node_idx[cParentParent]._children.index(cp)
                                    node_idx[cParentParent].remove(node_idx[cParent])
                                    c.attrib.update({'parent' : node_idx[cParentParent].attrib['idx']})
                                    node_idx[cParentParent].insert(cidx, c)
                                    break
                elif len(node_idx[cParent].getchildren()) == 2 and\
                node_idx[cParent].getchildren()[0].tag == '{http://www.w3.org/1998/Math/MathML}minus':
                    for c in node_idx[cParent].getchildren():
                        ##  print c.tag, c.attrib
                        if c.tag == '{http://www.w3.org/1998/Math/MathML}minus':
                            cParentParent = int(node_idx[cParent].attrib['parent'])
                            for cp in node_idx[cParentParent].getchildren():
                                ##  print cp.attrib['idx'], node_idx[cParent].attrib['idx']
                                if cp.attrib['idx'] == node_idx[cParent].attrib['idx']:
                                    cidx = node_idx[cParentParent]._children.index(cp)
                                    node_idx[cParentParent].remove(node_idx[cParent])
                                    c.attrib.update({'parent' : node_idx[cParentParent].attrib['idx']})
                                    node_idx[cParentParent].insert(cidx, c)
                                    break
                elif len(node_idx[cParent].getchildren()) > 3:
                    for c in node_idx[cParent].getchildren():
                        if c.text != None and c.text.strip() in self.__compartments__.keys():
                            node_idx[cParent].remove(node_idx[comp_idx])
                            break
        for n in node_idx:
            if node_idx[n].attrib.has_key('idx'):
                node_idx[n].attrib.pop('idx')
            if node_idx[n].attrib.has_key('compartment'):
                node_idx[n].attrib.pop('compartment')
            if node_idx[n].attrib.has_key('parent'):
                node_idx[n].attrib.pop('parent')

        strBuf = StringIO.StringIO()
        etree.write(strBuf)
        strBuf.seek(0)
        mathMLout = strBuf.read()
        newAST = self.SBML.readMathMLFromString(mathMLout)
        kl.setMath(newAST)

