# -*- coding: utf-8 -*-
#*******************************************************************
# * File:            merge.py
# * Description:
# * Author:          HarshaRani
# * E-mail:          hrani@ncbs.res.in
# ********************************************************************/
# **********************************************************************
#** This program is part of 'MOOSE', the
#** Messaging Object Oriented Simulation Environment,
#** also known as GENESIS 3 base code.
#**           copyright (C) 2003-2017 Upinder S. Bhalla. and NCBS
#Created : Friday Dec 16 23:19:00 2016(+0530)
#Version
#Last-Updated: Wed May 21 11:52:33 2018(+0530)
#         By: Harsha
#**********************************************************************/

# This program is used to merge chem models from src to destination
#Rules are :
#   -- If Compartment from the src model doesn't exist in destination model,
#       then entire compartment and its children are copied over including groups
#   -- Models are mergered at group level (if exists)
#       (Group is Neutral object in moose, which may represent pathway in network model)
#   -- Pool's are copied from source to destination if it doesn't exist, if exist nothing is done
#   -- Reaction (Reac), Enzyme (Enz) are copied
#       --- if any danglling Reac or Enz exist then that is not copied
#
#       --- if Reac Name's is different for a given path (group level)
#            then copy the entire Reac along with substrate/product
#       --- if same Reac Name and same sub and prd then nothing is copied
#       --- if same Reac Name but sub or prd is different then duplicated and copied
#
#       --- if Enz Name's is different for a given parent pool path
#            then copy the entire Enz along with substrate/product
#       --- if same Enz Name and same sub and prd then nothing is copied
#       --- if same Enz Name but sub or prd is different then duplicated and copied
#   -- Function are copied only if destination pool to which its suppose to connect doesn't exist with function of its own
#       which is a limitation in moose that no function can be connected to same pool
#
'''
Change log
May 21 : Instead of A and B changed to S and D (source and destination), 
        - at Pool level Neutral and its objected where copied, but again at Enz and Reac level copying was done which caused duplicates which is taken care
        - If Neutral object name is changed for destination, then change made to source file which would be easy to copy else path issue will come
Oct 25 : line to load SBML file was commented, which is uncommented now and also while copying MMenz had a problem which also cleaned up
Oct 14 : absolute import with mtypes just for python3

Oct 12 : clean way of cheking the type of path provided, filepath,moose obj, moose path are taken,
        if source is empty then nothing to copy, 
        if destination was empty list is update with new object

Oct 11 : missing group are copied instead of creating one in new path which also copies Annotator info
        earlier if user asked to save the model, it was saving default to kkit format, now user need to run the command to save (if this is run in command)
        To check: When Gui is allowed to merge 2 models, need to see what happens

'''

import sys
import os
#from . import _moose as moose
import moose

from moose.chemMerge import mtypes
from moose.chemUtil.chemConnectUtil import *
from moose.chemUtil.graphUtils import *
#from moose.genesis import mooseWriteKkit

def checkFile_Obj_str(file_Obj_str):
    model = moose.element('/')
    loaded = False
    found = False
    if isinstance(file_Obj_str, str):
        if os.path.isfile(file_Obj_str) == True:
            model,loaded = loadModels(file_Obj_str)
            found = True
        elif file_Obj_str.find('/') != -1 :
            if not isinstance(file_Obj_str,moose.Neutral):
                if moose.exists(file_Obj_str):
                    model = file_Obj_str
                    loaded = True
                    found = True
        elif isinstance(file_Obj_str, moose.Neutral):
            if moose.exists(file_Obj_str.path):
                model = file_Obj_str.path
                loaded = True
                found = True
    elif isinstance(file_Obj_str, moose.Neutral):
        if moose.exists(file_Obj_str.path):
            model = file_Obj_str.path
            loaded = True
            found = True
    if not found:
        print ("%s path or filename doesnot exist. " % (file_Obj_str))
    return model,loaded

def mergeChemModel(src,des):
    """ Merges two model or the path """
    A = src
    B = des
    sfile = src
    dfile = des
    loadedA = False
    loadedB = False
    modelA = moose.element('/')
    modelB = moose.element('/')
    modelA,loadedA = checkFile_Obj_str(A)
    modelB,loadedB = checkFile_Obj_str(B)
    
    if loadedA and loadedB:
        ## yet deleteSolver is called to make sure all the moose object are off from solver
        deleteSolver(modelA)
        deleteSolver(modelB)

        global poolListina
        poolListina = {}
        grpNotcopiedyet = []
        dictComptA = dict( [ (i.name,i) for i in moose.wildcardFind(modelA+'/##[ISA=ChemCompt]') ] )
        dictComptB = dict( [ (i.name,i) for i in moose.wildcardFind(modelB+'/##[ISA=ChemCompt]') ] )
                
        poolNotcopiedyet = []
        if len(dictComptA):
            for key in list(dictComptA.keys()):
                if key not in dictComptB:
                    # if compartment name from modelB does not exist in modelA, then copy
                    copy = moose.copy(dictComptA[key],moose.element(modelB))

                    dictComptB[key]=moose.element(copy)
                else:
                    #if compartment name from modelB exist in modelA,
                    #volume is not same, then change volume of ModelB same as ModelA
                    if abs(dictComptB[key].volume - dictComptA[key].volume):
                        #hack for now
                        while (abs(dictComptB[key].volume - dictComptA[key].volume) != 0.0):
                            dictComptA[key].volume = float(dictComptB[key].volume)
                    dictComptB = dict( [ (i.name,i) for i in moose.wildcardFind(modelB+'/##[ISA=ChemCompt]') ] )
                    
                    #Mergering pool
                    poolMerge(dictComptA[key],dictComptB[key],poolNotcopiedyet)

            
            comptBdict =  comptList(modelB)

            poolListinb = {}
            poolListinb = updatePoolList(comptBdict)
            
            R_Duplicated, R_Notcopiedyet,R_Dangling = [], [], []
            E_Duplicated, E_Notcopiedyet,E_Dangling = [], [], []
            for key in list(dictComptA.keys()):
                funcExist, funcNotallowed = [], []
                funcExist,funcNotallowed = functionMerge(dictComptB,dictComptA,key)

                poolListinb = updatePoolList(dictComptB)
                R_Duplicated,R_Notcopiedyet,R_Dangling = reacMerge(dictComptA,dictComptB,key,poolListinb)

                poolListinb = updatePoolList(dictComptB)
                E_Duplicated,E_Notcopiedyet,E_Dangling = enzymeMerge(dictComptB,dictComptA,key,poolListinb)
            
            # if isinstance(src, str):
            #     if os.path.isfile(src) == True:
            #         spath, sfile = os.path.split(src)
            #     else:
            #         sfile = src
            # else:
            #     sfile = src
            # if isinstance(des, str):
            #     print " A str",des
            #     if os.path.isfile(des) == True:
            #         dpath, dfile = os.path.split(des)
            #     else:
            #         dfile = des
            # else:
            #     dfile = des
            
            print("\nThe content of %s (src) model is merged to %s (des)." %(sfile, dfile))
            # Here any error or warning during Merge is written it down
            if funcExist:
                print( "\nIn model \"%s\" pool already has connection from a function, these function from model \"%s\" is not allowed to connect to same pool,\n since no two function are allowed to connect to same pool:"%(dfile, sfile))
                for fl in list(funcExist):
                    print("\t [Pool]:  %s [Function]:  %s \n" %(str(fl.parent.name), str(fl.path)))
            if funcNotallowed:
                print( "\nThese functions is not to copied, since pool connected to function input are from different compartment:")
                for fl in list(funcNotallowed):
                    print("\t [Pool]:  %s [Function]:  %s \n" %(str(fl.parent.name), str(fl.path)))
            if R_Duplicated or E_Duplicated:
                print ("These Reaction / Enzyme are \"Duplicated\" into destination file \"%s\", due to "
                        "\n 1. If substrate / product name's are different for a give reaction/Enzyme name "
                        "\n 2. If product belongs to different compartment "
                        "\n Models have to decide to keep or delete these reaction/enzyme in %s" %(dfile, dfile))
                if E_Duplicated:
                    print("Reaction: ")
                for rd in list(R_Duplicated):
                    print ("%s " %str(rd.name))

                if E_Duplicated:
                    print ("Enzyme:")
                    for ed in list(E_Duplicated):
                        print ("%s " %str(ed.name))

            if R_Notcopiedyet or E_Notcopiedyet:

                print ("\nThese Reaction/Enzyme in model are not dagging but while copying the associated substrate or product is missing")
                if R_Notcopiedyet:
                    print("Reaction: ")
                for rd in list(R_Notcopiedyet):
                    print ("%s " %str(rd.name))
                if E_Notcopiedyet:
                    print ("Enzyme:")
                    for ed in list(E_Notcopiedyet):
                        print ("%s " %str(ed.name))

            if R_Dangling or E_Dangling:
                print ("\n Dangling reaction/enzyme are not allowed in moose, these are not merged to %s from %s" %(dfile, sfile))
                if R_Dangling:
                    print("Reaction: ")
                    for rd in list(R_Dangling):
                        print ("%s " %str(rd.name))
                if E_Dangling:
                    print ("Enzyme:")
                    for ed in list(E_Dangling):
                        print ("%s " %str(ed.name))
            
            ## Model is saved
            print ("\n ")
            print ('\nMerged model is available under moose.element(\'%s\')' %(modelB))
            print ('  From the python terminal itself \n to save the model in to genesis format use \n   >moose.mooseWriteKkit(\'%s\',\'filename.g\')' %(modelB))
            print ('  to save into SBML format \n   >moose.mooseWriteSBML(\'%s\',\'filename.xml\')' %(modelB))
            return modelB
            # savemodel = raw_input("Do you want to save the model?  \"YES\" \"NO\" ")
            # if savemodel.lower() == 'yes' or savemodel.lower() == 'y':
            #     mergeto = raw_input("Enter File name ")
            #     if mergeto and mergeto.strip():
            #         filenameto = 'merge.g'
            #     else:
            #         if str(mergeto).rfind('.') != -1:
            #             mergeto = mergeto[:str(mergeto).rfind('.')]
            #         if str(mergeto).rfind('/'):
            #             mergeto = mergeto+'merge'

            #         filenameto = mergeto+'.g'

            #     error,written = moose.mooseWriteKkit(modelB, filenameto)
            #     if written == False:
            #         print('Could not save the Model, check the files')
            #     else:
            #         if error == "":
            #             print(" \n The merged model is saved into \'%s\' " %(filenameto))
            #         else:
            #             print('Model is saved but these are not written\n %s' %(error))

            # else:
            #     print ('\nMerged model is available under moose.element(\'%s\')' %(modelB))
            #     print ('  If you are in python terminal you could save \n   >moose.mooseWriteKkit(\'%s\',\'filename.g\')' %(modelB))
            #     print ('  If you are in python terminal you could save \n   >moose.mooseWriteSBML(\'%s\',\'filename.g\')' %(modelB))
            #return modelB
        
    else:
        print ('\nSource file has no objects to copy(\'%s\')' %(modelA))
        return moose.element('/')
def functionMerge(comptA,comptB,key):
    funcNotallowed, funcExist = [], []
    comptApath = moose.element(comptA[key]).path
    comptBpath = moose.element(comptB[key]).path
    objA = moose.element(comptApath).parent.name
    objB = moose.element(comptBpath).parent.name

    #This will give us all the function which exist in modelB
    funcListinb = moose.wildcardFind(comptBpath+'/##[ISA=Function]')
    for fb in funcListinb:
        #This will give us all the pools that its connected to, for this function
        fvalueOut = moose.element(fb).neighbors['valueOut']
        for poolinB in fvalueOut:
            poolinBpath = poolinB.path
            poolinA = poolinBpath.replace(objB,objA)
            connectionexist = []
            if moose.exists(poolinA):
                #This is give us if pool which is to be connected already exist any connection
                connectionexist = moose.element(poolinA).neighbors['setN']+moose.element(poolinA).neighbors['setConc']+ moose.element(poolinA).neighbors['increment']
                if len(connectionexist) == 0:
                    #This pool in model A doesnot exist with any function
                    inputs = moose.element(fb.path+'/x').neighbors['input']
                    volumes = []
                    for ins in inputs:
                        volumes.append((findCompartment(moose.element(ins))).volume)
                    if len(set(volumes)) == 1:
                        # If all the input connected belongs to one compartment then copy
                        createFunction(fb,poolinA,objB,objA)
                    else:
                        # moose doesn't allow function's input to come from different compartment
                        funcNotallowed.append(fb)
                else:
                    #Pool in model 'A' already exist function "
                    funcExist.append(fb)
            else:
                print(" Path in model A doesn't exists %s" %(poolinA))

    return funcExist,funcNotallowed

def createFunction(fb,setpool,objB,objA):
    fapath1 = fb.path.replace(objB,objA)
    fapath = fapath1.replace('[0]','')

    if not moose.exists(fapath):
        # if fb.parent.className in ['CubeMesh','CyclMesh']:
        #   des = moose.Function('/'+objA+'/'+fb.parent.name+'/'+fb.name)
        # elif fb.parent.className in ['Pool','ZombiePool','BufPool','ZombieBufPool']:
        #   for akey in list(poolListina[findCompartment(fb).name]):
        #       if fb.parent.name == akey.name:
        #           des = moose.Function(akey.path+'/'+fb.name)
        des = moose.Function(fapath)
    else:
        des = moose.element(fapath)
    inputB = moose.element(fb.path+'/x').neighbors["input"]
    moose.connect(des, 'valueOut', moose.element(setpool),'setN' )
    inputA = []
    inputA = moose.element(fapath+'/x').neighbors["input"]
    if not inputA:
        for src in inputB:
            pool = ((src.path).replace(objB,objA)).replace('[0]','')
            numVariables = des.numVars
            expr = ""
            expr = (des.expr+'+'+'x'+str(numVariables))
            expr = expr.lstrip("0 +")
            expr = expr.replace(" ","")
            des.expr = expr
            moose.connect( pool, 'nOut', des.x[numVariables], 'input' )

def comptList(modelpath):
    comptdict = {}
    for ca in moose.wildcardFind(modelpath+'/##[ISA=ChemCompt]'):
        comptdict[ca.name] = ca
    return comptdict

def loadModels(filepath):
    """ load models into moose if file, if moosepath itself it passes back the path and
    delete solver if exist """

    modelpath = '/'
    loaded = False

    if os.path.isfile(filepath) :
        fpath, filename = os.path.split(filepath)
        # print " head and tail ",head,  " ",tail
        # modelpath = filename[filename.rfind('/'): filename.rfind('.')]
        # print "modelpath ",modelpath
        # ext = os.path.splitext(filename)[1]
        # filename = filename.strip()
        modelpath = '/'+filename[:filename.rfind('.')]
        modeltype = mtypes.getType(filepath)
        subtype = mtypes.getSubtype(filepath, modeltype)

        if subtype == 'kkit' or modeltype == "cspace":
            if moose.exists(modelpath):
                moose.delete(modelpath)
            moose.loadModel(filepath,modelpath)
            loaded = True

        elif subtype == 'sbml':
            if moose.exists(modelpath):
                moose.delete(modelpath)
            moose.mooseReadSBML(filepath,modelpath)
            loaded = True
        else:
            print("This file is not supported for mergering")
            modelpath = moose.Shell('/')

    elif moose.exists(filepath):
        modelpath = filepath
        loaded = True

    return modelpath,loaded

def deleteSolver(modelRoot):
    compts = moose.wildcardFind(modelRoot+'/##[ISA=ChemCompt]')
    for compt in compts:
        if moose.exists(compt.path+'/stoich'):
            st = moose.element(compt.path+'/stoich')
            st_ksolve = st.ksolve
            moose.delete(st)
            if moose.exists((st_ksolve).path):
                moose.delete(st_ksolve)

def poolMerge(comptS,comptD,poolNotcopiedyet):
    #Here from source file all the groups are check if exist, if doesn't exist then create those groups
    #Then for that group pools are copied
    SCmptGrp = moose.wildcardFind(comptS.path+'/#[TYPE=Neutral]')
    SCmptGrp = SCmptGrp +(moose.element(comptS.path),)
    
    DCmptGrp = moose.wildcardFind(comptD.path+'/#[TYPE=Neutral]')
    DCmptGrp = DCmptGrp +(moose.element(comptD.path),)
    
    objS = moose.element(comptS.path).parent.name
    objD = moose.element(comptD.path).parent.name
    
    for spath in SCmptGrp:
        grp_cmpt = ((spath.path).replace(objS,objD)).replace('[0]','')
        if moose.exists(grp_cmpt):
            #If path exist, but its not the Neutral obj then creating a neutral obj with _grp
            #It has happened that pool, reac, enz name might exist with the same name, which when tried to create a group
            # it silently ignored the path and object copied under that pool instead of Group
            if moose.element(grp_cmpt).className != spath.className:
                grp_cmpt = grp_cmpt+'_grp'
                moose.Neutral(grp_cmpt)
                # If group name is changed while creating in destination, then in source file the same is changed,
                # so that later path issue doens't come 
                spath.name = spath.name+'_grp'
        else:
            #Neutral obj from src if doesn't exist in destination,then create src's Neutral obj in des
            src = spath
            srcpath = (spath.parent).path
            des = srcpath.replace(objS,objD)
            moose.Neutral(moose.element(des).path+'/'+spath.name)
        
        dpath = moose.element(spath.path.replace(objS,objD))
        spoollist = moose.wildcardFind(spath.path+'/#[ISA=PoolBase]')
        dpoollist = moose.wildcardFind(dpath.path+'/#[ISA=PoolBase]')
        #check made, for a given Neutral or group if pool doesn't exist then copied
        # but some pool if enzyme cplx then there are holded untill that enzyme is copied in
        # `enzymeMerge` function        
        for spool in spoollist:
            if spool.name not in [dpool.name for dpool in dpoollist]:
                copied = copy_deleteUnlyingPoolObj(spool,dpath)
                if copied == False:
                    #hold it for later, this pool may be under enzyme, as cplx
                    poolNotcopiedyet.append(spool)
    
def copy_deleteUnlyingPoolObj(pool,path):
    # check if this pool is under compartement or under enzyme?(which is enzyme_cplx)
    # if enzyme_cplx then don't copy untill this perticular enzyme is copied
    # case: This enzyme_cplx might exist in modelA if enzyme exist then this
    # will automatically copie's the pool
    copied = False

    if pool.parent.className not in ["Enz","ZombieEnz","MMenz","ZombieMMenz"]:
         if path.className in ["Neutral","CubeMesh","CyclMesh"]:
            poolcopied = moose.copy(pool,path)
            copied = True
            # deleting function and enzyme which gets copied if exist under pool
            # This is done to ensure Dangling function / enzyme not copied.
            funclist = []
            for types in ['setConc','setN','increment']:
                funclist.extend(moose.element(poolcopied).neighbors[types])

            for fl in funclist:
                moose.delete(fl)
            enzlist = moose.element(poolcopied).neighbors['reac']
            for el in list(set(enzlist)):
                moose.delete(el.path)
    return copied

def updatePoolList(comptAdict):
    for key,value in list(comptAdict.items()):
        plist = moose.wildcardFind(value.path+'/##[ISA=PoolBase]')
        poolListina[key] = plist
    return poolListina

def enzymeMerge(comptD,comptS,key,poolListind):
    war_msg = ""
    RE_Duplicated, RE_Notcopiedyet, RE_Dangling = [], [], []
    comptDpath = moose.element(comptD[key]).path
    comptSpath = moose.element(comptS[key]).path
    objD = moose.element(comptDpath).parent.name
    objS = moose.element(comptSpath).parent.name
    #nzyListina => enzyListind

    enzyListind = moose.wildcardFind(comptDpath+'/##[ISA=EnzBase]')
    enzyListins = moose.wildcardFind(comptSpath+'/##[ISA=EnzBase]')
    for es in enzyListins:
        eSsubname, eSprdname = [],[]
        eSsubname = subprdList(es,"sub")
        eSprdname = subprdList(es,"prd")
        allexists, allexistp = False, False
        allclean = False

        poolinDlist = poolListind[findCompartment(es).name]
        for pD in poolinDlist:
            if es.parent.name == pD.name:
                edpath = es.parent.path.replace(objS,objD)

                if not moose.exists(edpath+'/'+es.name):
                    #This will take care
                    #  -- If same enzparent name but different enzyme name
                    #  -- or different parent/enzyme name
                    if eSsubname and eSprdname:
                        allexists = checkexist(eSsubname,objS,objD)
                        allexistp = checkexist(eSprdname,objS,objD)
                        if allexists and allexistp:
                            enzPool = moose.element(pD.path)
                            edpath = es.parent.path.replace(objS,objD)
                            enz = moose.element(moose.copy(es,moose.element(edpath)))
                            enzPool = enz.parent
                            if es.className in ["ZombieEnz","Enz"]:
                                moose.connect(moose.element(enz),"enz",enzPool,"reac")
                            if es.className in ["ZombieMMenz","MMenz"]:
                                moose.connect(enzPool,"nOut",enz,"enzDest")
                            connectObj(enz,eSsubname,"sub",comptD,war_msg)
                            connectObj(enz,eSprdname,"prd",comptD,war_msg)
                            allclean = True
                        else:
                            # didn't find sub or prd for this Enzyme
                            RE_Notcopiedyet.append(es)
                    else:
                        #   -- it is dagging reaction
                        RE_Dangling.append(es)
                else:
                    #Same Enzyme name
                    #   -- Same substrate and product including same volume then don't copy
                    #   -- different substrate/product or if sub/prd's volume is different then DUPLICATE the Enzyme
                    allclean = False
                    ed = moose.element(es.path.replace(objS,objD))
                    eDsubname = subprdList(ed,"sub")
                    eSsubname = subprdList(es,"sub")
                    hasSamenoofsublen,hasSameS,hasSamevols = same_len_name_vol(eDsubname,eSsubname)

                    eDprdname = subprdList(ed,"prd")
                    eSprdname = subprdList(es,"prd")
                    hasSamenoofprdlen,hasSameP,hasSamevolp = same_len_name_vol(eDprdname,eSprdname)
                    if not all((hasSamenoofsublen,hasSameS,hasSamevols,hasSamenoofprdlen,hasSameP,hasSamevolp)):
                        # May be different substrate or product or volume of Sub/prd may be different,
                        # Duplicating the enzyme
                        if eSsubname and eSprdname:
                            allexists,allexistp = False,False
                            allexists = checkexist(eSsubname,objS,objD)
                            allexistp = checkexist(eSprdname,objS,objD)
                            if allexists and allexistp:
                                es.name = es.name+"_duplicated"
                                if es.className in ["ZombieEnz","Enz"]:
                                    edpath = es.parent.path.replace(objS,objD)
                                    enz = moose.copy(es,moose.element(edpath))
                                    moose.connect(enz, 'enz', edpath, 'reac' )

                                if es.className in ["ZombieMMenz","MMenz"]:
                                    edpath = es.parent.path.replace(objS,objD)
                                    enz = moose.copy(es,moose.element(edpath))
                                    enzinfo = moose.Annotator(enz.path+'/info')
                                    moose.connect(moose.element(enz).parent,"nOut",moose.element(enz),"enzDest")
                                    #moose.connect(moose.element(enz),"enz",moose.element(enz).parent,"reac")

                                connectObj(enz,eSsubname,"sub",comptD,war_msg)
                                connectObj(enz,eSprdname,"prd",comptD,war_msg)
                                RE_Duplicated.append(enz)
                                allclean = True
                            else:
                                allclean = False
                    else:
                        allclean = True

                    if not allclean:
                        # didn't find sub or prd for this enzyme
                        #   --  it may be connected Enzyme cplx
                        if eSsubname and eSprdname:
                            RE_Notcopiedyet.append(es)
                        else:
                            RE_Dangling.append(es)

    return RE_Duplicated,RE_Notcopiedyet,RE_Dangling

def reacMerge(comptS,comptD,key,poolListina):
    RE_Duplicated, RE_Notcopiedyet, RE_Dangling = [], [], []
    war_msg = ""
    comptSpath = moose.element(comptS[key]).path
    comptDpath = moose.element(comptD[key]).path
    objS = moose.element(comptSpath).parent.name
    objD = moose.element(comptDpath).parent.name
    
    reacListins = moose.wildcardFind(comptSpath+'/##[ISA=ReacBase]')
    reacListind = moose.wildcardFind(comptDpath+'/##[ISA=ReacBase]')
    
    for rs in reacListins:
        rSsubname, rSprdname = [],[]
        rSsubname = subprdList(rs,"sub")
        rSprdname = subprdList(rs,"prd")
        allexists, allexistp = False, False
        allclean = False

        if rs.name not in [rd.name for rd in reacListind]:
            # reaction name not found then copy
            # And assuming that pools are copied earlier EXPECT POOL CPLX
            # To be assured the it takes correct compartment name incase reaction sub's
            # belongs to different compt
            key = findCompartment(rs).name
            if rSsubname and rSprdname:
                allexists =  checkexist(rSsubname,objS,objD)
                allexistp =  checkexist(rSprdname,objS,objD)
                if allexists and allexistp:
                    rdpath = rs.parent.path.replace(objS,objD)
                    reac = moose.copy(rs,moose.element(rdpath))
                    connectObj(reac,rSsubname,"sub",comptD,war_msg)
                    connectObj(reac,rSprdname,"prd",comptD,war_msg)
                    allclean = True
                else:
                    # didn't find sub or prd for this reaction
                    #   --  it may be connected Enzyme cplx
                    RE_Notcopiedyet.append(rs)
            else:
                #   -- it is dagging reaction
                RE_Dangling.append(rs)
                #print ("This reaction \""+rb.path+"\" has no substrate/product Dangling reaction are not copied")
                #war_msg = war_msg+"\nThis reaction \""+rb.path+"\" has no substrate/product Dangling reaction are not copied"

        else:
            #Same reaction name
            #   -- Same substrate and product including same volume then don't copy
            #   -- different substrate/product or if sub/prd's volume is different then DUPLICATE the reaction
            
            allclean = False
            for rd in reacListind:
                if rs.name == rd.name:
                    rSsubname = subprdList(rs,"sub")
                    rDsubname = subprdList(rd,"sub")
                    hasSamenoofsublen,hasSameS,hasSamevols = same_len_name_vol(rSsubname,rDsubname)

                    rSprdname = subprdList(rs,"prd")
                    rDprdname = subprdList(rd,"prd")
                    hasSamenoofprdlen,hasSameP,hasSamevolp = same_len_name_vol(rSprdname,rDprdname)
                    if not all((hasSamenoofsublen,hasSameS,hasSamevols,hasSamenoofprdlen,hasSameP,hasSamevolp)):
                        # May be different substrate or product or volume of Sub/prd may be different,
                        # Duplicating the reaction
                        if rSsubname and rSprdname:
                            allexists,allexistp = False,False
                            allexists = checkexist(rSsubname,objS,objD)
                            allexistp = checkexist(rSprdname,objS,objD)
                            if allexists and allexistp:
                                rs.name = rs.name+"_duplicated"
                                #reac = moose.Reac(comptA[key].path+'/'+rb.name+"_duplicated")
                                rdpath = rs.parent.path.replace(objS,objD)
                                reac = moose.copy(rs,moose.element(rdpath))
                                connectObj(reac,rSsubname,"sub",comptD,war_msg)
                                connectObj(reac,rSprdname,"prd",comptD,war_msg)
                                RE_Duplicated.append(reac)
                                allclean = True
                            else:
                                allclean = False
                    else:
                        allclean = True

                    if not allclean:
                        # didn't find sub or prd for this reaction
                        #   --  it may be connected Enzyme cplx
                        if rSsubname and rSprdname:
                            RE_Notcopiedyet.append(rs)
                        else:
                            RE_Dangling.append(rs)
    
    return RE_Duplicated,RE_Notcopiedyet,RE_Dangling

def subprdList(reac,subprd):
    rtype = moose.element(reac).neighbors[subprd]
    rname = []
    for rs in rtype:
        rname.append(moose.element(rs))
    return rname

def same_len_name_vol(rA,rB):
    uaS = set(rA)
    ubS = set(rB)
    aS = set([uas.name for uas in uaS])
    bS = set([ubs.name for ubs in ubS])

    hassameLen = False
    hassameSP  = False
    hassamevol = False
    hassamevollist = []
    if (len(rA) == len(rB) ):
        hassameLen = True
        if  not (len (aS.union(bS) - aS.intersection(bS))):
            hassameSP = True
            if rB and rA:
                rAdict = dict( [ (i.name,i) for i in (rA) ] )
                rBdict = dict( [ (i.name,i) for i in (rB) ] )

                for key,bpath in rBdict.items():
                    apath = rAdict[key]
                    comptA = moose.element(findCompartment(apath))
                    comptB = moose.element(findCompartment(bpath))
                    if not abs(comptA.volume -comptB.volume):
                            hassamevollist.append(True)
                    else:
                            hassamevollist.append(False)

    if len(set(hassamevollist))==1:
        for x in set(hassamevollist):
            hassamevol = x

    return ( hassameLen,hassameSP,hassamevol)

def connectObj(reac,spList,spType,comptA,war_msg):
    #It should not come here unless the sub/prd is connected to enzyme cplx pool
    allclean = False
    for rsp in spList:
        for akey in list(poolListina[findCompartment(rsp).name]):
            if rsp.name == akey.name:
                if moose.exists(akey.path):
                    moose.connect(moose.element(reac), spType, moose.element(akey), 'reac', 'OneToOne')
                    allclean = True
                else:
                    #It should not come here unless the sub/prd is connected to enzyme cplx pool
                    allclean = False
    return allclean

def checkexist(spList,objB,objA):
    allexistL = []
    allexist = False
    for rsp in spList:
        found = False
        rspPath = rsp.path.replace(objB,objA)
        if moose.exists(rspPath):
            found = True
        allexistL.append(found)

    if len(set(allexistL))==1:
        for x in set(allexistL):
            allexist = x

    return allexist

if __name__ == "__main__":
    try:
        sys.argv[1]
    except IndexError:
        print("Source filename or path not given")
        exit(0)
    else:
        src = sys.argv[1]
        if not os.path.exists(src):
            print("Filename or path does not exist %s." %(src))
        else:
            try:
                sys.argv[2]
            except IndexError:
                print("Destination filename or path not given")
                exit(0)
            else:
                des = sys.argv[2]
                if not os.path.exists(src):
                    print("Filename or path does not exist %s." %(des))
                    exit(0)
                else:
                    print ("src and des %s, %s." %(src, des))
                    mergered = mergeChemModel(src,des)

                '''
                try:
                    sys.argv[3]
                except IndexError:
                    print ("Merge to save not specified")
                    mergeto = "merge"
                else:
                    mergeto = sys.argv[3]
                    if str(mergeto).rfind('.') != -1:
                        mergeto = mergeto[:str(mergeto).rfind('.')]
                    if str(mergeto).rfind('/'):
                        mergeto = mergeto+'merge'

                    mergered = mergeChemModel(src,des,mergeto)
                '''