"""
     python/ui/model_selection.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2011 University of York
     Copyright (C) 2012 STFC

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU Lesser General Public License for more details.
"""

import re,string,types

from mmdb2 import *
import mmut

import dataobj
from  utils import *
from global_definitions import *


SELECTION_LABELS = { 'all':"All atoms",
               'allnotsolv':'All non-solvent',
               'peptide':'All peptide',
               'nopeptide':'No peptide',
               'catrace':'CA trace',
               'main':'Main chain',
               'side':'Side chains',
               'CA+side' : 'CA+side chains',
               'peptide_link' : 'peptide link',
               'ssbond':'SS bridges',
               'nucleic':'All nucleic acid',
               'nonucleic':'No nucleic acid',
               'backbone':'Backbone',
               'base':'Bases',
               'sugar_ring':'Sugar rings',
               'saccharide': 'Saccharides',
               'metal': 'Metal',
               'monomers': 'Monomers',
               'sites': 'Active sites',
               'solvent': 'Solvent',
               'solute': 'Solute',
               'same_as' : 'Same as other' }

SELECTION_ICONS = { "All atoms":'allatoms.svg',
               'All non-solvent':'allnonsolvent.svg',
               'All peptide':'allpeptide.svg',
               'CA trace':'catrace.svg',
               'Main chain':'mainchain.svg',
               'Side chains':'sidechain.svg',
               'SS bridges':'ssbridge.svg',
               'Disulphide bridges':'ssbridge.svg',
               'All nucleic acid':'allnucleic.svg',
               'Backbone':'backbone.svg',
               'Solvent':'solvent.svg',
               'Bases':'bases.svg',
               'Same as other':'sameas.svg',
               'peptide link':'peptidelink.svg',
               'Saccharides':'saccharides.svg',
               'Sugar rings':'sugarrings.svg',
               'Nucleotides':'bases.svg',
               'Metals':'metal.svg',
               'Metal':'metal.svg',
               'Monomers':'monomers.svg',
               'Lipids':'lipids.svg',
               'Solute':'solute.svg'
               }

REVISED_SELECTION_COMMANDS = { 'nopeptide' : 'monomers',
                               'amino_acid' : 'peptide' }

PEPTIDE_ATOMTYPE_MENU = ['peptide','catrace','main','side','Ca+side','ssbond']
NUCLEIC_ATOMTYPE_MENU = ['nucleic','backbone','base','sugar_ring']

   
#-------------------------------------------------------------------
#-------------------------------------------------------------------
class SelectionParser:
#-------------------------------------------------------------------
#-------------------------------------------------------------------

#-------------------------------------------------------------------
  def command_to_list( self,command ):
#-------------------------------------------------------------------
    '''
    Convert an input command string to a python list selection
    '''
    toks = self.tokenise(command)
    if toks[0] > 0:
      return [1,'Error parsing command']
    else:
      #toks=self.expand(toks[1],toks[2],toks[3])
      t = Cselect_tree('TOP','',self)
      rv = t.import_command_string(toks[1],toks[2],toks[3])
      l = t.get()
      t.cleanup()
      return [0,l]
    
#-------------------------------------------------------------------
  def list_to_command(self,list):
#-------------------------------------------------------------------
    t = Cselect_tree('TOP','',self)
    t.set(list)
    rv = t.export_command_string()
    t.cleanup()
    return rv

#-------------------------------------------------------------------------
  def tokenise(self,inp_str,report=1):
#-------------------------------------------------------------------------
    stdops = ['and','or','xor','excl','not']
    elements = []
    ops = []
    levels = []
    wk_str = re.sub('{',' { ',inp_str)
    wk_str = re.sub('}',' } ',wk_str)
    wk_str = re.sub('not',' not ',wk_str)
    # This is a kludge to hide operaters within quotes
    quoted = re.findall('".*?"',wk_str)
    #print "quoted",quoted
    if len(quoted)>0:wk_str = re.sub('".*?"','QuOTE',wk_str)
      
    rx = re.compile('\{| and | or | xor | excl | not |\}')
    nq = -1
    for ele in rx.split(wk_str):
      qu = re.findall('QuOTE',ele)
      
      for ii in range(0,len(qu)):
        nq = nq+1
        ele = re.sub('QuOTE',quoted[nq],ele,1)
      elements.append(string.strip(ele))
    for op in rx.findall(wk_str): 
      ops.append(string.strip(op))
    l = self.getlevels(ops)
    if l[0] > 0: return l
    levels = l[1]

    #print "elements",elements
    #print "ops",ops
    #print "levels",levels

    if len(ops) > 0:

# Test the first operator
      if ops[0] == '{':
        if elements[0] != '':
          return [3,'Missing operator before open brace at ' \
                + "'" + elements[0] + ' ' + ops[0] + "'"]
      else:
        if elements[0] == '' and ops[0] != 'not':
          return [4,'Missing element before ' + ops[0]]

      lop = stdops.count(ops[0]) 
      for i in range(1,len(ops)):
        cop = stdops.count(ops[i])
        dlev = levels[i] - levels[i-1]
        if lop and cop:
# test if two consecutive operators
          if elements[i] == '' and ops[i] != 'not':
            return [4,'Missing element between ' + ops[i-1] + ' ' + ops[i]]
        if lop:
# test if there is operator before/after braced group
          if dlev > 0:
            if elements[i] != '':
              return [3,'Missing operator before open braces at ' \
                  + "'" + ops[i-1] + ' ' + elements[i] + ' ' + ops[i] + "'"]
          elif ops[i] != 'not':
            if elements[i] == '':
              return [4,'Missing element between ' + ops[i-1] + ' ' + ops[i]]
        if cop:
          if dlev < 0:
            if elements[i] != '':
              return [3,'Missing operator after close braces at ' \
                + "'" + ops[i-1] + ' ' + elements[i] + ' ' + ops[i] + "'"]
          elif ops[i] != 'not':
            if elements[i] == '':
              return [4,'Missing element between ' + ops[i-1] + ' ' + ops[i]]
        else:
# test for missing operator between braced groups
          if ops[i-1] == '}' and ops[i] == '{':
            return [5,'Missing operator between braces } { ']
        lop = cop
      if lop:
        if elements[-1] == '':
          return [4,'Missing element after final ' + ops[-1]]
      else:
        if elements[-1] != '':
          return [3,'Missing operator after braces at ' \
                + "'" + ops[-1] + ' ' + elements[-1] + "'"]
    return [0,elements,ops,levels]

#-------------------------------------------------------------------------
  def expand(self,elements,ops,levels):
#-------------------------------------------------------------------------
    # Expand a command string to replace any aliases with their
    # full command
    initl = len(elements)
    i = 0
    while i < len(elements):
      toks = self.expand0(elements[i])
      if toks[0] < 0:
#   There is no expansion
        i = i + 1
      elif toks[0] > 0:
# Tokenising the expansion failed
        return toks
      else:
        elements.pop(i)
# There is an expansion which needs to be inserted into the lists
        if len(toks[1]) == 1:
          elements.insert(i,toks[1][0])
        else:
          elements.insert(i,'')
          elements0 =  toks[1]
          elements0.reverse()
          for e in elements0: elements.insert(i,e)
          elements.insert(i,'')
          ops0 = toks[2]
          ops0.reverse()
          ops.insert(i,'}')
          for o in ops0: ops.insert(i,o)
          ops.insert(i,'{')
#        print 'expanded elements',elements,'ops',ops
   
    
    if len(elements) != initl:
      l = self.getlevels(ops)
      if l[0] > 0: return l
      levels = l[1]
    return [0,elements,ops,levels]

#-------------------------------------------------------------------------
  def expand0(self,element):
#-------------------------------------------------------------------------
# Test if first word of element corresponds to a defined protocol
# or a user selection alias
    if element == '': return [-1]
    words = splitWords(element)
    fword = words[0]
    expanded = self.expand_selection_alias(fword)
    if expanded:
      return self.tokenise(expanded)
    else:
      return [-1] 

#-------------------------------------------------------------------------
  def getlevels(self,ops):
#-------------------------------------------------------------------------
    level = 0
    levels =[]
    for op in ops:
      if op == '{':
        level = level +1
        levels.append(level)
      elif op == '}':
        levels.append(level)
        level = level - 1
        if level < 0:
          return (2,'Unmatched closing brace',len(levels))
      else:
        levels.append(level)
    if level > 0:
      return (1,'Unmatched opening brace',0)
    else:
      return (0,levels)

#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
class ModelAnalysis(SelectionParser):
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------

    
#-------------------------------------------------------------------------
  def __init__(self):
#-------------------------------------------------------------------------
    self.selection_commands =  ['all','sphere','neighb','one_atom','termini',
      'ssetermini','modeltermini','ranges','slab','buried','ssbonds','same_names','symid']
    self.selection_methods =  ['selectAll','selectSphere', \
      'selectNeighb','selectOneAtom','selectTermini','selectSSETermini','selectModelTermini',
      'selectRanges','selectSlab','selectBuried',
      'selectSSbonds','selectEquivalentAtoms','selectSymid']

    


#-------------------------------------------------------------------------
  def analyse(self):
#-------------------------------------------------------------------------

    import string,mmut
    
    self.first_nmr_model = 1
    self.nmr_models=self.molHnd.GetNumberOfModels()
    if self.nmr_models < 2 :  self.nmr_models=0
    #print "analyse nmr_models",self.nmr_models
    if self.nmr_models:
      pMdl = None
      self.first_nmr_model = 0
      while (not pMdl) and self.first_nmr_model<self.nmr_models:
        self.first_nmr_model = self.first_nmr_model+1
        pMdl = self.molHnd.GetModel(self.first_nmr_model)
      #print "analyse first_nmr_model",self.first_nmr_model
    self.chains = []
    self.peptides = []
    self.nucleics = []
    self.solvent_chains = []
    self.monomers = []
    self.sites = []
    self.nres_solvent = 0
    self.molecule_type = []
    # Why is this line here?
    #self.mol_colour = ''
    self.chain_colours = []
    self.model_colours = []
    self.contains_hydrogen = 0
    self.alt_locs = []
    self.lipids = []
    self.ca_only = 0
    

    selmodel = 0
    if self.nmr_models>0: selmodel = self.first_nmr_model
    #print "selmodel",selmodel


    self.analyse_restypes()

    # diagnostic
    #ntot = self.molHnd.GetSelIndex(hydHnd,hydSel)    
    #print "Total number of atoms",ntot
    elements = []

    hydHnd = self.molHnd.NewSelection()
    hydSel = newPPCAtom()

    selindexp = intp()

    # Remove all common element types or those already known for
    # this structure
    self.molHnd.Select (hydHnd,STYPE_ATOM,selmodel,'*', \
              ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_NEW)

    for ele in MODELINFO().get_names(key='element',set=self.name_label):
      if ['H',' H','H '].count(ele): self.contains_hydrogen = 1        
      self.molHnd.Select (hydHnd,STYPE_ATOM,selmodel,'*', \
          ANY_RES,'*',ANY_RES,'*','*','*',ele,'*',SKEY_CLR)
      #nother = self.molHnd.GetSelIndex(hydHnd,hydSel)
      #print "ele,nother",ele,nother
    #nother = self.molHnd.GetSelIndex(hydHnd,hydSel)
    hydSel = mmut.GetAtomSelIndex(self.molHnd,hydHnd,selindexp)
    nother = selindexp.value()

    # Are there any novel element types
    while nother > 0:
      pcat = getPCAtom(hydSel,0)
      ele = string.rstrip(pcat.GetElementName())
      #print "extra element ",ele
      elements.append(ele)
      if ['H',' H','H '].count(ele): self.contains_hydrogen = 1
      self.molHnd.Select  (hydHnd,STYPE_ATOM,selmodel,'*', \
            ANY_RES,'*',ANY_RES,'*','*','*',ele,'*',SKEY_CLR)
      #nother = self.molHnd.GetSelIndex(hydHnd,hydSel)
      hydSel = mmut.GetAtomSelIndex(self.molHnd,hydHnd,selindexp)
      nother = selindexp.value()

    # If there are novel elements create/extend the  'element'
    # selection protocol for this model
    #print "new elements",elements
    if elements:
      status,protocols = MODELINFO().get_info( \
        set=self.name_label,info='selection_protocols',key='element')
      selele={}
      order = []
      if not status and protocols.has_key('element'):
        for key,value in protocols['element'].items():
          if key == 'ORDER':
            for item in value: order.append(item)
          else:
            selele[key]=value
      for ele in elements:
          order.append(ele)
          selele[ele] = ele
      #print 'protocols',order,selele
      selele['ORDER']=order
      # And save updates to .ccp4mg
      MODELINFO().add_info(set=self.name_label, \
        info='selection_protocols',key='element',value=selele)
    #print "Novel elements",elements


    # Test for alternate locations
    self.molHnd.Select  (hydHnd,STYPE_ATOM,selmodel,'*', \
        ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_NEW)
    self.molHnd.Select  (hydHnd,STYPE_ATOM,'/*/*/*/*:',SKEY_CLR)
    #nalt = self.molHnd.GetSelIndex(hydHnd,hydSel)
    hydSel = mmut.GetAtomSelIndex(self.molHnd,hydHnd,selindexp)
    nalt = selindexp.value()

    while nalt > 0:
      pcat = getPCAtom(hydSel,0)
      alt = pcat.GetAltLoc()
      #print "Appending", alt
      self.alt_locs.append(alt)
      self.molHnd.Select  (hydHnd,STYPE_ATOM,selmodel,'*', \
            ANY_RES,'*',ANY_RES,'*','*','*','*',alt,SKEY_XOR)
      #nalt = self.molHnd.GetSelIndex(hydHnd,hydSel)
      hydSel = mmut.GetAtomSelIndex(self.molHnd,hydHnd,selindexp)
      nalt = selindexp.value()
       
    #delPPCAtom(hydSel)
    self.molHnd.DeleteSelection(hydHnd)

  def getSitesSelection(self):
    siteinfo = self.molHnd.SiteInfo()
    sites = [x for x in siteinfo.split('\n') if x.startswith('SITE  ')]
    remarks = [x for x in siteinfo.split('\n') if x.startswith('REMARK 800')]

    theSites = {}
    for x in sites:
      if not theSites.has_key(x[11:14]):
        theSites[x[11:14]] = []
      theSites[x[11:14]].append(x[18:62])

    currentID = None
    siteDescs = {}
    for x in remarks:
      if x.startswith('REMARK 800 SITE_IDENTIFIER: '):
        currentID = x[len('REMARK 800 SITE_IDENTIFIER: '):len('REMARK 800 SITE_IDENTIFIER: ')+3]
      elif currentID and  x.startswith('REMARK 800 SITE_DESCRIPTION: '):
        siteDescs[currentID] = x[len('REMARK 800 SITE_DESCRIPTION: '):].strip()

    newSites = []
    for s,v in theSites.items():
      sel = []
      for vv in v:
        if len(vv[4:10].strip())>0:
          sel.append('//' + vv[4] + '/' + vv[5:10].strip() + '(' + vv[:3] + ')')
        if len(vv[15:21].strip())>0:
          sel.append('//' + vv[15] + '/' + vv[16:21].strip() + '(' + vv[11:14] + ')')
        if len(vv[26:32].strip())>0:
          sel.append('//' + vv[26] + '/' + vv[27:32].strip() + '(' + vv[22:25] + ')')
        if len(vv[37:43].strip())>0:
          sel.append('//' + vv[37] + '/' + vv[38:43].strip() + '(' + vv[33:36] + ')')
      if siteDescs.has_key(s):
        newSites.append({'name':s,'description':siteDescs[s],'selection':sel})

    return newSites

#---------------------------------------------------------------------------------------
  def analyse_restypes(self):
#---------------------------------------------------------------------------------------
    import mmut
    import selection_protocols
    nChains = intp()
    selindexp = intp()

    #chainTable = newPPCChain()
    #self.molHnd.GetChainTable(self.first_nmr_model,chainTable,nChains)

    chainTable = mmut.GetChainTable(self.molHnd,self.first_nmr_model,nChains)

    self.nChains = nChains.value()
    hydHnd = self.molHnd.NewSelection()
    hydSel = newPPCAtom()
    resHnd = self.molHnd.NewSelection()
    resSel = newPPCResidue()
    amino_acid = MODELINFO().get_names_string(key='amino_acid', \
                                  set=self.name_label)
    nucleic =  MODELINFO().get_names_string(key='nucleic_acid', \
                                  set=self.name_label)
    saccharide =  MODELINFO().get_names_string(key='saccharide', \
                                  set=self.name_label)
    nucleotide =  MODELINFO().get_names_string(key='nucleotide', \
                                  set=self.name_label)
    metal =  MODELINFO().get_names_string(key='metal', \
                                  set=self.name_label)

    self.chains = []
    self.monomers = []
    self.sites = []
    self.nres_solvent = 0
    self.peptides = []
    self.nucleics = []
    self.solvent_chains = []
    self.lipids = []
    self.saccharides = []
    self.nucleotides = []
    self.metals = []
    
    self._sites = self.getSitesSelection()
    for s in self._sites:
      for res in s['selection']:
         self.sites.append(res)

    for n in range(0,self.nChains):
      pc = getPCChain(chainTable,n)

      chainID = pc.GetChainID()
      nres = pc.GetNumberOfResidues()
      #print 'chain',chainID,nres
      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
	ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_NEW)
      #nrestot =self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nrestot = selindexp.value()

      self.molHnd.SelectAminoNotHet(resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID,ANY_RES,'*',ANY_RES,'*',amino_acid,'*','*','*',SKEY_XOR)
      #nresaa = nrestot - self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nresaa = nrestot - selindexp.value()


      ### Test the GetSequence method
      #tmpselhnd = self.molHnd.NewSelection()
      #print "tmpselhd",tmpselhnd
      #self.molHnd.Select(tmpselhnd,STYPE_ATOM,resHnd,SKEY_NEW)
      #print "after propagate"
      #sequence = self.molHnd.GetSequence(tmpselhnd)
      #print "SEQUENCE",sequence
      #del tmpselhnd


      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
        ANY_RES,'*',ANY_RES,'*',nucleic,'*','*','*',SKEY_XOR)
      #nresna = (nrestot - nresaa) - self.molHnd.GetSelIndex(resHnd,resSel)
      #print "nresna",chainID,nresna
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nresna = (nrestot - nresaa) - selindexp.value()
      
      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
        ANY_RES,'*',ANY_RES,'*',saccharide,'*','*','*',SKEY_XOR)
      #nressac = (nrestot - nresaa - nresna) - self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nressac = (nrestot - nresaa -nresna) - selindexp.value()
      #print "nressac",chainID,nressac

      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
        ANY_RES,'*',ANY_RES,'*',nucleotide,'*','*','*',SKEY_XOR)
      #nressac = (nrestot - nresaa - nresna) - self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nresnt = (nrestot - nresaa -nresna - nressac) - selindexp.value()
      #print "nressac",chainID,nressac

      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
        ANY_RES,'*',ANY_RES,'*',metal,metal,'*','*',SKEY_XOR)
      #nresmet = (nrestot - nresaa - nresna -nressac) - self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nresmet = (nrestot - nresaa - nresna - nressac -nresnt) - selindexp.value()
      #print "nresmet",chainID,nresmet

      if nresna:
        # Residue names with conflict between nucleic or element
        conflicts =  MODELINFO().get_names_string(key='conflicts', \
                                  set=self.name_label)
        if conflicts:
          tmpHnd = self.molHnd.NewSelection()
          self.molHnd.Select  (tmpHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
          ANY_RES,'*',ANY_RES,'*',conflicts,'*','*','*',SKEY_XOR)
          #nconflicts = self.molHnd.GetSelIndex(tmpHnd,resSel)
          resSel = mmut.GetResidueSelIndex(self.molHnd,tmpHnd,selindexp)
          nconflicts = selindexp.value()
          #print 'ModelAnalysis nconflicts',chainID,nconflicts
          if nconflicts:
            for ir in range(0,nconflicts):
              pcres = getPCResidue(resSel,ir)
              if pcres.GetNumberOfAtoms() == 1 and string.strip(pcres.GetAtom(0).name) == string.strip(pcres.GetResName()):
                self.monomers.append(self.molHnd.AtomLabel_residue1(pcres))
                self.molHnd.SetCustomRestype(string.strip(pcres.GetResName()),mmut.RESTYPE_NONPOLY,True)
                nresna = nresna - 1
          self.molHnd.DeleteSelection(tmpHnd)
          #print 'ModelAnalysis after conflicts monomers',self.monomers,nresna

      
      solvent = MODELINFO().get_names_string(key='solvent', \
                             set=self.name_label)
      solute = MODELINFO().get_names_string(key='solute', \
                             set=self.name_label)

      #chainselHnd = self.molHnd.NewSelection()
      #self.molHnd.Select  (chainselHnd,STYPE_ATOM,self.first_nmr_model,chainID,ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_NEW)
      #import mmut
      #have_lipids = mmut.MMUTLipidAnalyse(self.molHnd,chainselHnd)
      #print "have_lipids",have_lipids

      if solute: solvent = solvent+','+solute
      #print "analyse solvent",solvent
      self.molHnd.Select  (resHnd,STYPE_RESIDUE,self.first_nmr_model,chainID, \
        ANY_RES,'*',ANY_RES,'*',solvent,'*','*','*',SKEY_XOR)
      #nreslig = self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nreslig = selindexp.value()
      nressol = nrestot - ( nresaa + nresna + nreslig )
      self.nres_solvent =  self.nres_solvent + nressol
      if nresaa > 0:
         if chainID not in self.peptides: self.peptides.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      if nresna > 0:
         if chainID not in self.nucleics: self.nucleics.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      if nressac > 0:
         if chainID not in self.saccharides: self.saccharides.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      if nresnt > 0:
         if chainID not in self.nucleotides: self.nucleotides.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      if nresmet > 0:
         if chainID not in self.metals: self.metals.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      if nressol> 0:
         if chainID not in self.solvent_chains: self.solvent_chains.append(chainID)
         if chainID not in self.chains: self.chains.append(chainID)
      #if have_lipids> 0: self.lipids.append(chainID)  # This isn't used for anything yet though, I think.

      #Deal with the remaining residues - check if they are novel
      #amino acid/nucleic/solvent/solute and if so then add them
      #to the selection definition for those types.
      #If not then add them to the list of monomers
      novel_restype = []
      if nreslig > 0:
        import re
        import mmut
        for ir in range(0,nreslig):
          pcres = getPCResidue(resSel,ir)
          resname = pcres.GetResName()
          resid = self.molHnd.AtomLabel_residue1(pcres)
          restype = self.molHnd.GetRestypeCode(pcres)
          #print "monomer",resname,restype,mmut.RESTYPE_PEPTIDE,mmut.RESTYPE_DPEPTIDE,mmut.RESTYPE_LPEPTIDE

          isNTermACE = False
          if resname == 'ACE':
            # Check if covalently bound to N-terminus.
            isNTermACE = self.molHnd.isNTerminusBound(pcres)
            print "isNTerm",isNTermACE
          isAminoHet = False
          if [mmut.RESTYPE_PEPTIDE,mmut.RESTYPE_DPEPTIDE,mmut.RESTYPE_LPEPTIDE].count(restype) or isNTermACE:
            print "Checking",resid,pcres,pcres.nAtoms, resname in selection_protocols.amino_acid
            if pcres and pcres.nAtoms > 0 and resname in selection_protocols.amino_acid:
              print "Checking 2",resid
              at = pcres.GetAtom(0)
              isAminoHet = at.Het
              #FIXME NOT GOOD ENOUGH! Need to check if peptide bound. 
            if not novel_restype.count(resname) and not isAminoHet:
              MODELINFO().add_name(key='amino_acid',set=self.name_label,new_name=resname)
            if isAminoHet:
              self.monomers.append(resid)
          elif [mmut.RESTYPE_NUCL,mmut.RESTYPE_DNA, mmut.RESTYPE_RNA].count(restype):
            if not novel_restype.count(resname):
              MODELINFO().add_name(key='nucleic_acid',set=self.name_label,new_name=resname)
          elif [mmut.RESTYPE_SACH,mmut.RESTYPE_DSACH, mmut.RESTYPE_LSACH].count(restype):
            if not novel_restype.count(resname):
              MODELINFO().add_name(key='saccharide',set=self.name_label,new_name=resname)
          elif [mmut.RESTYPE_SOLVENT].count(restype):
            if not novel_restype.count(resname):
              MODELINFO().add_name(key='solvent',set=self.name_label,new_name=resname)
          elif [mmut.RESTYPE_SOLUTE].count(restype):
            if not novel_restype.count(resname):
              MODELINFO().add_name(key='solute',set=self.name_label,new_name=resname)
          elif [mmut.RESTYPE_METAL].count(restype):
            if not novel_restype.count(resname):
              MODELINFO().add_name(key='metal',set=self.name_label,new_name=resname)
          else:
            #Treat it as a 'monomer'
            # If there are multiple models then rename will
            # specify model 1 when we want all models in the
            # monomer menu
            self.monomers.append(resid)
          if not isAminoHet or (resname == 'ACE' and not isNTermACE):
            novel_restype.append(resname)

    # Set these restypes as monomers in the ModelInfo
    if self.monomers:
      #print 'ModelAnalysis monomers',self.monomers
      import types
      status,sel_prot = MODELINFO().get_info( \
        set=self.name_label, info='selection_protocols',key='monomer')
      if not sel_prot.has_key('monomer'): sel_prot['monomer']= { 'ORDER' : [] }
      for mon in  self.monomers:
        import re
        mon_type = re.sub('\).*','',( re.sub('.+\(','',mon)))
        if not sel_prot['monomer']['ORDER'].count(mon_type): sel_prot['monomer']['ORDER'].append(mon_type)
      #print "ModelAnalysis.analyse Seting ModelInfo monomer",self.name_label,sel_prot['monomer']['ORDER']
      ret = MODELINFO().add_info(set=self.name_label, \
      info='selection_protocols',key='monomer',value=sel_prot['monomer'])


    # 
    #print "self.nucleics,peptides,chains", self.nucleics,self.peptides,self.chains
    if len(self.peptides)>0:
      self.molecule_type.append('PROTEIN')
      self.molHnd.Select (hydHnd,STYPE_ATOM,1,'*', \
              ANY_RES,'*',ANY_RES,'*',amino_acid,'N,C,O','*','*',SKEY_NEW)
      #nmainch = self.molHnd.GetSelIndex(hydHnd,hydSel)
      hydSel = mmut.GetAtomSelIndex(self.molHnd,hydHnd,selindexp)
      nmainch = selindexp.value()
      #print "nmainch",nmainch
      if nmainch == 0: self.ca_only = 1
    if len(self.nucleics)>0:
      self.molecule_type.append('NUCLEIC')
    if len(self.saccharides)>0:
      self.molecule_type.append('SACCHARIDE')
    if len(self.nucleotides)>0:
      self.molecule_type.append('NUCLEOTIDES')
    if len(self.metals)>0:
      self.molecule_type.append('METAL')
    if len(self.monomers)>0:
      self.molecule_type.append('MONOMER')
    if len(self.lipids)>0:
      self.molecule_type.append('LIPID')

    #print "ca_only",self.ca_only

    #delPPCChain(chainTable)
    if resHnd: self.molHnd.DeleteSelection(resHnd)
    if hydHnd: self.molHnd.DeleteSelection(hydHnd)
    #if resSel: delPPCResidue(resSel)


#----------------------------------------------------------------------
  def get(self,mode=''):
#----------------------------------------------------------------------
    if ['nmr_models','chains','peptides','nucleics','solvent_chains', \
        'monomers','sites','nres_solvent','molecule_type','contains_hydrogen', \
        'alt_locs','lipids'].count(mode):
      return getattr(self,mode,'')
   
#----------------------------------------------------------------------
  def equivalent_monomers(self,copy_from_params=''):
#----------------------------------------------------------------------
    if not copy_from_params.has_key('monomers') or \
       not copy_from_params['monomers']: return []
    import re
    equiv_mons = []
    selHnd = self.molHnd.NewSelection()
    resSel = newPPCResidue()
    selindexp = intp()
    for item in copy_from_params['monomers']:
      if self.monomers.count(item):
        equiv_mons.append(item)
      else:
        t = re.search('\(.*\)' , item)
        if t:
          restype = t.group().strip('()')
          self.molHnd.Select  (selHnd,STYPE_RESIDUE,1,'*', \
              ANY_RES,'*',ANY_RES,'*',restype,'*','*','*',SKEY_NEW)
          #nr = self.molHnd.GetSelIndex(selHnd,resSel)
          resSel = mmut.GetResidueSelIndex(self.molHnd,selHnd,selindexp)
          nr = selindexp.value()
          if nr>0:
            for ir in range(0,nr):
              pcres = getPCResidue(resSel,ir)
              resid = self.molHnd.AtomLabel_residue1(pcres)
              #print "resid",resid
              equiv_mons.append(resid)
            
    self.molHnd.DeleteSelection(selHnd)
    return equiv_mons
  
  
#------------------------------------------------------------------------
  def get_peptide_menu(self):
#-----------------------------------------------------------------------
    mm = []
    if len(self.peptides) > 1:
      for pept in self.peptides:
        mm.append ('//' + pept)
    return mm

#----------------------------------------------------------------------
  def get_nucleic_menu(self):
#----------------------------------------------------------------------
    mm = []
    if len(self.nucleics) > 1:
      for nucl in self.nucleics:
          mm.append ('//' + nucl)
    return mm

#----------------------------------------------------------------------
  def get_monomer_menu(self):
#----------------------------------------------------------------------
    #mm = []
    #if len(self.monomers) > 0:
    #  for mon in self.monomers:
    #    mm.append (mon)
    return self.monomers

#----------------------------------------------------------------------
  def get_nmr_models_menu(self):
#----------------------------------------------------------------------
    menu = []
    alias = []
    if self.nmr_models:
      for i in range(self.first_nmr_model,self.nmr_models+1):
        menu.append('/'+str(i))
        alias.append('/'+str(i))
    else:
      menu.append('/1')
      alias.append('/1')
    symm_mates = self.model_symmetry.getSymmetrymates()
    if symm_mates.get(symops,[]):
      for i in range(len(symm_mate['symops'])):
        model = self.nmr_models + 1 + i
        mod = '/'+str(model) + ' ' + \
              symm_mates['symops'][i]+' '+ symm_mates['tran_a'][i]+' '+ \
                symm_mates['tran_b'][i]+' '+ symm_mates['tran_c'][i]
        menu.append(mod)
        alias.append('/'+str(model))
    #print "model_selection get_nmr_models_menu",[menu,alias]
    return [menu,alias]


#----------------------------------------------------------------------
  def expand_selection_alias (self,alias):
#----------------------------------------------------------------------
    # Is it a standard alias?
    expanded = MODELINFO().expand_selection_alias(alias=alias, \
                                               set=self.name_label)
    if expanded: return expanded
    
    #test if a molecule dependent alias
    if ['sites'].count(alias) > 0:
      if alias == 'sites':
        if self.sites:
          expn = self.sites[0]
          for mon in self.sites[1:]:
            expn = expn + ' or '+mon
          #print "expn",expn
          return self.command_to_list(expn)[1]
        else:
          return None
        
    if ['monomers'].count(alias) > 0:
      if alias == 'monomers':
        if self.monomers:
          expn = self.monomers[0]
          for mon in self.monomers[1:]:
            expn = expn + ' or '+mon
          #print "expn",expn
          return self.command_to_list(expn)[1]
        else:
          return None
        
    # Test is a named object
    if hasattr(self,'objnames') and self.objnames.count(alias):
      ii =  self.objnames.index(alias)
      if self.objinsts[ii].object_type == 'moldisp':
        return self.objinsts[ii].selection
          
    return None

  
#--------------------------------------------------------------------------
  def nAtoms (self,selhnd):
#---------------------------------------------------------------------------
    selAtoms = newPPCAtom()
    selindexp = intp()
    #nselatoms =self.molHnd.GetSelIndex(selhnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(self.molHnd,selhnd,selindexp)
    nselatoms = selindexp.value()
    #print "Number of selected atoms",nselatoms
    #delPPCAtom(selAtoms) 
    return nselatoms
#--------------------------------------------------------------------------
  def nResidues (self,selhnd):
#---------------------------------------------------------------------------
    selRes = newPPCResidue()
    selindexp = intp()
    #nselres =self.molHnd.GetSelIndex(selhnd,selRes)
    selRes = mmut.GetResidueSelIndex(self.molHnd,selhnd,selindexp)
    nselres = selindexp.value()
    #print "Number of selected atoms",nselatoms
    #delPPCAtom(selAtoms) 
    return nselres

#----------------------------------------------------------------------
  def get_selobj_menu(self,all=0,intermol=0,user_defined=0,exclude=[]):
#----------------------------------------------------------------------
    from utils import tclSafe
    mm = []
    aa = []
    i = 0
    if all:
      mm.append('All atoms')
      aa.append('all')
    for obj in self.get_dispobj(object_type = 'MolDisp'):
      if not exclude.count(obj.name):
        mm.append(obj.SelHandle.getLabel())
        aa.append(obj.name)
    if intermol:
      for mol in get_dataobj(object_type='MolData'):
        if mol != self:
          mmlist,aalist = mol.get_selobj_menu(exclude=exclude)
          for item in mmlist:
            mm.append(mol.name + ' ' + item)
          for item in aalist:
            aa.append(mol.name + '#' + item)
    if user_defined:
      status,user_selection=MODELINFO().get_info( \
        set=self.name_label,info='user_selection')
      for key in user_selection.keys():
        if not mm.count(key):
          mm.append(tclSafe(key))
          aa.append(key)
    #print "get_selobj_menu",mm,aa
    return mm,aa


#----------------------------------------------------------------------
  def interpret_selobj_alias(self,selstring,return_selHnd=0):
#----------------------------------------------------------------------
    """
    Interpret the selection string returned from the GUI and
    return either: if return_selHnd=0 => [mode,selection_string]
    or if return_selHnd=0 => [mode,selHnd]
    The mode is 'MolDisp' or 'user' .  If it is user then the
    calling function should delete the selHnd 
    """
    #print "interpret_selobj_alias",selstring
    if not selstring: return None
    if self.objnames.count(selstring):
      if not return_selHnd:
        return ['MolDisp',self.objinsts[self.objnames.index(selstring)]]
      else:
        dispobj =  self.objinsts[self.objnames.index(selstring)]
        #print "dispobj",dispobj,dispobj.selHnd
        selHnd = dispobj.get_selHnd()
        if selHnd<0:
          return None
        else:
          return ['MolDisp',selHnd]
          
    elif selstring == 'all':
      if not return_selHnd:
        return ['ALL','ALL']
      else:
        rv = self.parse_selection('all')
        if rv[0] == 0 and rv[1] > 0:
          selHnd = rv[1]
          return ['user',selHnd]
        else:
          return None
    else:
      status,user_selection=MODELINFO().get_info( \
        set=self.name_label,info='user_selection',key=selstring)
      #print "interpret_selobj_alias",status,user_selection
      if not status and user_selection.has_key(selstring):
        if not return_selHnd:
          return ['user',selstring]
        else:
          rv = self.parse_selection(user_selection[selstring])
          if rv[0] == 0 and rv[1] > 0:
            selHnd = rv[1]
            return ['user',selHnd]
          else:
            return None
          
    # Try assuming the first word is name of another data object
    if string.find(selstring,'#')<0: return None
    
    from model import moldata
    mol,disp = string.split(selstring,'#')
    molobj = get_dataobj(mol)
    if not molobj: return None
    ret =  molobj[0].interpret_selobj_alias(disp,return_selHnd=return_selHnd)
    return ret

#------------------------------------------------------------------
  def if_user_alias_active(self,user_alias):
#------------------------------------------------------------------
# test if the user selection is been used by any of the current
# objects
    retobj = []
    for obj in self.objinsts:
      if obj.selection_label == user_alias: retobj.append(obj)
    return len(retobj)

#------------------------------------------------------------------
  def if_user_colour_active(self,user_colour):
#------------------------------------------------------------------
# test if the user colour scheme is been used by any of the current
# objects
    retobj = []
    for obj in self.get_dispobj(object_type='MolDisp'):
      if obj.colour_mode == 'rules' and obj.user_scheme and obj.user_scheme[0] == user_colour:
        retobj.append(obj)
    return len(retobj)

#------------------------------------------------------------------
  def getSSE(self,atomptr=None,atom_name=''):
#------------------------------------------------------------------
     if not atomptr: atomptr = self.interpretAtomID(self,atom_name)
     if not atomptr: return ''
     import pygl_coord
     mask = pygl_coord.inta(7)
     for i in range(0,7): mask[i] = 1
     s0 = self.molHnd.ListSecStructure(mask,atomptr)
     if not s0:
       return ''
     else:
       return string.split(s0)[1]


#-----------------------------------------------------------------------
  def interpretAtomID(self,mol=None,atom_name='',force_one_atom=0,selHnd=-1):
#-----------------------------------------------------------------------
    selindexp = intp()
    if selHnd>0:
      selhnd = selHnd
    else:
      selhnd = mol.molHnd.NewSelection()
      rv = mol.molHnd.Select(selhnd,STYPE_ATOM,atom_name,SKEY_NEW)
      #print "interpretAtomID rv",rv
      if rv: return None
    selAtoms = newPPCAtom()
    #nselatoms =mol.molHnd.GetSelIndex(selhnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(mol.molHnd,selhnd,selindexp)
    nselatoms = selindexp.value()
    #print "nselatoms",nselatoms
    if nselatoms < 1:
      retval = None
    elif not force_one_atom or nselatoms==1:
      retval =  getPCAtom(selAtoms,0)
    else:
      retval = None
    #delPPCAtom(selAtoms)

    if selHnd<0: mol.molHnd.DeleteSelection(selhnd)
    return retval

#-----------------------------------------------------------------------
  def standardiseAtomID(self,atom_name):
#-----------------------------------------------------------------------

    pAtom = self.interpretAtomID(self,atom_name)
    #print 'standardiseAtomID pAtom',pAtom
    if not pAtom:return ''
    new = pAtom.GetAtomID()[0]
    #print 'standardiseAtomID new',new
    # Remove the atom name
    if new.count('['):new = new[0:new.index('[')]
    return new


#-----------------------------------------------------------------------
  def interpretResidueID(self,atom_name,ret='',ignore_name=0):
#-----------------------------------------------------------------------
    selRes = None
    selhnd = self.molHnd.NewSelection()
    rv = self.molHnd.Select(selhnd,STYPE_RESIDUE,atom_name,SKEY_NEW)
    selindexp = intp()
    #print "interpretResidueID",atom_name,rv
    if rv and ignore_name :
      # Try removing residue name - assume a user mistake      
      import re
      atom_name=re.sub(r'\(.*\)','',atom_name)
      rv = self.molHnd.Select(selhnd,STYPE_RESIDUE,atom_name,SKEY_NEW)
    if rv:
      retval = [0,None]
    else:
      selRes = newPPCResidue()
      #nselres =self.molHnd.GetSelIndex(selhnd,selRes)
      selRes = mmut.GetResidueSelIndex(self.molHnd,selhnd,selindexp)
      nselres = selindexp.value()
      if nselres == 0:
        retval = [0,None]
      elif nselres != 1:
        # if ret is set then return either first or last res in
        # this selection
        if ret == 'first':
          retval =  [nselres,getPCResidue(selRes,0)]
        elif ret == 'last':
          retval =  [nselres,getPCResidue(selRes,nselres-1)]
        else:
          retval = [nselres,None]
      else:
        retval =  [1,getPCResidue(selRes,0)]
        
    #if selRes: delPPCResidue(selRes)
    self.molHnd.DeleteSelection(selhnd)
    return retval

#-------------------------------------------------------------------
  def interpretRanges(self,ranges_first='',ranges_last='',keep_order=0,
                      extend=0):
#-------------------------------------------------------------------
    nres_first = 0
    nres_last =0
    first = None
    last = None
    seli = []
    #print "interpretRanges extend",ranges_first,ranges_last,extend
    if extend and not ranges_last:
      split_id = self.splitAtomID(ranges_first)
      if len(split_id)>=3 and string.find(split_id[2],'-')>=0:
        split_res = string.split(split_id[2],'-')
        ranges_first = '/'+split_id[0]+'/'+split_id[1]+'/'+split_res[0]
        ranges_last = '/'+split_id[0]+'/'+split_id[1]+'/'+split_res[1]
      else:
        ranges_last = ranges_first
    #print "interpretRanges extend",ranges_first,ranges_last,extend

    if ranges_first:
      nres_first,first = self.interpretResidueID(ranges_first,ret='first',ignore_name=1)
      if nres_first:
        if (self.nmr_models or self.model_symmetry.get_symmetry_models()) and \
           re.match('/0',ranges_first):
          modelNum = 0
        else:
          modelNum =  first.GetModelNum()
      #print "modelNum",modelNum
    if ranges_last:
      nres_last,last = self.interpretResidueID(ranges_last,ret='last',ignore_name=1)
        
    if not nres_first and not nres_last:
      return [None,'']
    elif not nres_last:
      seli =  [None,ranges_first]
    elif not nres_first:
      seli = [None,ranges_last]
    # Both in same chain - convert to mmdb selection format
    elif first.GetChainID() == last.GetChainID() and (not extend):
      first_id = str(first.GetSeqNum())
      ins = first.GetInsCode()
      if ins: first_id = first_id+'.'+ins
      last_id = str(last.GetSeqNum())
      ins = last.GetInsCode()
      if ins: last_id = last_id+'.'+ins
      if keep_order or last.index > first.index:
        seli = [None,'/'+str(modelNum)+'/'+first.GetChainID()+'/'+first_id+ \
             '-' +last_id]
      else:
        seli = [None,'/'+str(modelNum)+'/'+first.GetChainID()+'/'+last_id+ \
             '-' +first_id]
    else:
      if extend:
        seli = [None,'range_cn '+ranges_first+' '+ ranges_last]
      else:
        seli = [None,'range '+ranges_first+' '+ ranges_last]

    return seli


#---------------------------------------------------------------------------
  def convertAtomID ( self,  out = 'residue', atomID = '' ):
#---------------------------------------------------------------------------
    import string
    if out == 'all': return '/*/*/*.*/*:*'
    order = ['model','chain','residue','atom']
    if not order.count(out): return atomID
    ss = string.split(atomID,'/')
    #print 'convertAtomID',ss
    id = ''
    for n in range(1,min(order.index(out)+2,len(ss))):      
      id = id + '/' + ss[n]
    print "convertAtomID atomID",atomID,'id',id
    if string.strip(ss[n])=='': id = id + '/'
    return id

#-------------------------------------------------------------------------
  def splitAtomID ( self, atomID='' ,split_res=0,res_range=0):
#-------------------------------------------------------------------------
    model = ''
    chain = ''
    res = ''
    res2 = ''
    atom = ''
    aid = ''
    atomID = atomID.strip()
    if atomID:
      aid = string.split(atomID,'/')
      if len(aid) <= 1:
        # there are no separators
        atom = atomID
      else:
        # If atomID begins with a slash and model number then expect
        # first item (before first slash) to be the blank
        # If this is not so then insert a model number 
        if aid[0] and len(aid)<5 :
          aid.insert(0,'')
          aid.insert(1,'1')
        if len(aid) >= 2: model = aid[1]    
        if len(aid) >= 3: chain = aid[2]    
        if len(aid) >= 5:
          atom = string.split(aid[4],'[')[0]
          # mmdb GetAtomID returns the altLoc after the element type
          if len(string.split(aid[4],']'))>1:
            atom = atom+string.split(aid[4],']')[1]
        if len(aid) >= 4:
          if aid[3].count('-'):
            res,res2 = aid[3].split('-')[0:2]
          else:
            if split_res:
              res = string.split(string.split(aid[3],'(')[0],'.')
              if len(res)==1: res.append('')
              return [model,chain,res[0],res[1],atom]
            else:
              res = aid[3]
    #print 'splitAtomID',atomID,aid,[model,chain,res,res2,atom]
    if res_range:
      return [model,chain,res,res2,atom]
    else:
      return [model,chain,res,atom]

#----------------------------------------------------------------------
  def getResidueID(self,first_atom=None,last_atom=None):
#----------------------------------------------------------------------
    '''
    Return text of form chain_id/first_res_id-last_res_id
    PCAtom first_atom,last_atom
    same_chain - flag if residues must be same chain
    '''
    chnid = first_atom.GetChainID()
    if not chnid: chnid = ' '
    first = '/'+str(first_atom.GetModelNum())+'/' +chnid +'/'+ str(first_atom.GetSeqNum())
    if first_atom.GetInsCode(): first = first + '.' + first_atom.GetInsCode()
  
    if not last_atom:
      return first
    else:
      diff_chain = 1
      if first_atom.GetChainID() == last_atom.GetChainID() and \
         first_atom.GetModelNum() == last_atom.GetModelNum(): diff_chain=0
      last = str(last_atom.GetSeqNum())
      if last_atom.GetInsCode(): last = last + '.' + last_atom.GetInsCode()

      chnid = last_atom.GetChainID()
      if not chnid: chnid = ' '
      if  diff_chain: 
        last = '/'+str(last_atom.GetModelNum())+'/' + chnid + '/' + last
      range = first + '-' +last
      if not diff_chain:
        last = '/'+str(last_atom.GetModelNum())+'/' + chnid + '/' + last

      return [range,first,last,diff_chain]
   
#-----------------------------------------------------------------
  def allselectedatoms(self):
#-----------------------------------------------------------------
    """
    Return a mmdb selection containing all the atoms selected
    in the (possibly > 1) display objects
    """
    l = len(self.objinsts)
    if l == 0:
      return [0]
    else:
     allHnd = self.molHnd.NewSelection()
     allSel = newPPCAtom()
     selindexp = intp()
     for sel in self.objinsts:
       self.molHnd.Select(allHnd,STYPE_ATOM,sel.selHnd,SKEY_OR)
     #nat=self.molHnd.GetSelIndex(allHnd,allSel)
     allSel = mmut.GetAtomSelIndex(self.molHnd,allHnd,selindexp)
     nat = selindexp.value()
     return [nat,allSel,allHnd]
  
#-------------------------------------------------------------------------
  def parse_selection ( self,defn='',command='',test=0 , cleanup=1, dispobj=''):
#-------------------------------------------------------------------------
# Parse the input selection string command and return a tuple rv
# rv[0] is status 0= success
# If rv[0]==0 then rv[1] is selection handle of selected atoms
# if  rv[0] != 0 then rv[1] is string with error message

# If test != 0 then the final selection handle is deleted
# and the number of selected atoms is returned. 
#This mode is used to test validity of input selection string
    #print 'PARSE_SELECTION defn',defn,'command',command

    #try:
    if 1:
      if defn:
        t = Cselect_tree('TOP','',self,dispobj=dispobj)
        if t.set(defn):
          t.cleanup()
          return [5,"Can not interpret internal selection list"]
      elif not command:
        #print "no selection"
        selHnd = self.molHnd.NewSelection()
        return [0,selHnd]
      else:
        toks = self.tokenise(command)
        if toks[0] > 0:
          return toks
        else:
          #toks=self.expand(toks[1],toks[2],toks[3])
          t = Cselect_tree('TOP','',self,dispobj=dispobj)
          t.import_command_string(toks[1],toks[2],toks[3])
          
      rv = t.apply()
      #s = t.export_command_string()
      #pint "command string",ss,'natoms',t.nSelAtoms(),rv
      if rv[0] == 0: 
        if test:
          na = t.nSelAtoms()
          list = t.get()
          rv = [0,na,list]
          t.cleanup()
          del t
        else:
          if cleanup:
            rv = [0,t.selHnd]
            t.cleanup(1)
            del t
          else:
            rv = [0,t.selHnd,t]
      else:
         t.cleanup()
         del t
      return rv
    
    #except:
    #  return [1,'Error parsing selection']



  
#--------------------------------------------------------------------------
  def selectAll(self,skey=SKEY_OR,selhnd=None):
#--------------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']
    #self.molHnd.SelectAtoms(selhnd,0,0,skey)
    self.molHnd.SelectAtoms(selhnd, 0,"*",ANY_RES,"*",ANY_RES,"*", \
                               "*","*","*","*",skey )
    rv = 0
    if rv > 0:
      return [1,'Error selecting all atoms']
    else:
      return [0,selhnd]

#--------------------------------------------------------------------------
  def selectSphere (self,x=0.0,y=0.0,z=0.0,r=10.0,type='atom', \
	skey=SKEY_OR, selhnd=None):
#--------------------------------------------------------------------------

    x = float(x)
    y = float(y)
    z = float(z)
    r = float(r)
    if not selhnd: return [1,'No selection handle provided']

#    print 'sphere',x,y,z,r,type,skey,selhnd

    if type == 'atom':
      self.molHnd.SelectSphere(selhnd,STYPE_ATOM,x,y,z,r,skey)
      return

# Select whole residue/chain etc

    tmpselHnd = self.molHnd.NewSelection()
    if type == 'residue':
      self.molHnd.SelectSphere(tmpselHnd,STYPE_RESIDUE,x,y,z,r,SKEY_NEW)
    elif type == 'chain':
      self.molHnd.SelectSphere(tmpselHnd,STYPE_CHAIN,x,y,z,r,SKEY_NEW)
    elif type == 'model':
      self.molHnd.SelectSphere(tmpselHnd,STYPE_MODEL,x,y,z,r,SKEY_NEW)


    retval = self.molHnd.Select(selhnd,STYPE_ATOM,tmpselHnd,skey)
    self.molHnd.DeleteSelection(tmpselHnd)
    return retval


#---------------------------------------------------------------------------
  def selectNeighbRanges(self,cid='',model='',maxd=5.0,mind=0.0, qualifier='all',invert=0,
                                      allModels=False,**keywords):
#---------------------------------------------------------------------------
    mind = safeFloat(mind,0.0)
    maxd = safeFloat(maxd,15.0)

    # Get the 'central' atoms
    if model and data(model):
      dobj = data(model)
    else:
      dobj = self
    rv = dobj.parse_selection(command=cid)
    if rv[0]: return rv
    central_selHnd  = rv[1]
    selAtoms = newPPCAtom()
    selindexp = intp()
    #nselatoms = dobj.molHnd.GetSelIndex(central_selHnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(dobj.molHnd,central_selHnd,selindexp)
    nselatoms = selindexp.value()
    #print "selectNeighbRanges nselatoms ",nselatoms
    if nselatoms <= 0:
      dobj.molHnd.DeleteSelection(central_selHnd)
      return([1,'No atoms selected for centre of neighbourhood'])

    # Get all close atoms
    neighb_selHnd = self.molHnd.NewSelection()
    self.molHnd.SelectNeighbours(neighb_selHnd,STYPE_ATOM,selAtoms, \
	  nselatoms,mind,maxd,SKEY_NEW)
    dobj.molHnd.DeleteSelection(central_selHnd)

    if qualifier != 'all':
      rv = self.parse_selection(command=qualifier)
      #print "selectNeighbRanges parse group",group,rv,self.nAtoms(neighb_selHnd)
      if  rv[0]:
        self.molHnd.DeleteSelection(neighb_selHnd)
        return rv
      group_selHnd = rv[1]
      self.molHnd.Select(neighb_selHnd,STYPE_ATOM,group_selHnd,SKEY_AND)
      self.molHnd.DeleteSelection(group_selHnd)
      #print 'selectNeighbRanges after parse group',self.nAtoms(neighb_selHnd)
      
    res_selHnd = self.molHnd.NewSelection()
    self.molHnd.Select(res_selHnd,STYPE_RESIDUE,neighb_selHnd,SKEY_NEW)
    self.molHnd.DeleteSelection(neighb_selHnd)

    rv = self.parse_selection(command='peptide')
    if not rv[0]:
      pept_selHnd = self.molHnd.NewSelection()
      self.molHnd.Select(pept_selHnd,STYPE_RESIDUE,rv[1],SKEY_NEW)
      self.molHnd.DeleteSelection(rv[1])
      if invert:
        self.molHnd.Select(pept_selHnd,STYPE_RESIDUE,res_selHnd,SKEY_CLR)
      else:
        self.molHnd.Select(pept_selHnd,STYPE_RESIDUE,res_selHnd,SKEY_AND)
        
    resID_list = self.selection2RangeList(pept_selHnd,allModels=allModels)
        
    self.molHnd.DeleteSelection(res_selHnd)
    self.molHnd.DeleteSelection(pept_selHnd)
    return [0,resID_list]
  
#---------------------------------------------------------------------------
  def selectInvertRanges(self,cid='', qualifier='peptide',allModels=False, **keywords):
#---------------------------------------------------------------------------
    '''
    Return a list of residue ranges that is the invert of the cid list
    '''
    import types
    if isinstance(cid,types.ListType):
      cid_list = cid
    else:
      cid_list = cid.split(',')
       
    # Get the selected residues
    resHnd = self.molHnd.NewSelection()
    for cid in cid_list:
      self.molHnd.Select(resHnd,STYPE_RESIDUE,cid,SKEY_OR)    
    #print 'selectInvertRanges selected',self.nResidues(resHnd)
  
    # Get 'all' the residues (probably all the peptide)
    rv = self.parse_selection(command=qualifier)
    if  rv[0]:
       self.molHnd.DeleteSelection(resHnd)
       return rv
    allAtomHnd = rv[1]
    allResHnd = self.molHnd.NewSelection()
    self.molHnd.Select(allResHnd,STYPE_RESIDUE,allAtomHnd,SKEY_NEW)
    self.molHnd.DeleteSelection(allAtomHnd)
    #print 'selectInvertRanges all',self.nResidues(allResHnd)

    # Exclude the seelcted ranges
    self.molHnd.Select(allResHnd,STYPE_RESIDUE,resHnd,SKEY_CLR)
    #print 'selectInvertRanges all-selected',self.nResidues(allResHnd)
       
    resID_list = self.selection2RangeList(allResHnd,allModels=allModels)
    #print 'selectInvertRanges resID_list',resID_list
        
    self.molHnd.DeleteSelection(allResHnd)
    self.molHnd.DeleteSelection(resHnd)
    return [0,resID_list]

#--------------------------------------------------------------------------
  def selection2RangeList(self,res_selHnd,allModels=False):
#--------------------------------------------------------------------------
    selRes = newPPCResidue()
    selindexp = intp()
    #nselres = self.molHnd.GetSelIndex(res_selHnd,selRes)
    selRes = mmut.GetResidueSelIndex(self.molHnd,res_selHnd,selindexp)
    nselres = selindexp.value()
    #print 'selection2RangeList nselres',nselres
    
    resID_list = []
    ir = 0
    while ir < nselres:
      pcres = getPCResidue(selRes,ir)
      #print 'ir',ir,pcres.GetSeqNum()
      chnid = pcres.GetChainID()
      ll=1
      while (ir+ll) < nselres:
          #print "ir ll",ir,ll
          pcres2 = getPCResidue(selRes,(ir+ll))
          chnid2 = pcres2.GetChainID()
          if chnid2 != chnid:
            resID_list.append(self.resRangeCid(pcres,getPCResidue(selRes,(ir+ll-1)),allModels=allModels))
            ir = ir+ll
            ll = 999999
          elif pcres2.index == pcres.index + ll:
            ll = ll + 1
          else:
            if ll==1:
              resID_list.append(self.resRangeCid(pcres,None,allModels=allModels))
            else:
              resID_list.append(self.resRangeCid(pcres,getPCResidue(selRes,(ir+ll-1)),allModels=allModels))
            ir = ir + ll
            ll = 999999
      if ir+ll == nselres:
        resID_list.append(self.resRangeCid(pcres,getPCResidue(selRes,(nselres-1)),allModels=allModels))
        ir = 999999
      
    #print "selection2RangeList resID_list",resID_list
    return resID_list


#--------------------------------------------------------------------------
  def resRangeCid(self,pcres,pcres2=None,allModels=False):
#--------------------------------------------------------------------------
    # return the cid string for a redidue range defined by two PCResidue
    # Assumes residues are in same chain
    chnid = pcres.GetChainID()
    if not chnid: chnid = ' '
    seqnum = pcres.GetSeqNum()
    if allModels:
      resID=chnid + '/'+ str(seqnum) 
    else:
      resID='/'+str(pcres.GetModelNum())+'/' +chnid + '/'+ str(seqnum) 
    if pcres.GetInsCode(): resID = resID + '.' + pcres.GetInsCode()

    if pcres2 and pcres2!=pcres:     
      seqnum = pcres2.GetSeqNum()
      resID=resID + '-' + str(seqnum)
      if pcres2.GetInsCode(): resID = resID + '.' + pcres2.GetInsCode()
    return resID

#--------------------------------------------------------------------------
  def selectNeighb (self,cid='NONE',model='',maxd=15.0,mind=0.0,excl='', \
                    selobj_alias='', selobj_model='',hbonded=0, sym_mates=0,\
         group='residue',skey=SKEY_OR, sellist=None, nsellist=0, selhnd=None):
#--------------------------------------------------------------------------
    #print "into selectNeighb",group,excl,'sym_mates',sym_mates
    #print 'neighbours_cid',cid,excl,group,skey,selhnd,sym_mates
    sym_mates = int(sym_mates)
    
    # Generate symmetry mates
    if sym_mates:
      #print 'calling findSymmetryNeighbours',cid,maxd,self.model_symmetry
      rv = self.model_symmetry.findSymmetryNeighbours(selection=cid,radius=maxd,xtl_limit=2)
      #print 'selectNeighb findSymmetryNeighbours  sym_mates',rv
      if not rv[0]:
        nBuilt = self.model_symmetry.buildSymmetryMates(rv[1])
        #print 'selectNeighb built symmetry',nBuilt
    
    stype_groups = ['residue','chain','model']
    stypes = [STYPE_RESIDUE,STYPE_CHAIN,STYPE_MODEL]
    mind = safeFloat(mind,0.0)
    maxd = safeFloat(maxd,15.0)
    if not selhnd: return [1,'No selection handle provided']
    excl_list = []
    for item in string.split(excl,','):
      if item: excl_list.append(item[0])


    iniselHnd = -1
    iniMolData = self
    del_iniselHnd = 0
    neighb_selhnd = -1
    retval = [0]
    selindexp = intp()
    
    if not ['NONE',''].count(cid):
      if model:
        import dataobj
        rv = dataobj.get_dataobj(name=model)
        if rv: iniMolData=rv[0]
        if excl_list.count('c'): excl_list.remove('c')
      rv = iniMolData.parse_selection(command=cid)
      #print "selectNeighb cid",cid,rv
      if rv[0]: return rv
      iniselHnd  = rv[1]
      del_iniselHnd = 1
      selAtoms = newPPCAtom()
      #nselatoms = iniMolData.molHnd.GetSelIndex(iniselHnd,selAtoms)
      selAtoms = mmut.GetAtomSelIndex(iniMolData.molHnd,iniselHnd,selindexp)
      nselatoms = selindexp.value()
      #print "selectNeighb nselatoms ",nselatoms
      if nselatoms <= 0:
        iniMolData.molHnd.DeleteSelection(iniselHnd)
        return([1,'No atoms selected for centre of neighbourhood'])
    elif selobj_alias:
      #print "selobj_alias",selobj_alias,"selobj_model",selobj_model
      if selobj_model:
        import dataobj
        rv = dataobj.get_dataobj(name=selobj_model)
        if not rv:
          return [1,'Unable to find model '+selobj_model+' for neighbourhood']
        else:
          iniMolData = rv[0]
        sel = iniMolData.interpret_selobj_alias(selobj_alias,return_selHnd=1)
        if not sel: return [1,'Unable to find selection object '+selobj_alias+' in model '+selobj_model]
      else:
        sel = self.interpret_selobj_alias(selobj_alias,return_selHnd=1)
        if not sel: return [1,'Unable to find selection object '+selobj_alias]
        iniMolData = self
      #print "sel",sel
      iniselHnd = sel[1]
      if sel[0]=='user': del_iniselHnd = 1
      selAtoms = newPPCAtom()
      #nselatoms =iniMolData.molHnd.GetSelIndex(iniselHnd,selAtoms)
      selAtoms = mmut.GetAtomSelIndex(iniMolData.molHnd,iniselHnd,selindexp)
      nselatoms = selindexp.value()
    elif sellist and nsellist > 0:
      selAtoms = sellist
      nselatoms = nsellist
      iniMolData=self
    else:
      return [1,'No CID or selection alias specified']
    
    #print "selectNeighb iniselHnd iniMolData nselatoms",iniMolData,nselatoms

    if hbonded:
      
      if group == 'atom':
        
        rv = self.selectHBonded(other_selHnd=iniselHnd,other_mol=iniMolData.molHnd)
        if rv[0]:
          retval = rv
        else:
          hb_selhnd = rv[1]
          neighb_selhnd = self.molHnd.NewSelection()
          self.molHnd.Select(neighb_selhnd,STYPE_ATOM,hb_selhnd,SKEY_NEW)
          #retval = [0,neighb_selhnd]
        
      elif stype_groups.count(group):

        rv = self.selectHBonded(other_selHnd=iniselHnd,other_mol=iniMolData.molHnd)
        if rv[0]:
          retval = rv
        else:
          hb_selhnd = rv[1]
          stype = stypes[stype_groups.index(group)]
          tmpselhnd = self.molHnd.NewSelection()
          self.molHnd.Select(tmpselhnd,stype,hb_selhnd,SKEY_NEW)
          neighb_selhnd = self.molHnd.NewSelection()
          self.molHnd.Select(neighb_selhnd,STYPE_ATOM,tmpselhnd,SKEY_OR)
          self.molHnd.DeleteSelection(tmpselhnd)
          self.molHnd.DeleteSelection(hb_selhnd)
        
          #retval = [0,neighb_selhnd]

      else:
              
        res_selhnd = self.molHnd.NewSelection()
        neighb_selhnd = self.molHnd.NewSelection()

        if group == 'main_side':
          alias_list = ['main','side']
        elif group == 'solvent_monomers':
          alias_list = ['solvent','solute','monomers']
        else:
          alias_list = []
          alias_list.append(group)

        if not excl_list.count('s'): alias_list.extend(['solvent','solute'])
        if not excl_list.count('m'): alias_list.append('monomers')

        #print 'model_selection selectNeighb alias_list',alias_list

        for alias in alias_list:
          rv = self.parse_selection(command=alias)
          #print "group,alias,rv",group,alias,rv
          if rv[0]:         
            retval = [1,'Error selecting '+alias+'atoms']
          else:
            sele_selhnd=rv[1]
            #print "sele_selhnd",self.nAtoms(sele_selhnd)
            #Get hbonded atoms in main chain
            rv0 = self.selectHBonded(self_selHnd=sele_selhnd,other_selHnd=iniselHnd,other_mol=iniMolData.molHnd)
            #print "selectHBonded rv",rv0
            if rv0[0]:
              retval = [1,'Error finding hydrogen bonds']
            else:            
              tmp_selhnd=rv0[1]
              #print "tmp_selhnd",self.nAtoms(tmp_selhnd)
              #Residues with close atoms in main chain
              self.molHnd.Select(res_selhnd,STYPE_RESIDUE,tmp_selhnd,SKEY_NEW)
              #All atoms in residues with close atoms in main chain
              self.molHnd.Select(tmp_selhnd,STYPE_ATOM,res_selhnd,SKEY_NEW)
              #print "tmp_selhnd",self.nAtoms(tmp_selhnd)
              #Main chain atoms in residues with close atoms in main chain
              if alias == 'side' and not excl_list.count('a'):
                #Include CA atoms in final selection
                self.molHnd.DeleteSelection(sele_selhnd)
                rv = self.parse_selection(command='CA+side')
                sele_selhnd = rv[1]
              self.molHnd.Select(tmp_selhnd,STYPE_ATOM,sele_selhnd,SKEY_AND)
              #print "final selhnd",alias,self.nAtoms(tmp_selhnd)
              self.molHnd.Select(neighb_selhnd,STYPE_ATOM,tmp_selhnd,SKEY_OR)
              self.molHnd.DeleteSelection(sele_selhnd)              
              self.molHnd.DeleteSelection(tmp_selhnd)
        #print "total selhnd",self.nAtoms(selhnd)
        
        self.molHnd.DeleteSelection(res_selhnd)
        #if not retval[0]: retval = [0,neighb_selhnd]

    else:
      
      if group == 'atom':
        
        neighb_selhnd = self.molHnd.NewSelection()
        self.molHnd.SelectNeighbours(neighb_selhnd,STYPE_ATOM,selAtoms, \
	  nselatoms,mind,maxd,SKEY_NEW)
        #print "atom seelction nselatoms and nAtoms",nselatoms,self.nAtoms(neighb_selhnd)
        #retval = [0,selhnd]
  
      elif stype_groups.count(group):
        #print "before stype selection selhnd",selhnd,self.nAtoms(selhnd)
        # Select whole residue/chain etc
        tmpselHnd = self.molHnd.NewSelection()
        stype = stypes[stype_groups.index(group)]
        
        self.molHnd.SelectNeighbours(tmpselHnd,stype,selAtoms, \
  	    nselatoms,mind,maxd,SKEY_NEW)
        neighb_selhnd = self.molHnd.NewSelection()
        self.molHnd.Select(neighb_selhnd,STYPE_ATOM,tmpselHnd,SKEY_NEW)
        #print "stype selection selhnd",selhnd,self.nAtoms(selhnd)
        self.molHnd.DeleteSelection(tmpselHnd)
        rv = 0
        if rv != 0:
          retval =  [1,'Error propagating selection to atoms around ' + cid]
        #else:
        #  retval = [0,neighb_selhnd]

      else:

        res_selhnd = self.molHnd.NewSelection()
        tmp_selhnd = self.molHnd.NewSelection()
        close_selhnd = self.molHnd.NewSelection()
        neighb_selhnd = self.molHnd.NewSelection()

        self.molHnd.SelectNeighbours(close_selhnd,STYPE_ATOM,selAtoms, \

	                nselatoms,mind,maxd,SKEY_NEW)    
        #print "close_selhnd",nselatoms,self.nAtoms(close_selhnd)
        
        if group == 'main_side':
          alias_list = ['main','side']
        elif group == 'solvent_monomers':
          alias_list = ['solvent','solute','monomers']
        else:
          alias_list = [group]
          
        if not excl_list.count('s'): alias_list.extend(['solvent','solute'])
        if not excl_list.count('m'): alias_list.append('monomers')


        for alias in alias_list:
          rv = self.parse_selection(command=alias)
          #print "alias,rv",alias,rv
          if rv[0]:
            retval = [1,'Error parsing '+alias]
          else:
            sele_selhnd=rv[1]
            #print "sele_selhnd",self.nAtoms(sele_selhnd)
            #Get close atoms in main chain
            self.molHnd.Select(tmp_selhnd,STYPE_ATOM,close_selhnd,SKEY_NEW)
            #print "tmp_selhnd",self.nAtoms(tmp_selhnd)
            self.molHnd.Select(tmp_selhnd,STYPE_ATOM,sele_selhnd,SKEY_AND)
            #Residues with close atoms in main chain
            self.molHnd.Select(res_selhnd,STYPE_RESIDUE,tmp_selhnd,SKEY_NEW)
            #All atoms in residues with close atoms in main chain
            self.molHnd.Select(tmp_selhnd,STYPE_ATOM,res_selhnd,SKEY_NEW)
            #print "tmp_selhnd",self.nAtoms(tmp_selhnd)
            #Main chain atoms in residues with close atoms in main chain
            if alias == 'side' and not excl_list.count('a'):
              #Include CA atoms in final selection
              self.molHnd.DeleteSelection(sele_selhnd)
              rv = self.parse_selection(command='CA+side')
              sele_selhnd = rv[1]
            self.molHnd.Select(tmp_selhnd,STYPE_ATOM,sele_selhnd,SKEY_AND)
            self.molHnd.Select(neighb_selhnd,STYPE_ATOM,tmp_selhnd,SKEY_OR)
            #print "final selhnd",self.nAtoms(neighb_selhnd)
            self.molHnd.DeleteSelection(sele_selhnd)
          #print "total selhnd",self.nAtoms(selhnd)
        
        self.molHnd.DeleteSelection(res_selhnd)
        self.molHnd.DeleteSelection(close_selhnd)
        self.molHnd.DeleteSelection(tmp_selhnd)
        if retval[0]:
          retval = [1,'Error selecting atoms around ' + cid]
        #else:
        #  retval = [0,neighb_selhnd]


    # Exclude the 'central' selected atoms, main or solvent
    # No exclusion necessary if the central atoms are in a different model
    if not retval[0]:
      if excl:
        #print "doing excl",excl,excl_list
        if excl_list.count('c') and not selobj_model:
          self.molHnd.Select(neighb_selhnd,STYPE_ATOM,iniselHnd,SKEY_CLR)
        if excl_list.count('m') or excl_list.count('p'):
          if excl_list.count('m'):
            rv = self.parse_selection(command='monomers')
          else:
            rv = self.parse_selection(command='peptide_link')
          if rv[0]==0 and rv[1]>0:
              self.molHnd.Select(neighb_selhnd,STYPE_ATOM,rv[1],SKEY_CLR)
              self.molHnd.DeleteSelection(rv[1])
          else:
            retval =  [1,'Error excluding solvent']            
        if excl_list.count('s'):
          rv = self.parse_selection(command='solvent')
          if rv[0]==0 and rv[1]>0:
              self.molHnd.Select(neighb_selhnd,STYPE_ATOM,rv[1],SKEY_CLR)
              self.molHnd.DeleteSelection(rv[1])
          else:
            retval =  [1,'Error excluding solvent']
      #else:
        # Add back in the initial selection - this gets lost if
        # input selection is a residue of one atom or the method
        # argument type is 'atom'
        # self.molHnd.Select(selhnd,STYPE_ATOM,iniselHnd,SKEY_OR)

    if not retval[0]:
      self.molHnd.Select(selhnd,STYPE_ATOM,neighb_selhnd,skey)
      retval = [0,selhnd]
    
    if del_iniselHnd:iniMolData.molHnd.DeleteSelection(iniselHnd)
    if neighb_selhnd>0: self.molHnd.DeleteSelection(neighb_selhnd)
    return retval

#---------------------------------------------------------------------
  def selectHBonded(self,self_selHnd=-1,other_selHnd=-1,other_mol=''):
#---------------------------------------------------------------------
    #Find the atoms in set self_selHnd that are HBonded to
    #atoms in other_selHnd
    del_self_selHnd = 0
    
    if other_selHnd<0:
      return [1,'No selection given for H-bonding to']
    
    if self_selHnd<0:
      # Assume self is whole of this model
      rv = self.parse_selection(command='all')
      if rv[0]:
        return [1,'Error in searching for HBonds (failed selecting self)']
      self_selHnd = rv[1]
      del_self_selHnd = 1

    import mmut,HBonds

    # Intermolecular HBonds
    if other_mol != self.molHnd:
      CHBond = mmut.CHBond(self.molHnd,self_selHnd, \
               other_mol,other_selHnd)
    else:     
      # Inter MolDisp HBonds
      CHBond=mmut.CHBond(self.molHnd,self_selHnd,self.molHnd,other_selHnd)
    PM('HBond_param').update(CHBond.SetParams)  
    #print "creating CHBond",self.CHBond,self.Connectivity2 
    CHBond.Calculate(False)
    hb_selHnd1 = CHBond.hbonds.GetSelection(1)
    
    #print "hb_selHnd1",hb_selHnd1,self.nAtoms(hb_selHnd1)
    #print CHBond.hbonds.Print('test','test')
    
    if del_self_selHnd: self.molHnd.DeleteSelection(self_selHnd)
    del CHBond

    return [0,hb_selHnd1]


#--------------------------------------------------------------------
  def selectSSbonds(self,skey=SKEY_OR,selhnd=0,group='side',nmr_model='0'):
#--------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']

    rv = self.parse_selection(command="/"+str(nmr_model)+"/*/(CYS)/SG:*")
    if rv[0]: return [1,'Failed trying to select /'+nmr_model+'/*/(CYS)/SG:*']

    SGhnd = rv[1]
    selindexp = intp()
    selAtoms = newPPCAtom()
    #nselatoms = self.molHnd.GetSelIndex(SGhnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(self.molHnd,SGhnd,selindexp)
    nselatoms = selindexp.value()
    #print "selectSSbonded nselatoms",nselatoms
    if nselatoms<=0:
      # No CYS or no disulphide
      self.molHnd.Select(selhnd,STYPE_ATOM,SGhnd,skey)
      self.molHnd.DeleteSelection(SGhnd)
      return [0,selhnd]
    
    if str(nmr_model) == '0':
     allSelHnd = self.molHnd.NewSelection();
     for i in range(1,self.molHnd.GetNumberOfModels()+1):
      modelSelHnd = self.molHnd.NewSelection();
      self.molHnd.Select(modelSelHnd,STYPE_ATOM,i,"*",ANY_RES,"*",ANY_RES,"*","CYS","SG","*","*",SKEY_NEW);
      self.molHnd.Select(modelSelHnd,STYPE_ATOM,SGhnd,SKEY_AND);
      closeHnd = self.molHnd.FindCloseAtomPairs (modelSelHnd , 1.0, 2.6)

      # Get a selection handle for all CYS/SG close to another CYS/SG
      #print "closeatoms",closeHnd,self.nAtoms(closeHnd)

      # Handle the group keyword
      if group != 'atom':
        # get all the atoms in the CYS residues
        resHnd = self.molHnd.NewSelection()
        self.molHnd.Select(resHnd,STYPE_RESIDUE,closeHnd,SKEY_NEW)
        self.molHnd.Select(closeHnd,STYPE_ATOM,resHnd,SKEY_NEW)
        self.molHnd.DeleteSelection(resHnd)
        if group == 'side':
          self.molHnd.Select(closeHnd,STYPE_ATOM,'/*/*/*.*/CB,SG:*',SKEY_AND)
        elif group != 'residue':
          # Get the limiting selection (eg main or side)
          rv = self.parse_selection(command=group)
          if not rv[0]:
            self.molHnd.Select(closeHnd,STYPE_ATOM,rv[1],SKEY_AND)
            self.molHnd.DeleteSelection(rv[1])

      # Join with the input selection handle
      self.molHnd.Select(allSelHnd,STYPE_ATOM,closeHnd,SKEY_OR)
      self.molHnd.DeleteSelection(closeHnd)
     self.molHnd.Select(selhnd,STYPE_ATOM,allSelHnd,skey)
     self.molHnd.DeleteSelection(allSelHnd)
    else:
      closeHnd = self.molHnd.FindCloseAtomPairs (SGhnd, 1.0, 2.6)
      # Get a selection handle for all CYS/SG close to another CYS/SG
      #print "closeatoms",closeHnd,self.nAtoms(closeHnd)

      # Handle the group keyword
      if group != 'atom':
        # get all the atoms in the CYS residues
        resHnd = self.molHnd.NewSelection()
        self.molHnd.Select(resHnd,STYPE_RESIDUE,closeHnd,SKEY_NEW)
        self.molHnd.Select(closeHnd,STYPE_ATOM,resHnd,SKEY_NEW)
        self.molHnd.DeleteSelection(resHnd)
        if group == 'side':
          self.molHnd.Select(closeHnd,STYPE_ATOM,'/*/*/*.*/CB,SG:*',SKEY_AND)
        elif group != 'residue':
          # Get the limiting selection (eg main or side)
          rv = self.parse_selection(command=group)
          if not rv[0]:
            self.molHnd.Select(closeHnd,STYPE_ATOM,rv[1],SKEY_AND)
            self.molHnd.DeleteSelection(rv[1])

      # Join with the input selection handle
      self.molHnd.Select(selhnd,STYPE_ATOM,closeHnd,skey)
      self.molHnd.DeleteSelection(closeHnd)

    self.molHnd.DeleteSelection(SGhnd)
    return [0,selhnd]


#--------------------------------------------------------------------
  def selectBuried(self,cid='',model='',excl='',selobj_alias='', \
                   skey=SKEY_OR, selhnd=None,group='atom',self_cid='',
                   min_area=2.0,**keywords):
#------------------------------------------------------------------
    #print "in selectBuried",skey,selobj_alias,model,'cid*',cid,'*'

    # get selHnd for the 'other' atoms
    if cid:
      otherMolData = self
      if model:
        import dataobj
        rv = dataobj.get_dataobj(name=model)
        if rv: otherMolData=rv[0]
        
      rv = otherMolData.parse_selection(command=cid)
      #print "selectNeighb cid",cid,rv
      if rv[0]: return rv
      otherSelHnd  = rv[1]
    elif selobj_alias:
      import dataobj
      selobj = dataobj.get_dispobj(name=selobj_alias)
      if not selobj: return 1
      result = selobj.parent.parse_selection ( selobj.selection)
      if result[0]: return -2    
      otherSelHnd = result[1]
      otherMolData = selobj.parent
    else:
      return [1,'No selection entered']

    # get selhnd for 'self' atoms
    if self_cid:
      rv = self.parse_selection(command=self_cid)
      if rv[0]: return rv
      selfSelHnd = rv[1]
    else:
      # Select 'not' the other selection for self
      rv = self.parse_selection(command='not solvent')
      if rv[0]: return rv
      selfSelHnd = rv[1]
    self.molHnd.Select(selfSelHnd,STYPE_ATOM,otherSelHnd,SKEY_CLR)

    natoms = self.nAtoms(selfSelHnd)
    #print "selfSelHnd natoms",natoms
    if natoms<=0: return [1,'No atoms selected']

    import mmut
    udd_atom = self.molHnd.GetUDDHandle( UDR_ATOM,"atom_contact")
    if udd_atom <= 0: udd_atom = self.molHnd.RegisterUDReal(UDR_ATOM,"atom_contact")
    udd_res=self.molHnd.GetUDDHandle( UDR_ATOM,"residue_contact")
    if udd_res<=0:   udd_res = self.molHnd.RegisterUDReal(UDR_RESIDUE,"residue_contact")

    #print "contact_UDD",udd_atom,udd_res
    import mmut
    CSASArea = mmut.CSASArea(self.molHnd,selfSelHnd,otherMolData.molHnd,otherSelHnd )
    #print "CSASArea",CSASArea
    CSASArea.SetUDD(udd_atom,udd_res)
    rv = CSASArea.Calculate_Contact()
    #print "Calculate_Contact rv",rv
    #print CSASArea.Print(0)

    otherMolData.molHnd.DeleteSelection(otherSelHnd)
    self.molHnd.DeleteSelection(selfSelHnd)
    del CSASArea

    # Now apply selection based on the UDD values
    if group == 'atom':
      self.molHnd.SelectUDD (selhnd,STYPE_ATOM,udd_atom,min_area,999.9,skey)
    else:
      res_selhnd = self.molHnd.NewSelection()
      self.molHnd.SelectUDD (res_selhnd,STYPE_RESIDUE,udd_res,min_area,999.9,SKEY_NEW)
      self.molHnd.Select(selhnd,STYPE_ATOM,res_selhnd,skey)
      self.molHnd.DeleteSelection(res_selhnd)
    #print 'model_selection.selectBuried nAtoms',self.nAtoms(selhnd)
    return [0,selhnd]
    

#----------------------------------------------------------------------
  def selectOneAtom (self, selhnd=None, skey=SKEY_OR ):
#----------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']

    oneHnd = self.molHnd.NewSelection()
    resHnd = self.molHnd.NewSelection()
    selindexp = intp()

    # selhnd will contain the selected atoms - find CAs in amino acids
    # and C1 in nucleotides
    aa = MODELINFO().get_names_string(key='amino_acid', \
                                        set=self.name_label)             
    self.molHnd.Select  (oneHnd,STYPE_ATOM,0,'*', \
        ANY_RES,'*',ANY_RES,'*',aa,'CA','*','*',SKEY_NEW)
    na = MODELINFO().get_names_string(key='nucleic_acid', \
                                        set=self.name_label)
    self.molHnd.Select  (oneHnd,STYPE_ATOM,0,'*', \
        ANY_RES,'*',ANY_RES,'*',na,'C1','*','*',SKEY_OR)
    sol =  MODELINFO().get_names_string(key='solvent', \
                             set=self.name_label)

    self.molHnd.Select  (oneHnd,STYPE_ATOM,0,'*', \
        ANY_RES,'*',ANY_RES,'*',sol,'O','*','*',SKEY_OR)

    # test if we've got anything sensible
    #selAtom = newPPCAtom()
    #nAtom = self.molHnd.GetSelIndex(selhnd,selAtom)
    #print "Input selhnd", nAtom," atoms"

    # Find all unrepresented residues which do not have any atom selected
    # by first finding all the represented residues and then doing
    # exclusive-or against the set of required residues
    self.molHnd.Select(resHnd,STYPE_RESIDUE,oneHnd,SKEY_NEW)

    self.molHnd.Select (resHnd,STYPE_RESIDUE,0,'*', \
      ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_XOR)
   
    
    # Loop over all unrepresented residues and pick first atom
    # and add this to the selection
    selRes = newPPCResidue()
    #nRes =self.molHnd.GetSelIndex(resHnd,selRes)
    selRes = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
    nRes = selindexp.value()
    #print "selectOneAtom nRes",nRes
    for ir in range(0,nRes):
      p_res = getPCResidue(selRes,ir)
      p_atom = p_res.GetAtom(0)
      #print "serNum",p_atom.serNum
      self.molHnd.SelectAtoms(oneHnd,p_atom.serNum,p_atom.serNum,SKEY_OR)


    #print "oneHnd",self.nAtoms(oneHnd)
    # combine the selected atoms with all atoms in the residues of the input selection
    if skey == SKEY_AND:
      tmpHnd = self.molHnd.NewSelection()
      self.molHnd.Select(resHnd,STYPE_RESIDUE,selhnd,SKEY_NEW)
      self.molHnd.Select(tmpHnd,STYPE_ATOM,resHnd,SKEY_NEW)
      #print "tmpHnd",self.nAtoms(tmpHnd)
      self.molHnd.Select(oneHnd,STYPE_ATOM,tmpHnd,SKEY_AND)
      #print "final oneHnd",self.nAtoms(oneHnd)
      self.molHnd.DeleteSelection(selhnd)
      self.molHnd.DeleteSelection(tmpHnd)
      retval = [0,oneHnd]
    else:
      self.molHnd.Select(selhnd,STYPE_ATOM,oneHnd,skey)
      self.molHnd.DeleteSelection(oneHnd)
      retval=[0,selhnd]
      
    self.molHnd.DeleteSelection(resHnd)

    return retval

#-------------------------------------------------------------------------
  def selectTermini(self,selhnd=None, skey=SKEY_OR):
#-------------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']
    
    tmpHnd = self.molHnd.SelectChainTermini()

    #selAtom = newPPCAtom()
    #nAtom =self.molHnd.GetSelIndex(tmpHnd,selAtom)
    #print "selectTerminiinAtom",nAtom
      
    self.molHnd.Select(selhnd,STYPE_ATOM, tmpHnd,skey)
    self.molHnd.DeleteSelection(tmpHnd)
    return [0,selhnd]
  
#-------------------------------------------------------------------------
  def selectModelTermini(self,selhnd=None, skey=SKEY_OR):
#-------------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']
    
    tmpHnd = self.molHnd.NewSelection()
    for n in range(1,self.molHnd.GetNumberOfModels()+1):
      atmptr = self.molHnd.GetModel(n).GetAtom(0,0,0)
      if atmptr:
        self.molHnd.SelectAtoms(tmpHnd,atmptr.serNum,atmptr.serNum,SKEY_OR)
      
    self.molHnd.Select(selhnd,STYPE_ATOM, tmpHnd,skey)
    self.molHnd.DeleteSelection(tmpHnd)
    return [0,selhnd]

#-------------------------------------------------------------------------
  def selectSSETermini(self,selhnd=None, skey=SKEY_OR):
#-------------------------------------------------------------------------
    if not selhnd: return [1,'No selection handle provided']

    if selhnd:
      tmpHnd = self.molHnd.SelectSSETermini(selhnd)
    else:
      tmpHnd = self.molHnd.SelectSSETermini(-1)
      
    selAtom = newPPCAtom()
    selindexp = intp()
    #nAtom =self.molHnd.GetSelIndex(tmpHnd,selAtom)
    selAtom = mmut.GetAtomSelIndex(self.molHnd,tmpHnd,selindexp)
    nAtom = selindexp.value()
      
    self.molHnd.Select(selhnd,STYPE_ATOM, tmpHnd,skey)
    self.molHnd.DeleteSelection(tmpHnd)
    #delPPCAtom(selAtom)
    return [0,selhnd]


#----------------------------------------------------------------------
  def selectRanges(self,cid='',selhnd=None,skey=SKEY_OR,extend=0,qualifier='',
                  neighb_cid='',neighb_cutoff=4.0,neighb_qualifier='',neighb_invert=0,
                  **keywords ):
#----------------------------------------------------------------------
     # If extend is true is method will add preceding CA,C and trailing N,CA
     # to a range of residues
     #print 'selectRanges cid',cid
     
     if not selhnd: return [1,'No selection handle provided']
     extend = int(extend)
     #print 'selectRange cid',cid,'extend',extend

     if (not cid) and  neighb_cid:
       neighb_list = self.selectNeighbRanges(cid=neighb_cid,maxd=neighb_cutoff, qualifier=neighb_qualifier,invert=neighb_invert)
       
     import types
     if isinstance(cid,types.ListType):
       cid_list = cid
     else:
       cid_list = cid.split(',')
       
     if neighb_cid:
       rv = self.selectNeighbRanges(cid=neighb_cid,maxd=neighb_rad, qualifier=neighb_qualifier,invert=neighb_invert)
       if rv[0]:
         return rv
       else:     
         cid_list.extend(neighb_list)

     # Get the selected residues
     tmpResHnd = self.molHnd.NewSelection()
     tmpHnd =  self.molHnd.NewSelection()
     selRes = newPPCResidue()

     selindexp = intp()
 
     for cid in cid_list:
       self.molHnd.Select(tmpResHnd,STYPE_RESIDUE,cid,SKEY_NEW)
     
       #nselres =self.molHnd.GetSelIndex(tmpResHnd,selRes)
       selRes = mmut.GetResidueSelIndex(self.molHnd,tmpResHnd,selindexp)
       nselres = selindexp.value()
       if nselres > 0:

         # Add to the atom selection
         self.molHnd.Select(tmpHnd,STYPE_ATOM,tmpResHnd,SKEY_OR)

         if extend:
           # Is there another res in same chain before the first residue
           # in the range - if so add the CA,C to the selection
           pRes = getPCResidue(selRes,0)
           if pRes.index !=0:
             prevRes = pRes.chain.GetResidue(pRes.index-1)
             self.molHnd.SelectAtoms ( tmpHnd, prevRes.GetModelNum(),prevRes.GetChainID(),
               prevRes.GetSeqNum(),'*',prevRes.GetSeqNum(),'*','*','CA,C','C','*',SKEY_OR)
             #print 'selectRange prevRes',prevRes.GetSeqNum()
       
     
           # Is there another res in same chain after the last residue
           # in the range - if so add the N,CA to the selection
           pRes = getPCResidue(selRes,nselres-1)
           if (pRes.index + 1) < pRes.chain.GetNumberOfResidues():
             nextRes = pRes.chain.GetResidue(pRes.index+1)
             self.molHnd.SelectAtoms ( tmpHnd, nextRes.GetModelNum(),nextRes.GetChainID(),
               nextRes.GetSeqNum(),'*',nextRes.GetSeqNum(),'*','*','CA,N','*','*',SKEY_OR)
         #print 'selectRange nextRes',nextRes.GetSeqNum()

     if qualifier:
       rv = self.parse_selection(command=qualifier)
       if not rv[0]:
         self.molHnd.Select(tmpHnd,STYPE_ATOM,rv[1],SKEY_AND)
         self.molHnd.DeleteSelection(rv[1])
     self.molHnd.Select(selhnd,STYPE_ATOM, tmpHnd,skey)
     self.molHnd.DeleteSelection(tmpHnd)
     self.molHnd.DeleteSelection(tmpResHnd)
     return [0,selhnd]


#---------------------------------------------------------------------
  def selectEquivalentAtoms(self,other_DispObj='',other_molHnd=None,other_selHnd=-1,
                            selhnd=None,skey=SKEY_OR):
#---------------------------------------------------------------------
    '''
    other_molHnd and other_selHnd or MMDBManager and selection handle for (probably)
    a different model. Find the atoms in this model which have the same
    names as those in the other model. This expects both structures to have the same sequence.
    '''

    #print 'selectEquivalentAtoms other_DispObj',other_DispObj
    if other_DispObj:
      #try:
        import types
        if isinstance(other_DispObj,types.StringType):
            other_DispObj0 = get_dispobj(name=other_DispObj)
            if not other_DispObj0:
              return [1,'Can not interpret other selection '+str(other_DispObj)]
            other_DispObj = other_DispObj0
        other_molHnd = other_DispObj.parent.molHnd
        if hasattr(other_DispObj,'SelHandle'):
          other_selHnd = other_DispObj.SelHandle.getSelHnd()
        else:
          other_selHnd = other_DispObj.SelHandle1.getSelHnd()
      #except:
      #  return [1,'Can not interpret other selection '+str(other_DispObj)]

    #print 'selectEquivalentAtoms',other_molHnd,other_selHnd
      
    selindexp = intp()
    other_selAtoms = newPPCAtom()
    #other_nAtoms =other_molHnd.GetSelIndex(other_selHnd,other_selAtoms)
    selAtoms = mmut.GetAtomSelIndex(other_molHnd,other_selHnd,selindexp)
    other_nAtoms = selindexp.value()

    equiv_selHnd = self.molHnd.NewSelection()

    for ii in range(other_nAtoms):
      pcat = getPCAtom(other_selAtoms,ii)
      self.molHnd.SelectAtoms ( equiv_selHnd, pcat.GetModelNum(),pcat.GetChainID(),
        pcat.GetSeqNum(),pcat.GetInsCode(),pcat.GetSeqNum(),pcat.GetInsCode(),
                          pcat.GetResName(),pcat.GetAtomName(),'*',pcat.GetAltLoc(),SKEY_OR)

    equiv_selAtoms = newPPCAtom()
    #equiv_nAtoms = self.molHnd.GetSelIndex(equiv_selHnd,equiv_selAtoms)
    equiv_selAtoms = mmut.GetAtomSelIndex(self.molHnd,equiv_selHnd,selindexp)
    equiv_nAtoms = selindexp.value()

    #print 'selectEquivalentAtoms other:',other_nAtoms,'equivalent:',equiv_nAtoms

    self.molHnd.Select(selhnd,STYPE_ATOM,equiv_selHnd,skey)

    return [0,selhnd]

#---------------------------------------------------------------------
  def selectSymid(self,symid='1_555',selhnd=None,skey=SKEY_OR,**kw):
#---------------------------------------------------------------------
    rv = self.model_symmetry.symmetryMateModelNo(symid=symid,build=0)
    #print 'selectSymid',symid,rv
    if rv[0]:
      modelNo = 9999
    else:
      modelNo = rv[1]
    symHnd = self.molHnd.NewSelection()
    self.molHnd.Select  (symHnd,STYPE_ATOM,modelNo,'*', \
            ANY_RES,'*',ANY_RES,'*','*','*','*','*',SKEY_NEW)
    self.molHnd.Select(selhnd,STYPE_ATOM,symHnd,skey)
    self.molHnd.DeleteSelection(symHnd)
    return [0,selhnd]

    
#---------------------------------------------------------------------
  def find_cid_in_line(self,line=''):
#---------------------------------------------------------------------
    words = string.split(line)
    for word in words:
      if word:
        # strip enclosing quotes - as might be used in CIF file
        # such as vectors
        word = re.sub('^"','',word)
        word = re.sub('"$','',word)
        rv = self.parse_selection(command=word,test=1)
        if rv[0] == 0 and rv[1]>0:
          return word
    return ""

#----------------------------------------------------------------------
  def pdbATOM2cid(self,line=''):
#----------------------------------------------------------------------
    # 13 - 16        Atom            name          Atom name
    # 17             Character       altLoc        Alternate location indicator
    # 18 - 20        Residue name    resName       Residue name
    # 22             Character       chainID       Chain identifier
    # 23 - 26        Integer         resSeq        Residue sequence number
    # 27             AChar           iCode         Code for insertion of residues.
    # Assume atom is in model 1 and construct a cid from PDB ATOM/HETATM card

    if line[21] == ' ':
      cid = ""
    else:
      cid = line[21]+'/'

    cid = cid + string.strip(line[22:26])
    if line[26] != ' ': cid = cid + '.'+ line[26]
    # ? should H names be changed around?
    cid = cid + '/' + string.strip(line[12:16])
    if line[16] != ' ': cid = cid + ':'+line[16]
    #print "pdbATOM2cid",cid
    return cid


#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
class Cselect_tree:
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------

#-------------------------------------------------------------------------
  def __init__(self,name,parent=None,mol=None,dispobj=''):
#-------------------------------------------------------------------------
    self.name = name
    self.parent = parent
    self.mol = mol
    self.dispobj = dispobj
    self.element = None
    self.op = None
    self.children = []
    self.selHnd = None
    self.com_string = ""
  

#-------------------------------------------------------------------------
  def set(self,defn):
#-------------------------------------------------------------------------
    #print "set defn",defn
    if len(defn) < 1:
      return 1
    self.op = defn[0]
    if not self.op :
      self.element = defn[1]
    elif self.op == 'not':
      cl = Cselect_tree(self.name + '_l',self.name,self.mol,dispobj=self.dispobj)
      cl.set(defn[1])
      self.children.append(cl)      
    elif len(defn)>2:
      cl = Cselect_tree(self.name + '_l',self.name,self.mol,dispobj=self.dispobj)
      cl.set(defn[1])
      self.children.append(cl)
      cr = Cselect_tree(self.name + '_r',self.name,self.mol,dispobj=self.dispobj)
      cr.set( defn[2])
      self.children.append(cr)
    else:
      return 1
    #print "RETURN 0"
    return 0
#-------------------------------------------------------------------------
  def import_command_string(self,elements,ops,levels):
#-------------------------------------------------------------------------

    #print 'elements',elements,'ops',ops,'levels',levels

# check if down to one component
    if len(elements) == 1:
      self.element = elements[0]
#### USEFUL DIAGNOSTIC ON TREE STRUCTURE
#      print self.name, self.element
      return 0
# if enclosed in braces then strip them off
    while levels.count(0) == 0:
      l = len(levels)
      levs = []
      for lev in levels[1:l-1]: levs.append(lev -1)
      levels = levs
      ops = ops[1:l-1]
      l = len(elements)
      elements = elements[1:l-1]
# check again if we are down to 1 component
      if len(ops) <= 0:
        self.element = elements[0]
#        print self.name, self.element
        return 0
#      print 'stripped levels',levels,'elements',elements,'ops',ops

# find the *last* unnested logical operator
    iop = len(levels)-1
    while (levels[iop] != 0 or ops[iop] == 'not') and iop > 0: 
      iop = iop -1
    self.op = ops[iop]
#    print self.name, self.op
    if self.op != 'not':
      cl = Cselect_tree(self.name + '_l',self.name,self.mol,dispobj=self.dispobj)
      cl.import_command_string(elements[0:(iop+1)],ops[0:iop],levels[0:iop])
      self.children.append(cl)
    cr = Cselect_tree(self.name + '_r',self.name,self.mol,dispobj=self.dispobj)
    cr.import_command_string( elements[iop+1:], ops[iop+1:], levels[iop+1:])
    self.children.append(cr)
    return 0
  
#-------------------------------------------------------------------------
  def cleanup(self,save_selHnd=0):
#-------------------------------------------------------------------------
# Close the selHnd for all but the top object
    if self.selHnd and not save_selHnd: self.delete_selhnd (self.selHnd)
    for child in self.children:
      if not save_selHnd or child.selHnd != self.selHnd:
        child.cleanup()

#-------------------------------------------------------------------------
  def __del__(self):
#-------------------------------------------------------------------------
    for child in self.children:
      del child

#-------------------------------------------------------------------------
  def skey(self):
#-------------------------------------------------------------------------
    if self.op == 'and':
      return SKEY_AND
    elif self.op == 'or':
      return SKEY_OR
    elif self.op == 'xor':
      return SKEY_XOR
    elif self.op == 'excl':
      return SKEY_CLR
    

#-------------------------------------------------------------------------
  def apply(self):
#-------------------------------------------------------------------------

# propagate the 'apply' down through all nodes in the tree

    for child in self.children:
      if not child.selHnd and child.op:
        child.apply()

# For an 'ops' object apply the selection criteria of its 
# children.  Create a 'selHnd' for this object
#    print "in apply", self.name

    if len(self.children) == 0:
# There is one simple selection command
      self.selHnd = self.new_selHnd()
      rv = self.interpret(SKEY_NEW,self.element)
      return rv
 
    elif self.op == 'not':
      #print "in apply NOT"
      if not self.children[0].selHnd:
        self.selHnd = self.new_selHnd()
        rv = self.interpret(SKEY_NEW,self.children[0].element)
        if rv[0] > 0: return rv
      else:
        self.selHnd = self.children[0].selHnd
      rv = self.interpret(SKEY_XOR,'all')
      return rv

    if not self.children[0].selHnd and \
       not self.children[1].selHnd:
# Neither child selection is currently evaluated
      self.selHnd = self.new_selHnd()
      rv = self.interpret(SKEY_NEW,self.children[0].element)
      if rv[0] > 0: return rv
      rv = self.interpret(self.skey(),self.children[1].element)
      if rv[0] > 0: return rv

    elif self.children[0].selHnd and self.children[1].selHnd:
# both child selections are evaluated
      rv = self.merge_selection(self.skey(), \
        self.children[0].selHnd,self.children[1].selHnd)
      self.delete_selhnd(self.children[1].selHnd)
      if rv[0] > 0: return rv
      self.selHnd = self.children[0].selHnd

    elif self.children[0].selHnd:
# the left side selection is already evaluated
      self.selHnd = self.children[0].selHnd
      rv = self.interpret(self.skey(),self.children[1].element)
      if rv[0] > 0: return rv
    else:
# the right side selection is already evaluated
      if self.op != 'excl':
        self.selHnd = self.children[1].selHnd
        rv = self.interpret(self.skey(),self.children[0].element)
        if rv[0] > 0: return rv
      else:
# excl operation not cummutative
        self.selHnd = self.new_selHnd()
        rv = self.interpret(SKEY_NEW,self.children[0].element)
        if rv[0] > 0: return rv
        self.merge_selection(self.skey(),self.selHnd,self.children[1].selHnd)
        self.delete_selhnd(self.children[1].selHnd)
        if rv[0] > 0: return rv
    return [0,self.selHnd]


#-------------------------------------------------------------------------
  def new_selHnd(self):
#-------------------------------------------------------------------------
    selHnd = self.mol.molHnd.NewSelection()
    return selHnd

#-------------------------------------------------------------------------
  def interpret(self,skey,element):
#-------------------------------------------------------------------------
#   
    if not element:  return [1,'No selection']
    if string.strip(element) == '': return [1,'No selection']

    #print "interpret element",element
    words = splitWords0(element)
    #print "interpret words",words
    if len(words) <= 0:  return [1,'No selection']
    # Test if first word is an alias and
    # parse the expansion for the alias
    expansion = self.mol.expand_selection_alias(words[0])
    #print "expansion",words[0],expansion
    if expansion:
      if skey == SKEY_NEW: self.delete_selhnd(self.selHnd)
      rv = self.mol.parse_selection(expansion)
      if rv[0]:
        return [1,'Error interpreting expansion of '+words[0]]
      elif skey == SKEY_NEW:
        self.selHnd = rv[1]
      else:
        self.merge_selection(skey,self.selHnd,rv[1])
        self.delete_selhnd(rv[1])
      return (0,self.selHnd)
        
    kw = matchKeyword(string.lower(words[0]),self.mol.selection_commands)
    #print "word,kw",words[0],kw
    if kw[0] >= 1: 
      cmd = 'rv  = self.mol.' + self.mol.selection_methods[kw[1][0]] + '('
      for w in words[1:]:
        ww = re.split('=', w)
        if len(ww) > 1:
          if ( re.match("\'", ww[1]) and re.search("'$",ww[1]) ) or \
            ( re.match('\"', ww[1]) and re.search('"$',ww[1]) ):
            cmd = cmd + ww[0] + "=" + ww[1] + ","
          else:
            cmd = cmd + ww[0] + "='" + ww[1] + "',"
        else:
          cmd = cmd + "'" + w + "',"
      cmd = cmd + 'selhnd=' +  str(self.selHnd) + ',' + 'skey=' + str(skey)  + ')'
      #print "interpret cmd",cmd,self.nSelAtoms()
      if DEVELOPER():
        exec cmd
        if rv[0] == 0:
          self.selHnd = rv[1]
          return rv
        else:
          return [rv[0],'Error interpreting command ' + self.mol.selection_commands[kw[1][0]] + ': ' + rv[1]]
      else:
        try:
          exec cmd
          if rv[0] == 0:
            self.selHnd = rv[1]
            return rv
          else:
            return [rv[0],'Error interpreting command ' + self.mol.selection_commands[kw[1][0]] + ': ' + rv[1]]
        except:
           #import sys
           #print 'ModelAnalysis.interpret',sys.exc_info()
           return [1,'Error interpreting command ' + self.mol.selection_commands[kw[1][0]]]

    # Does the element contain operators - if so attempt to
    # interpret
    elif re.search(r'[=><]', element):
      #print "matched operator"
      sel_ops = []
      ele = element
      while ele:
        t = re.search(r'([^=><]*)(==|!=|<=|>=|<|>)([^=><]*)(.*)',ele)
        #if not t:
        #  t = re.search(r'([^=><]*)(=|>|<)([^=><]*)(.*)',ele)
        if not t:
          # Have an operator symbol but it dont make sense
          return[1,'Can not interpret '+element]
        
        tt = t.groups()
        #print "tt",tt
        # tt[1] should be operator and tt[0] and tt[2] are parameter
        # and limit value (either way round)
        tt0 = string.strip(tt[0])
        tt2 = string.strip(tt[2])
        param = self.interpret_operator_param(tt0)
        if param:
          op = ['==','!=','<=','<','>=','>','='].index(tt[1])
          if op == 6: op = 0
          value = tt2
        else:
          param = self.interpret_operator_param(tt2)
          if param:
            op = ['==','!=','>=','>','<=','<','='].index(tt[1])
            if op == 6: op = 0
            value = tt0
          else:
            return[1,'No parameter recognised in '+element]
        try:
          if param[1]=='float':
            value=float(value)
          elif param[1]=='int':
            # Weird this - something like int('6.0') breaks
            value = int(float(value))
          else:
            pass
        except:
           return[1,'Inappropriate parameter value '+value]
        sel_ops.append([param[0],param[1],op,value])
        #print "sel_ops",sel_ops
        if tt[3]:
          ele = param[0]+tt[3]
        else:
          ele = ''

      # Success! put return fail for now
      rv_op = self.apply_operators(sel_ops)
      #print "from apply_operators rv",rv_op
      if rv_op[0]==0:
        # merge the selection from the operator and
        # delete the selHnd
        rv = self.merge_selection(skey,self.selHnd,rv_op[1])
        #print "merge_selection rv",rv,self.selHnd,self.nSelAtoms(rv[1])
        self.delete_selhnd(rv_op[1])
        if (rv[0] == 0 ):
          return [0,self.selHnd]
        else:
          return [1,'Error merging '+element]
      else:        
        return rv_op
        
    else:
      #element0 = self.interpretSymop(element)
      rv = self.mol.molHnd.Select(self.selHnd,STYPE_ATOM,element,skey)
      #print 'interpret selection',element,self.nSelAtoms()
      if rv == 0:
        return [rv,self.selHnd]
      else:
        return [rv,'Error applying selection CID ' + element]


#------------------------------------------------------------------------
  def interpretSymop(self,element):
#------------------------------------------------------------------------
    m = re.match(r"(.*)(\d+)_(\d+)(.*)",element)
    if not m: return element
    gps = m.groups()
    #print 'interpretSymop',element,gps
    op = int(gps[1])
    tran = int(gps[2])
    
    if not ['','/'].count(gps[0]) or \
       op<1 or op>self.mol.model_symmetry.nofCrystalSymops() or \
       tran<111 or tran>999 :
      return element
    
    imodel = self.mol.model_symmetry.getPendingSymmetryMate(gps[1]+'_'+gps[2])
    if imodel<0:
      return element
    else:
      return '/'+str(imodel)+gps[3]
    
    
#------------------------------------------------------------------------
  def interpret_operator_param( self, tt):
#------------------------------------------------------------------------
    #print "interpret_operator_param",tt
    tt = string.upper(tt)
    for par in ['B','OCC','CHARGE','X','Y','Z','ATOM_SAS','RES_SAS', \
                'ATOM_BURIED','RES_BURIED']:
      if re.match(par,tt): return [par,'float']    
    for par in ['SERIAL']:
      if re.match(par,tt): return [par,'int']
    for par in ['SEC']:
      if re.match(par,tt): return [par,'string']
    return []

#------------------------------------------------------------------------
  def apply_operators( self, sel_ops ):
#------------------------------------------------------------------------
    import model
    import re
    import string
    import utils
    selHnd=self.mol.molHnd.NewSelection()
    skey = SKEY_NEW
    for op in sel_ops:
      #print "apply_operators op",op
      
      if not SelHandle.property_alias.count(op[0]): break
      # Make sure SAS calculated
      
      if ['ATOM_SAS','RES_SAS'].count(op[0]):
        self.mol.update_sasarea(reapply=0)
      if re.search('BURIED',op[0]):
        #n = string.split(op[0],'-')[1]
        #print "contact n",n
        if re.match('ATOM',op[0]):
          udd =  self.mol.molHnd.GetUDDHandle( UDR_ATOM,'atom_contact'+self.dispobj)
        else:
          udd =  self.mol.molHnd.GetUDDHandle( UDR_RESIDUE,'residue_contact'+self.dispobj)
        #print "apply_operators contact UDD",udd
      else:
        if op[0] == 'SERIAL':
          udd = self.mol.molHnd.GetUDDHandle( UDR_ATOM,"atomSerial")
          if udd<=0:
            serial_molHnd = Manager()
            serial_molHnd.SetFlag(MMDBF_IgnoreRemarks)
            serial_molHnd.SetFlag(MMDBF_IgnoreBlankLines)
            serial_molHnd.SetFlag(MMDBF_IgnoreHash)
            #serial_molHnd.SetFlag(MMDBF_EnforceUniqueChainID)
            #serial_molHnd.ReadCoorFile(self.mol.filename[2])
            utils.ReadCoorFile(serial_molHnd,self.mol.filename[2])
            udd = self.mol.molHnd.LoadSerial(serial_molHnd)
            del serial_molHnd
        else:
          #Get the enum code for the type of data
          prop_enum = SelHandle.property_enum[ \
             SelHandle.property_alias.index(op[0])]
          #print "in apply_operators prop_enum",prop_enum
          udd = self.mol.molHnd.LoadUDDData(prop_enum )
          #print "udd",udd
      if udd < 0:
        self.mol.molHnd.DeleteSelection(selHnd)
        return [1,"Error loading data "+op[0]]
      
      if op[0] == 'SEC':
        #print "op[3]",op[3]
        # Beware pre 1.111 used single letter code secstr_code
        # but now use the secstr_alias
        sec_code = model.MolData.secstr_code
        sec_alias = model.MolData.secstr_alias
        ss = -1
        if sec_code.count(op[3]):
          ss = sec_code.index(op[3])
        elif sec_alias.count(op[3]):
          ss = sec_alias.index(op[3])
        if ss>=0:
          min = int(ss)
          max = int(ss)
        else:
          min=0
          max=6
      elif op[1] == 'int':
        if op[2] == 0:
          max = int(op[3])
          min =  int(op[3])
        elif op[2] == 1:
          max = int(op[3])-1
          min = -99999999
        elif op[2] == 2:
          min = -99999999
          max =  int(op[3])
        elif op[2] == 3:
          min = -99999999
          max =  int(op[3])-1
        elif op[2] == 4:
          max = 99999999
          min =  int(op[3])
        elif op[2] == 5:
          max = 99999999
          min =  int(op[3])+1
      elif op[1] == 'float':
        if op[2] == 0:
          if op[3]>0.0:
            max = float(op[3])*1.0001
            min =  float(op[3])*0.9999
          else:
            max = float(op[3])*0.9999
            min =  float(op[3])*1.0001
        elif op[2] == 1:
          if op[3]>0.0:
            max = float(op[3])*0.9999
          else:
            max = float(op[3])*1.0001
          min = -9999999.9
        elif op[2] == 2:
          min = -9999999999.9
          max =  float(op[3])
        elif op[2] == 3:
          min = -9999999999.9
          max =  float(op[3])
        elif op[2] == 4:
          max = 9999999999.9
          min =  float(op[3])
        elif op[2] == 5:
          max = 9999999999.9
          min =  float(op[3])
          
      #print "min,max",min,max,op[0],udd
      if re.match('RES',op[0]) or op[0]=='SEC':
        resSelHnd=self.mol.molHnd.NewSelection()
        self.mol.molHnd.SelectUDD (resSelHnd,STYPE_RESIDUE,udd, \
            min,max,SKEY_NEW)
        self.mol.molHnd.Select(selHnd,STYPE_ATOM,resSelHnd,skey)
        self.mol.molHnd.DeleteSelection(resSelHnd)
        resSelHnd = -1
      else:
        self.mol.molHnd.SelectUDD (selHnd,STYPE_ATOM,udd, \
            min,max,skey)
      skey = SKEY_AND
      if op[2] == 1:
      # Need a second selection to get the atoms above the value
        if op[1] == 'int':
          min = int(op[3])+1
          max = 99999999
        elif op[1] == 'float':
          if op[3]>0:
            min = float(op[3])*1.0001
          else:
            min = float(op[3])*0.9999
          max = 9999999999.9
        self.mol.molHnd.SelectUDD (selHnd,STYPE_ATOM,udd, \
            min,max,SKEY_OR)
      #print "apply_operator nSelAtoms",self.nSelAtoms(selHnd)
    return [0,selHnd]
  
#-------------------------------------------------------------------------
  def merge_selection(self,skey,selHnd1,selHnd2):
#-------------------------------------------------------------------------
    #print 'CSelectTree.merge_selection',skey,selHnd1,selHnd2
    # Beware Select_propagate does not return a status flag
    #print "SelHandle.merge_selection skey",skey
    self.mol.molHnd.Select(selHnd1,STYPE_ATOM,selHnd2,skey)
    return [0,selHnd1]

#-------------------------------------------------------------------------
  def delete_selhnd(self,selHnd):
#-------------------------------------------------------------------------
    if selHnd >= 0: self.mol.molHnd.DeleteSelection(selHnd)
    selHnd = -1

#-------------------------------------------------------------------------
  def nSelAtoms(self,selhnd=None):
#-------------------------------------------------------------------------
    if not selhnd: selhnd = self.selHnd
    #if self.selAtoms: delPPCAtom(self.selAtoms)
    selindexp = intp()
    self.selAtoms = newPPCAtom()
    #nselatoms =self.mol.molHnd.GetSelIndex(selhnd,self.selAtoms)
    self.selAtoms = mmut.GetAtomSelIndex(self.mol.molHnd,selhnd,selindexp)
    nselatoms = selindexp.value()
#    print "Number of selected atoms",nselatoms
    #delPPCAtom(self.selAtoms)
    return nselatoms

#-------------------------------------------------------------------------
  def export_command_string(self):
#-------------------------------------------------------------------------
    for child in self.children:
      if not child.com_string:
        child.export_command_string()

    if len(self.children) == 0:
      self.com_string = self.element

    elif self.op == 'not':
      #print "export not",self.children[0].element
      if len(self.children[0].children) > 1:
        self.com_string = 'not {'+self.children[0].com_string+'}'
      else:
        self.com_string = 'not '+ self.children[0].com_string

    else:
      if not self.children[0].op or self.children[0].op == self.op:
        self.com_string =  self.children[0].com_string + ' ' + self.op + ' '
      else:
        #print self.children, self.op
        self.com_string = ' {'+self.children[0].com_string+'} '+self.op+' '

      if not self.children[1].op or self.children[1].op == self.op:
        #print "export_command_string",self.com_string,'*',self.children[1].com_string
        self.com_string = self.com_string+self.children[1].com_string
      else:
        self.com_string = self.com_string+'{'+self.children[1].com_string+'}'

    #print "com_string",self.com_string
    return [0,self.com_string]

#------------------------------------------------------------------------
  def search(self,keyword):
#------------------------------------------------------------------------
    hits = self.search0(keyword)
    # search0 returns a list of Cselect_tree objects
    # convert this to a list of selection definitions
    hitdefns = []
    for hit in hits:
      hitdefns.append(hit[1].get())

    #print "search",keyword,hitdefns
    return hitdefns


#-------------------------------------------------------------------------
  def search0(self,keyword):
#-------------------------------------------------------------------------
    # At a leaf - does the com_string match the keyword
    #print "search0",self.element,self.op
    if len(self.children) == 0:
      if self.element == keyword:
        return [[1,self]]
      else:
        return []

    # Loop over all children
    hits = []
    for child in self.children:
      hits.extend(child.search0(keyword))

    newhits=[]
    if len(hits)>0:
      if self.op == 'and':
        for hit in hits:
          if hit[0] == 2:
            newhits.append(hit)
          elif hit[0] == 1:
            newhits.append([1,self])
      else:
        for hit in hits: newhits.append([2,hit[1]])

    #print "search0",self.element,newhits
    return newhits

#----------------------------------------------------------------------
  def get(self):
#----------------------------------------------------------------------
    if not self.op:
      return [None,self.element]
    elif self.op == 'not':
      return ['not',self.children[0].get()]
    else:
      return [self.op,self.children[0].get(),self.children[1].get()]


#-------------------------------------------------------------------------
# Functions
#-------------------------------------------------------------------------
#-------------------------------------------------------------------
def list_to_command(list):
#-------------------------------------------------------------------
  t = Cselect_tree('TOP','')
  t.set(list)
  rv = t.export_command_string()
  t.cleanup()
  return rv

#-------------------------------------------------------------------
def command_to_list( command ):
#-------------------------------------------------------------------
  '''
  Convert an input command string to a python list selection
  '''
  parser = SelectionParser()
  toks = parser.tokenise(command)
  if toks[0] > 0:
    return [1,'Error parsing command']
  else:
    #toks=self.expand(toks[1],toks[2],toks[3])
    t = Cselect_tree('TOP','')
    rv = t.import_command_string(toks[1],toks[2],toks[3])
    l = t.get()
    t.cleanup()
    return [0,l]


#-------------------------------------------------------------------
#-------------------------------------------------------------------
class SelHandle(SelectionParser,dataobj.Dependency):
#-------------------------------------------------------------------
#-------------------------------------------------------------------

  selection_initialisation = { 'select' : 'all',
                'nothydrogen':0,
                'restypes':[],
                'elementtypes':[],
                'nmr_models':[],
                'symid': '',       
                'neighb_sel':'',
                'neighb_mol':'',
                'neighb_rad':'4.0',
                'neighb_group': 'residue',
                'neighb_hb': 0,
                'neighb_excl':1,
                'neighb_excl_solvent':0,
                'neighb_excl_monomer':0,
                'neighb_sym' : 0,
                'neighb_selobj' : '',            
                'buried_sel' : '',
                'buried_group' : 'atom',
                'buried_mol' : '',
                'buried_and_selection':'',
                'features' : '',
                'contact_sel':'',
                'contact_mol':'',            
                'property_join':'and',
                'property_gt_op':[],
                'property_gt_value':[],
                'property':[],
                'property_lt_op':[],
                'property_lt_value':[],
                'secstr':[],
                'ranges' : [],
                'ranges_extend' : 1,
                'ranges_qualifier' : '',
                'ranges_invert': 0,
                'ranges_neighb_cutoff' : 4.0,
                'ranges_neighb_model' : '',
                'ranges_neighb_selection' : '',
                'ranges_neighb_invert' : 0,
                'ranges_neighb_qualifier' : 'all',   
                'user_sel':'',
                'alt_locs':[],
                'as_MolDisp':'',
                'selection_scheme':'',
                'atomtype' : '',
                'chains' : [],
                'monomers' : [],
                'sites' : [],
                'use_all_models' : False,
                'cid':'' }

  property_alias = ['B','OCC','X','Y','Z','ATOM_SAS','RES_SAS', \
                    'ATOM_BURIED','RES_BURIED','SEC', 'SERIAL' ]
  property_short = ['Temp','Occ','X','Y','Z','SAS','SAS', \
                    'Buried','Buried','Secstr', 'Serial' ]
  
  property_enum =  [mmut.PROPERTY_B,mmut.PROPERTY_OCC, \
                    mmut.PROPERTY_X,mmut.PROPERTY_Y,mmut.PROPERTY_Z,
                    mmut.PROPERTY_ATOM_SAS,mmut.PROPERTY_RES_SAS,
                    mmut.PROPERTY_ATOM_CONTACT, mmut.PROPERTY_RES_CONTACT,
                    mmut.PROPERTY_SEC,mmut.PROPERTY_SERIAL]


  features_menu = ['Disulphide bridges','Solute','Metals','Lipids','Nucleic backbone','Nucleic sugar ring','Nucleic base']
  features_alias = ['ssbond','solute','metal','lipids','backbone','sugar_ring','base']


  
  maxLabelLength = 100

#-------------------------------------------------------------------
  def __init__(self,moldata_name,selHnd=-1,command='',
                 selparams={},dispobj_name='',history=0):
#-------------------------------------------------------------------
    self.moldata_name = moldata_name
    self.maintainHistory = history
    self.selHnd = -1
    self.dispobj_name = dispobj_name
    self.same_as = 0
    self.command = []
    self.label = ''
    dataobj.Dependency.__init__(self)
    # !! Beware assume we are adopting the selHnd rather than copying it
    if selHnd>0: self.setSelHnd(selHnd,resetCommand=0)
    self.selparams = { }
    self.selparams.update(SelHandle.selection_initialisation)
    #print "Selhandle.init command,selparams",command,selparams
    self.setSaveParams(selparams,dispobj_name=dispobj_name)
    #print 'Selhandle.init after setSaveParams',self.label
    if command:  self.setCommand(command)
    #print 'Selhandle.init after setCommand',self.label

#-------------------------------------------------------------------
  def __del__(self):
#-------------------------------------------------------------------
    #print "in SelHandle __del__"
    if self.selHnd>=0:
      mol = data(self.moldata_name)
      if mol: mol.molHnd.DeleteSelection(self.selHnd)
    dataobj.Dependency.delete(self)

#-------------------------------------------------------------------
  def setSelHnd(self,selHnd,resetCommand=0,copy=1):
#-------------------------------------------------------------------
    #print "SelHandle.setSelHnd",self,selHnd,"current selHnd",self.selHnd
    moldata =  data(self.moldata_name)
    if selHnd<0:
      # Just delete existing selHnd
      if self.selHnd>=0: moldata.molHnd.DeleteSelection( self.selHnd)
      self.selHnd = selHnd
    elif copy:
      # If necessary create a new selHnd and copy selection from loaded
      if self.selHnd<0: self.selHnd = moldata.molHnd.NewSelection()
      moldata.molHnd.Select(self.selHnd,STYPE_ATOM,selHnd,SKEY_NEW)
    else:
      # Adopt the loaded selHnd and delete the existing if nec.
      if self.selHnd>=0: moldata.molHnd.DeleteSelection( self.selHnd)
      self.selHnd = selHnd
    if resetCommand:
      self.command = []
      self.setSameAs(same_as=None)
    self.update_dependents('selection')
    #print 'setSelHnd',self,selHnd,self.selHnd

#-------------------------------------------------------------------
  def setCommand(self,command,retain_sameAs=0):
#-------------------------------------------------------------------

    #print "SelHandle.setCommand",self,command
    text_command = ''
    self.command = []
    if isinstance(command,types.ListType):
      self.command = command
      rv = self.list_to_command(command)
      if not rv[0]: text_command = rv[1]
    else:
     rv = self.command_to_list(command)
     #print 'setCommand>command_to_list',rv
     if not rv[0]:
       self.command = rv[1]
       text_command = command

    self.selparams['select'] =  'cid'
    self.selparams['cid'] = text_command 
    self.setLabel(text_command)
    

    #Unless this is called from selectSameAs remove dependency
    #print 'setCommand',self.same_as,retain_sameAs
    if self.same_as and not retain_sameAs:
        self.remove_dependency(self.get_same_as_SelHandle(),['selection','delete'])
        self.same_as = ''
        self.label = ''

    self.setSelHnd(-1,resetCommand=0)

#-------------------------------------------------------------------
  def setLabel(self,label=''):
#-------------------------------------------------------------------
    
    lab = string.strip(label)

    alias = SELECTION_LABELS.get(lab,'')
    if alias:  
      self.label = alias
    elif len(lab)>SelHandle.maxLabelLength:
      self.label = lab[0:SelHandle.maxLabelLength-2]+'..'
    else:
      self.label = lab

    '''
    import inspect
    print "setLabel",self,self.label
    stack =  inspect.stack()
    for item in stack[1:]: print item
    '''
      
    
#-------------------------------------------------------------------
  def setSaveParams(self,oldparams,dispobj_name=''):
#-------------------------------------------------------------------
    # This is front end to setSelParams which has facility to
    # revise params from old version s of program

    #print "setSaveParams oldparams",dispobj_name,oldparams
    #oldparams.get('command','NO command'),oldparams.get('same_as','NO same_as'),oldparams.get('select','NO select'),oldparams.get('ranges','NO ranges')

    if oldparams.has_key('oldSelection'):
      pm = PM("save_session_options")
      if pm and hasattr(pm,"getparams") and callable(pm.getparams):
        session_params = pm.getparams()
        if session_params.has_key('interpret_old_selections'):
          useOld = session_params['interpret_old_selections']
          if useOld:
            # Hum?
            rv,command = list_to_command(oldparams['oldSelection'])
            #print 'setSaveParams',oldparams['oldSelection'],rv,command
            if rv == 0:
              oldparams['select'] = 'cid'
              oldparams['cid'] = command

    if REVISED_SELECTION_COMMANDS.has_key(oldparams.get('select','')):
      oldparams['select'] = REVISED_SELECTION_COMMANDS[oldparams['select']]

    
    
    # Update v1.0 to v1.1 nov06
    if oldparams.has_key('neighb_wholeres'):
      if oldparams['neighb_wholeres']:
        if oldparams.has_key('neighb_excl_main'):
          if oldparams['neighb_excl_main']:
            oldparams['neighb_group'] = 'side'
          else:
            oldparams['neighb_group'] = 'residue'
      else:
        oldparams['neighb_group'] = 'atom'


    # Update 'same_as' for v2.0 - changed from 0/1 to ''/DispObj.name
    if oldparams.get('select','')=='same_as':
      if not oldparams.get('same_as',''):
        oldparams['same_as'] = dispobj_name

    # Update cid -> command for v2.0
    if not oldparams.has_key('command'):
      command = ''
      old_list = []
      # Update 'ranges'/'chains'/'monomers'/'strands' for v2.0
      if ['main','side','catrace','CA+side','peptide_link','backbone','sugar_ring','base'].count(oldparams.get('select','')):
        if oldparams.get('ranges',[]):
          old_list = oldparams.get('ranges',[])
        elif oldparams.get('chains',[]):
          old_list = oldparams.get('chains',[])
      elif ['monomers','nopeptide'].count(oldparams.get('select','')) and oldparams.get('monomers',[]):
        old_list = oldparams.get('monomers',[])
      elif ['strands'].count(oldparams.get('select','')) and oldparams.get('strands',[]):
        old_list = oldparams.get('strands',[])

       
      if old_list:
        command = ''
        for rng in old_list:
          command = command + rng + ' or '
        command = command[0:-4]
        if ['main','side','catrace','CA+side','peptide_link','backbone','sugar_ring','base'].count(oldparams.get('select','')):
          command = command + ' and ' + oldparams['select']
        oldparams['command'] = command
        oldparams['select'] = 'cid'
        import copy
        oldparams['cid'] = copy.deepcopy(command)
          
      else:
        oldparams['command'] = oldparams.get('cid','')
      #print "created oldparams[command]", oldparams['command']
           

    # Only keep the current parameters
    params = {}
    for key in SelHandle.selection_initialisation.keys():
      if oldparams.has_key(key):
        params[key] = oldparams[key]
        
        
    self.setSelParams(params)

    # Disallowing cases of command = '' leads to all atoms
    # being selected (for example doing status restore after
    # user has cleared the selection and then selected something else)
    #if oldparams.has_key('command') and oldparams['command']:    
    #  self.setCommand(oldparams['command'])
    if oldparams.has_key('same_as') and oldparams['same_as']:
      self.setSameAs(same_as=oldparams['same_as'])


#-------------------------------------------------------------------
  def setSelParams(self,selparams={},apply=1,**kw):
#-------------------------------------------------------------------
    import utils
    selparams.update(kw)
    #rv,diffs,undiffs = utils.dict_diffs(self.selparams,selparams)
    #if diffs:
    #self.selparams.update(selparams)
    #print "setSelParams",self,selparams.get('command',[]),selparams.get('select',[]),selparams.get('chains',[])
    if self.same_as: self.setSameAs(None)
    rv = self.interpret_selection(selparams=selparams)
    #print "setSelParams rv",rv,selparams
    if not rv[0]:
      for key,value in rv[1].items():
        self.selparams[key] = value
      self.update_dependents('selection')
      if apply:
        #print 'in setSelParams',self.makeSelectionLabel(self.selparams)
        self.setLabel(self.makeSelectionLabel(self.selparams))
        self.command = rv[2]
        #self.setCommand(rv[2])
        self.applyCommand()
        #print "setSelParams apply",self.command,self.label,self.selparams

#-------------------------------------------------------------------
  def makeSelectionLabel(self,sel_out):
#-------------------------------------------------------------------
    alias = ''
    if self.same_as and self.same_as == self.dispobj_name:
      alias = 'Same as other'       
    elif sel_out['select'] == 'clear':
      alias = "No atoms"
    elif sel_out['select'] == 'neighb':
      if sel_out['neighb_hb']:
        alias = 'HBond to' + sel_out['neighb_sel']
      else:
        alias = "Nhood of " + sel_out['neighb_sel']
    elif sel_out['select'] == 'buried':
      alias = "Buried by " + sel_out['buried_sel']
    elif sel_out['select']=='features' and \
             SelHandle.features_alias.count(sel_out['features']):
      alias = SelHandle.features_menu[SelHandle.features_alias.index(sel_out['features'])]
    elif sel_out['select'] == 'property':
       alias = str(sel_out['property_gt_value'][0])+ \
               sel_out['property_gt_op'][0] + \
                SelHandle.property_short[SelHandle.property_alias.index(sel_out['property'][0])]+ \
               sel_out['property_lt_op'][0] + \
               str(sel_out['property_lt_value'][0])

    elif  sel_out['select'] == 'restypes':
        if(len(sel_out['restypes'])>0):
          alias = sel_out['restypes'][0]
        for rt in sel_out['restypes'][1:]:
          alias = alias + ","+rt
    elif  sel_out['select'] == 'elementtypes' and len(sel_out.get('elementtypes',[]))> 0:
        alias = sel_out['elementtypes'][0]
        for et in sel_out['elementtypes'][1:]:
          alias = alias + ","+et
    elif ['all','allnotsolv','solvent'].count(sel_out['select']):
      alias = self.commandToLabel(sel_out['select'])
    elif sel_out['select'] == 'cid':
      alias = sel_out['cid']
    elif sel_out['select'] == 'selection_scheme':
      alias = sel_out['selection_scheme']
    elif ['monomers'].count(sel_out['select']):
      if sel_out.get('monomers',''):
        alias = sel_out['monomers'][0]+' '
        for mon in sel_out['monomers'][1:]:
          alias = alias +' '+mon
      else:
        alias = 'Monomers'
    elif ['ranges'].count(sel_out['select']):
      if sel_out.get('ranges',''):
        alias = sel_out['ranges'][0]+' '
        for mon in sel_out['ranges'][1:]:
          alias = alias +' '+mon
    elif ['main','catrace','side','amino_acid','nucleic','chains','peptide','trace','ssbond','peptide_link','backbone','base','sugar_ring'].count(sel_out['select']):
      alias = self.commandToLabel(sel_out['select'])
      if sel_out.get('chains',''):
        if ['amino_acid','peptide'].count(sel_out['select']):
            alias = ''
        else:
            alias = alias + ' '
        alias = alias + sel_out['chains'][0]
        for chn in sel_out['chains'][1:]:
          alias = alias + ',' + chn
          
      elif sel_out.get('strands',''):
          if ['nucleic'].count(sel_out['select']):
            alias = ''
          else:
            alias = alias + ' '     
          alias = sel_out['strands'][0]
          for chn in sel_out['strands'][1:]:
            alias = alias + ',' + chn


    # Alternate locs
    if sel_out.get('alt_locs','') and not alias:
        alias = 'Alt locs'+sel_out['alt_locs'][0]
        for item in sel_out['alt_locs'][1:]:
          alias = alias + ',' + item


    # NMR models
    if sel_out.get('nmr_models','') and (data(self.moldata_name).nmr_models or  len (data(self.moldata_name).model_symmetry.get_symmetry_models())>0) :
      if not alias :
        alias = 'Models' + string.strip(sel_out['nmr_models'][0],'/')
        for nmr in sel_out['nmr_models'][1:]:
          alias = alias + ','+string.strip(nmr,'/')
        
    if sel_out.get('notselect',0):
      alias = 'Not ' + alias

    return alias

#-------------------------------------------------------------------
  def get_same_as_SelHandle(self):
#-------------------------------------------------------------------    
    if self.same_as:
      dispobj = get_dispobj(name=self.same_as)
      if dispobj:    
        if hasattr(dispobj,'SelHandle'):
          return dispobj.SelHandle
        elif hasattr(dispobj,'SelHandle1'):         
          return dispobj.SelHandle1
    return None

#-------------------------------------------------------------------
  def setSameAs(self,same_as=None,label=''):
#-------------------------------------------------------------------
    #print "into SelHandle.setSameAs",self.same_as,'in:',same_as,label
    if same_as:
      dispobj = None
      if isinstance(same_as,dataobj.DispObj):
        dispobj = same_as
      elif isinstance(same_as,types.StringType):
        dispobj = get_dispobj(name=same_as)
      if not dispobj or not (hasattr(dispobj,'SelHandle') or  hasattr(dispobj,'SelHandle1')):
         return
     
      if self.same_as:
        self.remove_dependency(self.get_same_as_SelHandle(),['selection','delete'])
        self.label = ''
        self.same_as = ''

      if dispobj.name == self.dispobj_name:
        self.label = 'Same as other'
      else:
        self.label = ''
        
      self.same_as = dispobj.name
      self.add_dependency(self.get_same_as_SelHandle(),['selection','delete'],[self.selectSameAs,self.updateSameAs])

      self.selectSameAs()
      
    else:
      if self.same_as:
        # For now remove all dependencies - may need to be
        # more specific if create other dependencies in future
        self.remove_dependency(self.get_same_as_SelHandle(),['selection','delete'])
        self.same_as = ''
        self.label = ''

    self.applyCommand()

#-------------------------------------------------------------------
  def getSameAs(self):
#-------------------------------------------------------------------
    return getattr(self,'same_as',0)
  

#-------------------------------------------------------------------
  def selectSameAs(self,master=None,attribute=''):
#-------------------------------------------------------------------
    # Just copy selparams rather than going through setSelParams
    # which updates everything
    same_as_sel = self.get_same_as_SelHandle()
    if not same_as_sel: return ''
    if same_as_sel.getCommand(internal=1) == self.command: return
    #print 'selectSameAs',self.dispobj_name,same_as_sel,same_as_sel.getCommand(),same_as_sel.getSelParams()
    self.selparams.update(same_as_sel.getSelParams())
    self.command = same_as_sel.getCommand(internal=1)
    self.setSelHnd(-1,resetCommand=0)
    #self.setCommand(same_as_sel.getCommand(internal=1),retain_sameAs=1)
    self.label = self.makeSelectionLabel(self.selparams)
    if self.dispobj_name:
      get_dispobj(name=self.dispobj_name).set_reapply_selection()
      if not DISPLAYTABLE(): return
      gobj = DISPLAYTABLE().getDispObj(name=self.dispobj_name)
      if gobj:
        target =  get_dispobj(gobj.objectName())
        if not target or not hasattr(target,"SelHandle2"):
          return
        gobj.updateLabel('selection1')
        gobj.updateLabel('selection2')

      
#-------------------------------------------------------------------
  def updateSameAs (self, master = None, attribute = ''):
#-------------------------------------------------------------------
    if attribute == 'delete':
      self.setSameAs(same_as=None)
    
#-------------------------------------------------------------------
  def getSelParams(self,all='all'):
#-------------------------------------------------------------------
    pars = {}
    for key,value in SelHandle.selection_initialisation.items():
      if isinstance(value,types.ListType):
        pars[key] = []
        for item in self.selparams.get(key):
          pars[key].append(item)
      else:
        pars[key] = self.selparams.get(key)
 
    #pars.update(self.selparams)
    return pars


#-------------------------------------------------------------------
  def getSaveParams(self,all='all'):
#-------------------------------------------------------------------
    # The following method of copying a dict is necessary to prevent
    # mangled data when cloning display objects - this was particularly
    # obvious with selecting chains.  Python is broke.
    params = {}
    for key,value in SelHandle.selection_initialisation.items():
      if isinstance(value,types.ListType):
        params[key] = []
        for item in self.selparams.get(key):
          params[key].append(item)
      else:
        params[key] = self.selparams.get(key)
    #params.update(self.selparams)
    params['command'] = self.getCommand()
    params['same_as'] = self.getSameAs()
    #print "getSaveParams",self,params.get('command',[]),params.get('select',''),params.get('chains',''),params.get('same_as','')
    return params


#-------------------------------------------------------------------
  def getMolData(self):
#-------------------------------------------------------------------
    return self.moldata_name

#-------------------------------------------------------------------
  def getMolHnd(self):
#-------------------------------------------------------------------
    return data(self.moldata_name).molHnd
  
#-------------------------------------------------------------------
  def getLabel(self):
#-------------------------------------------------------------------
    if self.label:
      return self.label
    elif self.command:
      rv,com = self.list_to_command(self.command)
      return com
      '''
      if len(com)<20:
        return com
      else:
        return com[0:]+'..'
      '''
    else:
      return ''

#-------------------------------------------------------------------
  def getSelHnd(self,always=0):
#-------------------------------------------------------------------
    #print "SelHandle.getSelHnd ",self,self.selHnd
    if self.selHnd ==-1: self.applyCommand(always=always)

    return self.selHnd


#-------------------------------------------------------------------
  def getNofAtoms(self):
#-------------------------------------------------------------------
    if self.selHnd == -1: self.applyCommand()
    if self.selHnd < 0: return -1
    
    nat = data(self.moldata_name).nAtoms(self.selHnd)
    return nat

#-------------------------------------------------------------------
  def applyCommand(self,always=0):
#-------------------------------------------------------------------
    #print "SelHandle.applyCommand ",self,self.command
    if self.command:
      rv = data(self.moldata_name).parse_selection(self.command)
      #print "applyCommand rv",rv,self.command
    else:
      rv = 1,'No command'
    if rv[0]:
      if always:
        self.selHnd = data(self.moldata_name).molHnd.NewSelection()
        return 0
      else:
        self.selHnd = -2
        return 1
    else:
      self.setSelHnd(rv[1],resetCommand=0,copy=0)
      return 0

#-------------------------------------------------------------------
  def mergeSelection(self,command='',op='new',notop=0,SelHandle=None):
#-------------------------------------------------------------------
    #print "SelHandle.mergeSelection op,self.command",op,self.command
    com = []
    if command:
      if isinstance(command,types.StringType):
        rv,com = self.command_to_list(command)
        if rv: return 1
        if notop: com = ['not',com]
      else:
        com = command
    elif SelHandle:
      com = SelHandle.getCommand(internal=1)

    #print "SelHandle.mergeSelection com",com,'self.command',self.command
    if not com:
      return
    elif op == 'new' or ['',None,[None,''],[]].count(self.command):
      self.setCommand ( com )
    else:
      self.setCommand ([op,self.command,com])

    self.setLabel(self.list_to_command(self.command)[1])
    
 
#-------------------------------------------------------------------
  def getCommand(self,internal=0):
#-------------------------------------------------------------------
    if internal:
      return self.command
    if not self.command:
      return ''
    else:
      rv,com = self.list_to_command(self.command)
      if not rv:
        return com
      else:
        return ''

#-----------------------------------------------------------------------
  def getSelIndex(self,always=0):
#-----------------------------------------------------------------------

    #print "SelHandle.getSelIndex", self.selHnd
    if self.selHnd==-1: self.applyCommand(always=always)
    if self.selHnd<0: return [-1,'No selection']
      
    selAtoms = newPPCAtom()
    selindexp = intp()
    #nselatoms =data(self.moldata_name).molHnd.GetSelIndex(self.selHnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(data(self.moldata_name).molHnd,self.selHnd,selindexp)
    nselatoms = selindexp.value()
    #print "SelHandle.getSelIndex Number of selected atoms",nselatoms
    #delPPCAtom(selAtoms)
    return nselatoms,selAtoms
 
#----------------------------------------------------------------------
  def saveSelection ( self,name='',save_mode='this',overwrite=0):
#----------------------------------------------------------------------
    if not self.command:  return -1
    if not name: return -1
    import utils
    name = utils.oneWord(name)
    
    if save_mode == 'this':  save_mode=self.moldata_name
    status = MODELINFO().add_info(set=save_mode,info='user_selection',
         key=name,value=self.command)
    if not status:
      # Save the non-default selparams
      rv,diffs,undiffs = utils.dict_diffs(SelHandle.selection_initialisation,self.selparams)
      #print "saving selparams",diffs
      MODELINFO().add_info(set=save_mode,info='user_selparams',
         key=name,value=diffs)
      # Force a save to file
      MODELINFO().save(set=self.moldata_name,filename= data(self.moldata_name).filename)

      
#----------------------------------------------------------------------
  def restoreSelection(self,name='',op='new'):
#----------------------------------------------------------------------
    retval,user_selection=MODELINFO().get_info( \
          set=[self.moldata_name,'generic'], \
          info='user_selection',key=name,first=1)

    #print "restoreSelection",user_selection
    command = user_selection.get(name,[])
    if retval or not command:
      MGGUI().WarningMessage('Selection ' + name + ' not recognised', \
                                 parent='DisplayTable')
    else:
      self.mergeSelection(command=command,op=op)
      if op == 'new': self.setLabel(name)

#------------------------------------------------------------------------
  def interpret_selection ( self, selparams={}, **keywords ):
#-----------------------------------------------------------------------
    import model
    selparams.update(keywords)
    #print "interpret selparams",selparams
    
    # Initialise the sel_out array
    sel_out = {}
    for key,value in SelHandle.selection_initialisation.items():
      sel_out[key]=value
    # Load any previous settings
    sel_out.update(selparams)    

    # Generate the selection command based on the sel_out array

    sel = []
    warning = ""

    if sel_out['select'] == 'clear':
      sel = [None,'']
    elif sel_out['select'] == 'neighb':    
      if sel_out['neighb_rad'] == '':
        warning = warning + "Neighbourhood radius undefined\n"
# Test if something actually selected
      if sel_out['neighb_sel']:
        if sel_out['neighb_mol']:
          data_obj = get_dataobj(name=sel_out['neighb_mol'])
        else:
          data_obj = get_dataobj(self.moldata_name)
        if not data_obj:  return [1]
        data_obj = data_obj[0]
        rv = data_obj.parse_selection (command=sel_out['neighb_sel'],test=1)
        #print "interpret_selection neighb parse_selection rv",rv
        if rv[0] != 0:
          warning =  warning + "Neighbourhood selection uninterpretable\n"
        elif rv[1] == 0:
          warning =  warning + "Neighbourhood selection finds no atoms\n"
      elif sel_out['neighb_selobj']:
        selobj_rv = data(self.moldata_name).interpret_selobj_alias( sel_out['neighb_selobj'])
        if not selobj_rv:
          warning =  warning + "Neighbourhood selection alias not interpretable\n"
      else:
        warning =  warning + "Neighbourhood selection not defined\n"
      # Strip spaces from neighb_sel - liable to come from unnamed chain
      # and certain to mess up splitting to words in selection
      if sel_out['neighb_selobj'] and selobj_rv:
        selo = string.split(sel_out['neighb_selobj'],'#')
        if len(selo) == 2:
          sel0 = "neighb selobj_alias=" + selo[1] +' selobj_model='+selo[0]
        else:
          sel0 = "neighb selobj_alias=" + sel_out['neighb_selobj']
        '''
        if selobj_rv[0] == 'rules':
          alias = "Nhood of " +sel_out['neighb_selobj']
        elif len(selo) == 2:
          alias = "Nhood of " +selo[0]+' '+getattr(selobj_rv[1],'selection_label','')
        elif  selobj_rv[0] == 'user':
          alias = "Nhood of " +selobj_rv[1]
        else:
          alias = "Nhood of " +getattr(selobj_rv[1],'selection_label','')
        '''
      else:
        sel0 = 'neighb cid="' +string.strip(sel_out['neighb_sel'])+'"'
        if sel_out['neighb_mol']: sel0 = sel0 + " model=" + sel_out['neighb_mol']

      sel0=sel0+" maxd="+ str(sel_out['neighb_rad'])
      sel0 = sel0 + " group="+sel_out['neighb_group']
      if sel_out['neighb_hb']:
        sel0 = sel0 +" hbonded="+str(sel_out['neighb_hb'])
      if sel_out['neighb_sym']:
        sel0 = sel0 +" sym_mates="+str(sel_out['neighb_sym'])
      n_excl = ''
      if sel_out.get('neighb_excl',0):n_excl = n_excl+',central'
      if sel_out.get('neighb_excl_solvent',0): n_excl = n_excl+',solvent'
      if sel_out.get('neighb_excl_monomer',0): n_excl = n_excl+',monomer'
      if sel_out.get('neighb_excl_site',0): n_excl = n_excl+',site'
      if sel_out.get('neighb_excl_peptide',0): n_excl = n_excl+',peptide'
      if n_excl: sel0 = sel0 + " excl=" + n_excl[1:]
      #print "pret_sel sel0",sel0
      sel = [None,sel0]

      if sel_out.get('neighb_and','') and sel_out.get('neighb_and_selection',''):
        #print "neighb_and",sel_out['neighb_and_selection'],sel_out['neighb_and_label']
        sel = ['and',sel,self.command_to_list(sel_out['neighb_and_selection'])[1]]
        #print "sel",sel

    elif sel_out['select']=='features' and \
             SelHandle.features_alias.count(sel_out['features']):
      sel = [None,sel_out['features']]

    elif sel_out['select'] == 'buried':    
      if sel_out.get('buried_sel',''):
        if sel_out.get('buried_mol',''):
          data_obj = get_dataobj(name=sel_out['buried_mol'])
          #print "neighb_mol",sel_out['neighb_mol'],data_obj
          if not data_obj: return [1]
          data_obj = data_obj[0]
          if data_obj == data(self.moldata_name):
            sel_out['buried_mol']=''
        else:
          data_obj = data(self.moldata_name)
        rv = data_obj.parse_selection ( \
                       command=sel_out['buried_sel'],test=1)
        #print "interpret_selection buried trying parse_selection rv",rv
        if rv[0] != 0:
          warning = warning + "Buried area selection uninterpretable\n"
        elif rv[1] == 0:
          warning =  warning + "Buried area selection finds no atoms\n"
      elif sel_out.get('buried_selobj',''):
        selobj_rv = data(self.moldata_name).interpret_selobj_alias( sel_out['buried_selobj'])
        if not selobj_rv:
          warning =  warning + "Buried area selection alias not interpretable\n"
      else:
        warning =  warning + "Buried area selection not defined\n"
      # Strip spaces from neighb_sel - liable to come from unnamed chain
      # and certain to mess up splitting to words in selection
      if sel_out.get('buried_selobj',''):
        selo = string.split(sel_out['buried_selobj'],'#')
        if len(selo) == 2:
          sel0 = "buried selobj_alias=" + selo[1] +' model='+selo[0]
        else:
          sel0 = "buried selobj_alias=" + sel_out['buried_selobj']

        '''
        if selobj_rv[0] == 'rules':
          alias = "Buried " +sel_out['buried_selobj']
        elif len(selo) == 2:
          alias = "Buried by " +selo[0]+' '+selobj_rv[1].selection_label
        else:
          alias = "Buried by " +selobj_rv[1].selection_label
        '''

      else:
        sel0 = 'buried cid="' +string.strip(sel_out['buried_sel'])+'"'
        if sel_out.get('buried_mol',''): sel0 = sel0 + " model=" + sel_out['buried_mol']

      sel0 = sel0 + ' self_cid="'+sel_out.get('buried_and_selection','')+'" group="'+sel_out.get('buried_group','atom')+'"'
      #print "pret_sel buried sel0",sel0
      sel = [None,sel0]


    # Selection by property
    elif sel_out['select'] == 'property':
     #print "property sel",sel_out['property']
     for i in range(0,len(sel_out.get('property',[]))):
        #if re.search('CONTACT',sel_out['property'][i]) and self.contact_UDD:
        #  ext = '-'+str(self.contact_UDD[2])
        #else:
        #  ext = ''
        sel0 = ''
        sel1 = ''
        if sel_out['property_gt_op'][i] and \
           (sel_out['property_gt_value'][i] or sel_out['property_gt_value'][i]==0.0) :
          sel0 = sel0 + str( sel_out['property_gt_value'][i])+' '+sel_out['property_gt_op'][i]+ \
                           ' '+sel_out['property'][i]
        else:
          sel1 = ' ' + sel_out['property'][i]
        if sel_out['property_lt_op'][i] and \
          (sel_out['property_lt_value'][i] or sel_out['property_lt_value'][i]==0.0) :
          sel0 = sel0 + sel1 + ' ' + sel_out['property_lt_op'][i]+' '+str( sel_out['property_lt_value'][i])
      
        if sel:
          sel = [sel_out['property_join'],sel,[None,sel0]]
        else:
          sel = [None,sel0]
        
    # Residue types
    elif  sel_out['select'] == 'restypes':
      if len(sel_out.get('restypes',[]))> 0:
        sel0 = '/*/*/('+sel_out['restypes'][0]
        for item in sel_out['restypes'][1:]: sel0 = sel0 +','+item
        sel0 = sel0 + ')/*:*'
        #print 'sel0',sel0
        sel = [None,sel0]
 
    # Element types
    elif  sel_out['select'] == 'elementtypes' and len(sel_out.get('elementtypes',[]))> 0:
        sel0 = '/*/*/*/*['+sel_out['elementtypes'][0]
        for item in sel_out['elementtypes'][1:]: sel0 = sel0 +','+item
        sel0 = sel0 + ']:*'
        sel = [None,sel0]
        
             
    elif ['all','allnotsolv','solvent','saccharide','nucleotide','metal'].count(sel_out['select']):
      import selection_protocols
      sel = [None,sel_out['select']]    
    
    elif sel_out['select'] == 'cid':
      status,sel = self.command_to_list(sel_out['cid'])
      #print "command_to_list ",status,sel
      if status: return [1]
      else:
         return [0,sel_out,sel]
  
    # Test that there is a saved user_selection with the key 
    elif sel_out['select'] == 'selection_scheme':
      retval,user_selection=MODELINFO().get_info( set=[self.moldata_name,'generic'], 
          info='user_selection',key=sel_out['selection_scheme'],first=1)
      #print 'interpret_selection MODELINFO().get_info',retval,user_selection
      if retval or not user_selection:
        # filed to find saved user seelction so try creating a new user selection
        if sel_out['cid']:
          rv,sel = self.command_to_list(sel_out['cid'])
          #print 'sel_out[cid]',sel_out['cid'],retval,sel 
          if not rv:
            status = MODELINFO().add_info(set=self.moldata_name,info='user_selection',
               key=sel_out['selection_scheme'],value=sel)
            #Try retrieving saved user_selection again
            retval,user_selection=MODELINFO().get_info(set=[self.moldata_name,'generic'], 
                   info='user_selection',key=sel_out['selection_scheme'],first=1)
      if retval or not user_selection.has_key(sel_out['selection_scheme']):
        # Still no saved user selection so abort
        MGGUI().WarningMessage('Selection ' + sel_out['selection_scheme'] + ' not recognised',
                                 parent='DisplayTable')
        return [1]
      sel = user_selection[sel_out['selection_scheme']]
      #print "interpret_selection user_selection",user_selection,sel_out['selection_scheme'],sel

    if ['monomers'].count(sel_out['select']):
      if sel_out.get('monomers',''):
        monsel = [None,sel_out['monomers'][0]]
        for mon in sel_out['monomers'][1:]:
          monsel = ['or',monsel,[None,mon]]
        if sel:
          sel = ['or',sel,monsel]
        else:
          sel = monsel
      else:
        sel = [None,'monomers']
      
    if ['sites'].count(sel_out['select']):
      if sel_out.get('sites',''):
        monsel = [None,sel_out['sites'][0]]
        for mon in sel_out['sites'][1:]:
          monsel = ['or',monsel,[None,mon]]
        if sel:
          sel = ['or',sel,monsel]
        else:
          sel = monsel
      else:
        sel = [None,'sites']
      
    elif ['peptide','main','catrace','side','amino_acid','nucleic','chains','trace','ssbond','backbone','base','sugar_ring'].count(sel_out['select']):
      if not ['chains','ranges','trace'].count(sel_out['select']):
        sel = [None,sel_out['select']]
        
      '''
      elif ['trace'].count(sel_out['select']):
        trace_selection = PM('model_drawing_style').get('trace_selection')
        trace_atom = PM('model_drawing_style').get('trace_atom')
        sel = ['and',[None,trace_selection],[None,'/*/*/*/'+trace_atom]]
      '''
        
      if sel_out.get('chains',''):
        chnsel = [None,sel_out['chains'][0]+'/*.*/*:*']
        for chn in sel_out['chains'][1:]:
          chnsel = ['or',chnsel,[None,chn+'/*.*/*:*']]
        if sel:
          sel = ['and',sel,chnsel]
        else:
          sel = chnsel
          
      elif sel_out.get('strands',''):
        chnsel = [None,sel_out['strands'][0]+'/*.*/*:*']
        for chn in sel_out['strands'][1:]:
          chnsel = ['or',chnsel,[None,chn+'/*.*/*:*']]
        sel = ['and',sel,chnsel]

        
      # Secondary structure
      elif sel_out.get('secstr','') and sel_out['select'] != 'nopeptide':
        #ii =model.MolData.secstr_alias.index(sel_out['secstr'][0])
        #sel0 = [None,'SEC ==  '+ model.MolData.secstr_code[ii]]
        sel0 = [None,sel_out['secstr'][0].lstrip('H ').lstrip('S ')]
        for sec in sel_out['secstr'][1:]:
          sel0 = ['or',sel0,[None,sec.lstrip('H ').lstrip('S ')]]
          #ii = model.MolData.secstr_alias.index(sec)
          #sel0 = ['or',sel0,[None,'SEC == '+model.MolData.secstr_code[ii]]]
        if sel:
          sel = ['and',sel,sel0]
        else:
          sel = sel0
        #print "after sec",sel


          # RANGES  
    elif sel_out.get('select','')=='ranges' and (sel_out.get('ranges',[]) or sel_out.get('ranges_neighb_selection',[])):
      comd = None
      if (not sel_out['ranges']) and sel_out['ranges_neighb_selection']:
        # PREPROCESS to find the ranges specified in a neighbr command and then save the ranges
        rv = data(self.moldata_name).selectNeighbRanges(cid=sel_out['ranges_neighb_selection'],model=sel_out.get('ranges_neighb_model',''),maxd=sel_out.get('ranges_neighb_cutoff',4.0),group=sel_out.get('ranges_neighb_qualifier','all'),invert=sel_out.get('ranges_neighb_invert',0),allModels=sel_out.get('use_all_models',False))
        #print 'interpret_selection after selectNeighbRanges',rv
        if not rv[0]:
          sel_out['ranges'] = []
          for item in rv[1]:sel_out['ranges'].append(item)
      elif sel_out['ranges'] and sel_out.get('ranges_invert',0):
        rv =  data(self.moldata_name).selectInvertRanges(cid=sel_out['ranges'],qualifier='peptide',allModels=sel_out.get('use_all_models',False))
        print 'interpret_selection after selectInvertRanges',rv
        if not rv[0]:
          sel_out['ranges'] = []
          for item in rv[1]:sel_out['ranges'].append(item)
          sel_out['ranges_invert'] = 0
      if sel_out['ranges']:
        comd = 'ranges cid="' +sel_out['ranges'][0].strip()
        for sele in sel_out['ranges'][1:]: comd = comd +','+sele.strip()
        comd = comd + '"'
      if comd:
        comd = comd +' extend='+str(sel_out.get('ranges_extend',1))+' qualifier='+sel_out.get('ranges_qualifier','all')
        if sel:
          sel = ['and',sel,[None,comd]]
        else:
          sel = [None,comd]
      #print 'interpret_selection ranges',sel
  
    # End of handling for peptide/monomers etc
    if sel_out.get('notselect',0):
      if sel: sel = ['not',sel]

     
    # SOLVENT
    if sel_out.get('solvent',''):
      if not sel or sel == [None, ''] :
        sel = ['or',[None,'solvent'],[None,'solute']]
      elif  sel_out['select'] != 'neighb':
        sel = ['or',sel,['or',[None,'solvent'],[None,'solute']]]

    '''
    # ATOMTYPE
    if sel_out.get('atomtype',''):
      if not sel or sel == [None, ''] :
        sel = [None,sel_out['atomtype']]
      else:
        sel = ['and',sel,[None,sel_out['atomtype']]]
    '''

    # EXCL H
    if  sel_out.get('nothydrogen',''):
      if sel: sel = ['excl',sel,[None,'/*/*/*,*/*[H]:*']]

    # Alternate locs
    if sel_out.get('alt_locs',''):
      if sel_out['alt_locs'][0] == 'no_alt':
        altsel = [None , '/*/*/*/*:']
        al='None'
      else:
        altsel = [None , '/*/*/*/*:'+ sel_out['alt_locs'][0]]
        al=sel_out['alt_locs'][0]
      for alt in sel_out['alt_locs'][1:]:
        if alt == 'no_alt':
          altsel = ['or',altsel,[None , '/*/*/*/*:']]
          al=al+',None'
        else:
          altsel = ['or',altsel,[None , '/*/*/*/*:'+alt]]
          al=al+','+alt
      if sel and sel != [None,'all']:
        sel = ['and',sel,altsel]
      else:
        sel = altsel


    # NMR models
    if sel_out.get('nmr_models','') and (data(self.moldata_name).nmr_models or  len (data(self.moldata_name).model_symmetry.get_symmetry_models())>0) :
      #print "model_seelction.interpret_selection sel_out['nmr_models']",sel_out['nmr_models']
      nmr = sel_out['nmr_models'][0]
      nmr_split =  string.split(nmr,'/')
      nmrsel =  [None,sel_out['nmr_models'][0]]
      for nmr in sel_out['nmr_models'][1:]:
        nmrsel = ['or',nmrsel,[None,nmr]]
        nmr_split =  string.split(nmr,'/')
      if sel and sel != [None,'all']:
        sel = ['and',sel,nmrsel]
      else:
        sel = nmrsel
      #print "NMR sel",sel


    #print "interpret_selection",sel_out
    #print "interpret_selection",self,sel
    
    return [0,sel_out,sel]

#-------------------------------------------------------------------
  def commandToLabel(self,item):
#-------------------------------------------------------------------
    '''
    sel_menu = ['All','All peptide','All peptide','Main chain','CA trace','Side chains',
                     'No peptide','All nucleic acid','No nucleic acid','Monomers' ,'Trace']
    sel_command = ['all','amino_acid','peptide','main','catrace','side', \
                 'nopeptide','nucleic','nonucleic','monomers' , 'trace']
      return sel_menu[sel_command.index(item)]
    else:
      return ''
    '''
    return SELECTION_LABELS.get(item,'')

#----------------------------------------------------------------------
  def popHistory(self):
#----------------------------------------------------------------------
    pass


#----------------------------------------------------------------------
  def cleanSelection(self,hydrogens=0,close=-1):
#----------------------------------------------------------------------
    if self.selHnd<0: self.applyCommand()
    if self.selHnd<0: return [-1,'No selection']

    moldata = data(self.moldata_name)
    
    # Remove hydrogens
    if hydrogens and moldata.contains_hydrogen:
      #moldata.molHnd.Select(self.selHnd,STYPE_ATOM,"[H]",SKEY_CLR)
      moldata.molHnd.SelectAtoms(self.selHnd, 0,"*",ANY_RES,"*",ANY_RES,"*",
                               "*","*","H","*",SKEY_CLR )

    if close>0:
      nexcluded = moldata.molHnd.ExcludeOverlappedAtoms(self.selHnd,close)
      #print "cleanSelection nexcluded",nexcluded


#----------------------------------------------------------------------
  def cleanAltLocs(self,allow=1,test=1):
#----------------------------------------------------------------------
    if self.selHnd<0: self.applyCommand()
    if self.selHnd<0: return [-1,'No selection']

    # Remove alternate locations 
    rv = 0  # return the number of altLocs removed from selection
    moldata = data(self.moldata_name)
    if not moldata.alt_locs: return 0
    selindexp = intp()

    if test:
      # test whether there are any altloc selected - application
      # may need to know in order to warn user
      selected_alts = []
      altHnd = moldata.molHnd.NewSelection()
      altSel = newPPCAtom()
      for alt in  moldata.alt_locs:
        moldata.molHnd.Select  (altHnd,STYPE_ATOM,0,'*', \
         ANY_RES,'*',ANY_RES,'*','*','*','*',alt,SKEY_NEW)
        moldata.molHnd.Select(altHnd,STYPE_ATOM,self.selHnd,SKEY_AND)
        #if moldata.molHnd.GetSelIndex(altHnd,altSel)>0: selected_alts.append(alt)
        altSel = mmut.GetAtomSelIndex(moldata.molHnd,altHnd,selindexp)
        if selindexp.value()>0: selected_alts.append(alt)
      moldata.molHnd.DeleteSelection(altHnd)
      rv = len(selected_alts)-allow
    else:
      # Don't bother testing
      selected_alts = moldata.alt_locs

    #print "cleanAltLocs selected_alts",selected_alts
      
    if len(selected_alts)>allow:
      for alt in selected_alts[allow:]:
        moldata.molHnd.Select (self.selHnd,STYPE_ATOM,0,'*', \
          ANY_RES,'*',ANY_RES,'*','*','*','*',alt,SKEY_CLR)
        
    return rv

#-------------------------------------------------------------------------
def mmdbHandle2Command(molHnd,selHnd):
#-------------------------------------------------------------------------
    import copy
    command = ''
    selAtom = newPPCAtom()
    selRes = newPPCResidue()
    selChain = newPPCChain()
    allSelHnd = molHnd.NewSelection()
    notSelHnd = molHnd.NewSelection()
    notResSelHnd = molHnd.NewSelection()
    resSelHnd = molHnd.NewSelection()
    chnSelHnd = molHnd.NewSelection()
    notChnSelHnd = molHnd.NewSelection()

    # Get 'all' selection
    molHnd.SelectAtoms(allSelHnd, 0,"*",ANY_RES,"*",ANY_RES,"*", \
                               "*","*","*","*",SKEY_NEW )

    # Get the atoms and then residue not in the selection
    molHnd.Select(notSelHnd,STYPE_ATOM,allSelHnd,SKEY_NEW)    
    molHnd.Select(notSelHnd,STYPE_ATOM,selHnd,SKEY_CLR)    
    molHnd.Select(notResSelHnd,STYPE_RESIDUE,notSelHnd,SKEY_NEW)
    #print 'mmdbHandle2Command selHnd',self.nAtoms(selHnd),' notSelHnd',self.nAtoms(notSelHnd),' notResSelHnd',self.nResidues(notResSelHnd)

    # Get residues which have not no atoms in selection (ie all atoms in
    # the residue are selected
    molHnd.Select(resSelHnd,STYPE_RESIDUE,allSelHnd,SKEY_NEW)
    molHnd.Select(resSelHnd,STYPE_RESIDUE,notResSelHnd,SKEY_CLR)

    '''
    # The following does not seem to work - will give whole chain
    # selection when inappropriate.  Do not expect whole chains in
    # extracting PISA data which is the current only use so comment
    # this out
    # get the chains have some atoms which are not in the selection
    # 'not' this selection to get chains for which all atoms are selected
    molHnd.Select(notChnSelHnd,STYPE_CHAIN,notResSelHnd,SKEY_NEW)
    molHnd.Select(chnSelHnd,STYPE_CHAIN,allSelHnd,SKEY_NEW)
    molHnd.Select(chnSelHnd,STYPE_CHAIN,notChnSelHnd,SKEY_CLR)

    nSelChn = molHnd.GetSelIndex(chnSelHnd,selChain)
    print 'mmdbHandle2Command nSelChn',nSelChn
    for ii in range(nSelChn):
      chnid = CChainPtr(getPCChain(selChain,ii)).GetChainID()
      if chnid:
        command = command + chnid+'/ or '
      else:
        command = command +'// / or'
      print 'mmdbHandle2Command command',command
      
    # Remove the 'whole chains' from the residue selection
    # Find the whole residues in the selection
    #molHnd.Select(resSelHnd,STYPE_RESIDUE,chnSelHnd,SKEY_CLR)
    '''
    
    selindexp = intp()
    #nSelRes = molHnd.GetSelIndex(resSelHnd,selRes)
    selRes = mmut.GetResidueSelIndex(molHnd,resSelHnd,selindexp)
    nSelRes = selindexp.value()
    #print 'mmdbHandle2Command nSelRes',nSelRes
    if nSelRes>0:
      ii = 1
      ifres = getPCResidue(selRes,0)
      ilres = ifres #copy.deepcopy(ifres)
      while ii<nSelRes:
        ires = getPCResidue(selRes,ii)
        #print 'ires',ires.index,'ilres', ilres.index
        if ires.index ==  ilres.index +1:
          ilres = ires #copy.deepcopy(ires)
        else:
          command = command + mmdbHandleResCom(ifres,ilres)
          ifres =  ires #copy.deepcopy(ires)
          ilres =  ires #copy.deepcopy(ires)
        ii = ii + 1
      command = command + mmdbHandleResCom(ifres,ilres)
    #print 'mmdbHandle2Command command',command

    # Remove atoms that were in whole residue selections and then add
    # Any remaining atoms to the command
    molHnd.Select(notSelHnd,STYPE_ATOM,resSelHnd,SKEY_NEW)
    #print 'mmdbHandle2Command Atoms to be excluded',self.nAtoms(notSelHnd)
    molHnd.Select(selHnd,STYPE_ATOM,notSelHnd,SKEY_CLR)
    #nSelAtom = molHnd.GetSelIndex(selHnd,selAtom)
    selAtom = mmut.GetAtomSelIndex(molHnd,selHnd,selindexp)
    nSelAtom = selindexp.value()
    #print 'mmdbHandle2Command nSelAtom',nSelAtom
    if nSelAtom:
      ii = 1
      iat_list = [getPCAtom(selAtom,0)]
      while ii < nSelAtom:
        iat = getPCAtom(selAtom,ii)
        if iat.residue.index == iat_list[0].residue.index:
          iat_list.append(iat)
        else:
          command = command + mmdbHandleAtomCom(iat_list)
          iat_list = [iat]
        ii = ii + 1
      command = command + mmdbHandleAtomCom(iat_list)

    # Remove the last 'or'
    if command: command = command[0:-4]
    
    molHnd.DeleteSelection(allSelHnd)
    molHnd.DeleteSelection(notSelHnd)
    molHnd.DeleteSelection(notResSelHnd)
    molHnd.DeleteSelection(resSelHnd)
    molHnd.DeleteSelection(chnSelHnd)
    molHnd.DeleteSelection(notChnSelHnd)

    return [0,command]

#-------------------------------------------------------------------------
def mmdbHandleResCom(ifres,ilres):
#-------------------------------------------------------------------------
    # return selection command for either single residue or range of residues
    chainid = ifres.chain.GetChainID()
    if ['',' '].count(chainid): chainid='// '
    if ifres.index == ilres.index:
      if ifres.GetInsCode():
        command = chainid+'/'+str(ifres.seqNum) + '.'+str(ifres.GetInsCode()) +' or '
      else:
        command = chainid+'/'+str(ifres.seqNum) + ' or '
    else:
      command = chainid+'/'+str(ifres.seqNum)
      if ifres.GetInsCode(): command = command + '.'+str(ifres.GetInsCode())
      if ilres.GetInsCode():
        command = command +'-' + str(ilres.seqNum) + '.'+str(ilres.GetInsCode()) +  ' or '
      else:
        command = command +'-' + str(ilres.seqNum)  +  ' or '
    return command

#-------------------------------------------------------------------------
def mmdbHandleAtomCom(iat_list):
#-------------------------------------------------------------------------
    import string
    ires = iat_list[0].residue
    chainid = ires.chain.GetChainID()
    if ['',' '].count(chainid): chainid='// '
    if ires.GetInsCode():
       command = chainid+'/'+str(ires.seqNum) + '.'+str(ires.GetInsCode()) +'/'
    else:
       command = chainid+'/'+str(ires.seqNum) + '/'
    for iat in iat_list:
      if iat.GetAltLoc():
        command = command + string.strip(iat.GetAtomName()) + ':' + string.strip(iat.GetAltLoc())+','
      else:
        command = command + string.strip(iat.GetAtomName()) +','
    return command[0:-1]+' or '


def fix_v108(selparams):
  #print 'fix_v108', selparams.get('command','')
  if selparams.get('command',''):
            selparams['select'] = 'cid'
            selparams['cid'] = selparams.get('command','')
  return selparams
