"""
     python/ui/Superpose.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2010 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 sys
from  mgapp import *
from mmut import *
import mmut
from dataobj import *
from Application import *
from global_definitions import *
import cprim
import copy
import os
import tempfile
import subprocess
import pygl_coord

'''
Methods
ssm - Secondary structure (SSM)
close - Close atoms
lsq - least square fit of seletected range(s) of residues
interactive - least square fit of interactivelt seelcted atoms/residues/ranges/ligands
'''

class Superpose(Application):
  insts = None
  initialisation = {
    'method': 'ssm',
    'fixed' : '',
    'show_equiv_mol' : '',
    'show_equivalents': 0,
    'show_distances' : 1,
    'show_mask' : 0,
    'close_atoms_central_atom' :  ' CA ',
    'close_atoms_res_cutoff' :  2.0,
    'close_atoms_cutoff' :  1.0,
    'close_atoms_initial_global' : 0,
    'monomer_keep_matches' : 0,
    'selected_atoms_mode' : 'catrace',
    'selected_atoms_selection' : ['CA','CB'],
    'apply_interactive_all' : 0,
    'conn_colour' : 'black',
    'fixed_mask_colour' : 'yellow',
    'moving_mask_colour' : 'magenta' }
  
  #selected_atoms_mode_menu = ['CA','main chain',"nucleic P,C1'",'user defined','all equivalent']
  #selected_atoms_mode_alias = ['catrace','main','nucleic','user','all']
  selected_atoms_mode_menu = ['CA','main chain',"nucleic P,C1'",'all equivalent','user defined']
  selected_atoms_mode_alias = ['catrace','main','nucleic','all','user']

  
#-------------------------------------------------------------------------
  def  __init__(self,params={},**keywords):
#-------------------------------------------------------------------------
    Superpose.insts = self
    self.supModel = {}
    
    for key,value in Superpose.initialisation.items():
      setattr(self,key,value)
    Application.__init__(self,'Superpose')
    if not ['ssm','close','interactive'].count(self.method):
      self.setparams({'method':'ssm'})

    self.setparams({'submethod':'ssm'})

    self.gesamt_out = ""
    self.initiallise_status()

#------------------------------------------------------------------------
  def initiallise_status(self):
#------------------------------------------------------------------------
    self.set_default_fixed()

    # Are current supModel still loaded?
    for model,supModel in self.supModel.items():
      molobj = data(model)
      if not molobj:
        supModel.delete()
        del self.supModel[model]
        
    for molobj in get_dataobj(object_type='MolData'):
      self.add_MolData(molobj.name)
      
    self.set_show_equiv_mol()
 
#------------------------------------------------------------------------
  def add_MolData(self,mol):
#------------------------------------------------------------------------
    import model
    molobj =data(mol)
    #print "Superpose.add_MolData molobj",mol,molobj
    if not molobj or molobj.object_type != 'MolData': return 1
    
    if not self.supModel.has_key(mol):
      self.supModel[mol] = SuperposeModel(self,mol,self.fixed,1)
    self.add_dependency(molobj,['delete'],[self.handle_delete_MolData])
    self.add_dependency(molobj,['rename'],[self.handle_rename_MolData])
    self.add_dependency(molobj,['transform'],[self.handle_transform_MolData])
    return 0


  def GetSuperposeModel(self,name):
    return self.supModel.get(name,None)
  
  def GetSuperposeModelMatch(self,name,imatch):
    sm = self.supModel.get(name,None)
    if sm:
      if imatch>=0 and imatch<len(sm.matches):
        return sm.matches[imatch]

    return None
      

#-------------------------------------------------------------------------
  def close(self):
#-------------------------------------------------------------------------
    #print 'Superpose.close'
    for model,supModel in self.supModel.items():
      supModel.handle_close_application()    
    self.draw()    
    Application.close(self)

#-------------------------------------------------------------------------
  def unclose(self):
#-------------------------------------------------------------------------
    #print 'Superpose.unclose'
    self.set_show_equivalents(self.show_equivalents)
    self.set_show_mask(self.show_mask)
    for model,supModel in self.supModel.items():
      supModel.handle_unclose_application()    

    self.draw()

#----------------------------------------------------------------------------
  def Exit(self):
#----------------------------------------------------------------------------    
    for model,supModel in self.supModel.items():
      supModel.save_matches()
      
#----------------------------------------------------------------------------
  def delete(self):
#----------------------------------------------------------------------------
    for model,supModel in self.supModel.items():
      supModel.delete()    
    Superpose.insts = []
    Application.delete(self)

    self.delete_graphobj()

#-----------------------------------------------------------------------
  def getparams(self):
#-----------------------------------------------------------------------
    import types
    pars = {}
    for key in Superpose.initialisation.keys():
      pars[key] = getattr(self,key)

    pars['superposeModels'] = {}
    for model,supModel in self.supModel.items():
       pars['superposeModels'][model] = supModel.getparams()

    #print 'Superpose.getparams',pars
    return pars

  
#-------------------------------------------------------------------------
  def set_default_fixed(self):
#-------------------------------------------------------------------------
    objlist = get_dataobj(object_type='MolData')
    namelist = []
    for obj in objlist: namelist.append(obj.name)

    if namelist.count(self.fixed):
      namelist.remove(self.fixed)
    else:
      self.fixed = ''
    if namelist.count(self.show_equiv_mol):
      namelist.remove(self.show_equiv_mol)
    else:
      self.show_equiv_mol = ''
      
    if not self.fixed and len(namelist)>0:
      self.fixed = namelist[0]
      namelist.remove(self.fixed)
    if not self.show_equiv_mol and len(namelist)>0: self.show_equiv_mol = namelist[0]
    #print 'Superpose.set_default_fixed',self.fixed,self.show_equiv_mol

#-------------------------------------------------------------------------
  def set_fixed(self,fixed):
#-------------------------------------------------------------------------
    # Save the current matches to MODELINFO 
    for model,supModel in self.supModel.items():
      if model != self.fixed:
        supModel.save_matches()
        
    if fixed and fixed != self.fixed:
      self.fixed = fixed
      self.set_default_fixed()
      for model,supModel in self.supModel.items():
        supModel.set_fixed(self.fixed)
    self.do_redraw = 1
    
#-------------------------------------------------------------------------
  def set_show_equiv_mol(self,show_equiv_mol=''):
#-------------------------------------------------------------------------
    if show_equiv_mol:
      if show_equiv_mol == self.fixed: return 1
      self.show_equiv_mol = show_equiv_mol

    for model,supModel in self.supModel.items():
      supModel.set_show_connectivity(int(self.show_equivalents and (model == self.show_equiv_mol)))
      supModel.set_show_mask(int(self.show_mask and [self.fixed,self.show_equiv_mol].count(model)))
      supModel.do_redraw = 1

#-------------------------------------------------------------------------
  def set_show_equivalents(self,show_equivalents):
#-------------------------------------------------------------------------
    #if show_equivalents == self.show_equivalents: return
    self.show_equivalents = show_equivalents
    if self.show_equiv_mol:
      self.supModel[self.show_equiv_mol].set_show_connectivity(self.show_equivalents)
      self.supModel[self.show_equiv_mol].do_redraw =1

#-------------------------------------------------------------------------
  def set_show_mask(self,show_mask):
#-------------------------------------------------------------------------
    #if show_mask == self.show_mask: return
    self.show_mask = show_mask
    for model in (self.show_equiv_mol,self.fixed):
      if model: self.supModel[model].set_show_mask(show_mask)

#-------------------------------------------------------------------------
  def set_colour(self,mode='',colour=''):
#-------------------------------------------------------------------------
    setattr(self,mode,colour)
    #print 'Superpose.set_colour',mode,colour
    if self.show_equiv_mol:
      self.supModel[self.show_equiv_mol].do_redraw =1

#-------------------------------------------------------------------------
  def setparams(self,params={},**kw):
#-------------------------------------------------------------------------
    params.update(kw)
    print "Superpose.setparams",params

    for key,value in params.items():
      
      if  ['fixed','show_equiv_mol'].count(key):
        if data(value): setattr(self,key,value)

    for key,value in params.items():
      if key == 'superposeModels':
        for model,pars in params['superposeModels'].items():
          # Test that the MolData object is loaded
          #print 'Superpose.setparams',model,pars
          molobj = data(model)
          if molobj:
            if not self.supModel.has_key(model):
              self.supModel[model] = SuperposeModel(self,model,self.fixed,1)
            self.supModel[model].setparams(pars)
            #if self.method == 'interactive': self.supModel[model].update_Connectivity2()

      elif key == 'method' and value != self.method:
        if self.method == 'interactive':
          for model,supModel in self.supModel.items():
            supModel.clear_Connectivity2()
        self.method = value
        if self.method == 'interactive':
          for model,supModel in self.supModel.items():
            supModel.update_Connectivity2()
      elif key == 'selected_atoms_selection':
        # make sure selected_atoms_selection is a list - confusion from pre 1.113
        # and handle the text value sent by the gui
        import types
        if isinstance(value,types.StringType):
          self.set_user_selected_atoms(value)
        else:
          setattr(self,key,value)      
      elif key == 'close_atoms_central_atom':
        # Needs to have correct format for mmdb atom name
        atnam = value.strip()[0:3]
        if len(atnam)==3:
          atnam = ' '+atnam 
        elif len(atnam)==2:
          atnam = ' '+atnam + ' '
        elif len(atnam)==1:
          atnam = ' '+atnam + '  '
        elif len(atnam)==0:
          atnam = ' CA '    
      else:
        print "set",key,value
        setattr(self,key,value)
         
      if ['selected_atoms_selection','selected_atoms_mode'].count(key) and self.method == 'interactive':
        for model,supModel in self.supModel.items():
          #supModel.update_Connectivity2()
          supModel.do_redraw = 1
          
#-------------------------------------------------------------------------
  def handle_rename_MolData(self,master='',**keywords):
#-------------------------------------------------------------------------
    if not self.supModel.has_key(master): return
    pass
  
#-------------------------------------------------------------------------
  def handle_delete_MolData(self,master='',**keywords):
#-------------------------------------------------------------------------

    # Delete data structure for this mol
    if  self.supModel.has_key(master):
      self.supModel[master].delete()
      del self.supModel[master]    

    if [self.fixed,self.show_equiv_mol].count(master):
      self.set_default_fixed()
      

#--------------------------------------------------------------------------
  def handle_transform_MolData(self,master='',mol1='',mol2='',hide=0,**keywords):
#--------------------------------------------------------------------------
    #print "in handle_transform_MolData",mol1,mol2
    if self.supModel.has_key(self.show_equiv_mol):
      self.supModel[self.show_equiv_mol].do_redraw = 1
      

#-------------------------------------------------------------------------
  def  handle_saved_files(self,saved_list=[]):
#-------------------------------------------------------------------------
    #print "Superpose saved_list",saved_list,self.matches,self.MGAlign
    pass

#-------------------------------------------------------------------------
  def gesamt_multi(self):
#-------------------------------------------------------------------------
    import pygl_coord
    rv = 0
    output_text = "Done nothing!"
    if len(self.supModel) == 0:
      return [rv,output_text]

    models = []
    selections = []
    files = []
    MolData_names = []

    for model,supModel in self.supModel.items()[0:1]:
      if not supModel.on:
        continue
      mol0 = data(supModel.fixed_MolData_name)
      models.append(mol0)
      mask_selHnd0 = supModel.parent.supModel[supModel.fixed_MolData_name].get_selHnd()
      MolData_names.append(supModel.fixed_MolData_name)
      gesamt_d0 = mol0.molHnd.SelectionToSCOP(mask_selHnd0)
      if len(gesamt_d0) == 0:
        gesamt_d0 = '*'
      selections.append(gesamt_d0)
      f = mol0.filename[2]
      files.append(f)

    for model,supModel in self.supModel.items()[:]:
      if not supModel.on:
        continue
      mol = data(supModel.MolData_name)
      f = mol.filename[2]
      if f in files:
          continue
      files.append(f)
      models.append(mol)
      mask_selHnd = supModel.parent.supModel[supModel.MolData_name].get_selHnd()
      MolData_names.append(supModel.MolData_name)
      gesamt_d = mol.molHnd.SelectionToSCOP(mask_selHnd)
      if len(gesamt_d) == 0:
        gesamt_d = '*'
      selections.append(gesamt_d)

    """
    print models
    print selections
    print files
    """

    command = ["gesamt"]
    for i in range(len(files)):
      command.append(files[i])
      command.append("-d")
      command.append(selections[i])

    #print command
    env = copy.deepcopy(os.environ)
    gesamt_txt_output = tempfile.NamedTemporaryFile(suffix="_gesamt_out.txt",prefix="ccp4mg"+str(os.getpid()),delete=False)
    proc = subprocess.Popen(command,stdout=gesamt_txt_output,universal_newlines=True,stderr=subprocess.STDOUT,env=env,shell=False)
    self.retval = None
    import time
    t1 = time.time()
    while self.retval == None:
      self.retval = proc.poll()
    outname = gesamt_txt_output.name
    gesamt_txt_output.close()
    fo = open(outname)
    progout = fo.readlines()
    fo.close()
    print "time for gesamt",time.time()-t1

    #print progout

    mats = []
    rmsds = []
    for isup in range(len(self.supModel)):
        rmsds.append("99.999")

    self.gesamt_out = ""
    nalign = 0

    if len(files)==2:
      # Gesamt pairwise results
      for il,l in zip(range(len(progout)),progout[:]):
         if l.startswith(" Transformation matrix for Target:"):
            mat = []
            mat.extend([float(x) for x in progout[il+3].split()])
            mat.extend([float(x) for x in progout[il+4].split()])
            mat.extend([float(x) for x in progout[il+5].split()])
            mat.extend([0.0,0.0,0.0,1.0])
            mats.append(mat)
            print mat
         if l.startswith(" Aligned residues :"):
            nalign = int(l[len(" Aligned residues :"):].strip())
         if l.startswith(" RMSD             :"):
                rmsd = float(l[len(" RMSD             :"):].strip())

      for model,supModel in self.supModel.items():
        if supModel.on:
          supModel.last_rms = '%.2f/%i'%(rmsd,nalign)

    else:
      # Gesamt multi results
      for il,l in zip(range(len(progout)),progout[:]):
        #print l.rstrip("\n")
        self.gesamt_out += l
        if l.startswith("      Nalign:  "):
            nalign = int(l.strip().split()[1])
        if l.startswith("   (o) For structure S"):
            mat = []
            mat.extend([float(x) for x in progout[il+3].split()])
            mat.extend([float(x) for x in progout[il+4].split()])
            mat.extend([float(x) for x in progout[il+5].split()])
            mat.extend([0.0,0.0,0.0,1.0])
            mats.append(mat)
        if l.startswith("   (o) pairwise r.m.s.d. (consensus r.m.s.d. on diagonal):"):
           isup = 0
           for model,supModel in self.supModel.items()[:]:
               rmsds[isup] = progout[il+4+isup].split()[isup+1]
               isup += 1

      for model,supModel in self.supModel.items()[:]:
        #supModel.last_rms = rmsds[0]
        mol = supModel.MolData_name
        for imat in range(len(MolData_names)):
            if mol == MolData_names[imat] and imat < len(rmsds):
                supModel.last_rms = '%.2f/%i'%(float(rmsds[imat]),nalign)
                break

    if len(files)==2:
        mat = mats[0]
        mat = pygl_coord.matrix(4,4,mat)
        models[1].molHnd.SetTransform(mat,True)
        models[1].update_dependents('transform')
        self.emitUpdateSignal(MolData_names[1])
    else:
      for imat in range(len(mats)):
        mat = mats[imat]
        mat = pygl_coord.matrix(4,4,mat)
        """
        print files[imat]
        print models[imat]
        mat.Print()
        print
        """
        models[imat].molHnd.SetTransform(mat,True)
        models[imat].update_dependents('transform')
        self.emitUpdateSignal(MolData_names[imat])

    self.do_redraw = 1

    #print "Written",self.gesamt_output_file
    #print "Moving",self.MolData_name

    return [rv,output_text]

#-------------------------------------------------------------------------
  def apply(self,**kw):
#-------------------------------------------------------------------------
    if not self.fixed:
        MGGUI().WarningMessage("Need to select a fixed model")
        return 1
    self.gesamt_out = ""

    nMoved = 0
    output_text = ''
    rv = 0
    if self.method == 'ssm':
      if self.submethod == "gesamt":
        self.gesamt_multi()
      else:
        for model,supModel in self.supModel.items():
          if model != self.fixed and supModel.on:
            if self.submethod == "gesamt":
              supModel.superpose_Gesamt()
              rc = [0,'OK'] # FIXME Need to get a return code in here somehow.
            else:
              rc = supModel.superpose_SSM()
            if rc[0] == 0:
              nMoved = nMoved + 1
            else:
              rv = rv = 1
              output_text = output_text + rc[1] + '\n'

    elif self.method == 'close':
      for model,supModel in self.supModel.items():
        if model != self.fixed and supModel.on:
          rc = supModel.superpose_Close()
          if rc[0] == 0:
            nMoved = nMoved + 1
          else:
            rv = rv + 1
            output_text = output_text + rc[1] + '\n'
          
    elif self.method == 'interactive':
      movingSupModel = self.supModel.get(self.show_equiv_mol,None)
      if not movingSupModel: return
      if self.apply_interactive_all:
        matches = movingSupModel.get_matches()
        for model,supModel in self.supModel.items():
          if model != self.fixed and supModel.on:
            if model != self.show_equiv_mol: supModel.set_matches(matches)
            rc = supModel.superpose_Matched()
            if rc[0] == 0:
              nMoved = nMoved + 1
            else:
              rv = rv + 1
              output_text = output_text + rc[1] + '\n'
      else:      
        rc = movingSupModel.superpose_Matched()
        if rc[0] == 0:
          nMoved = nMoved + 1
        else:
          rv = rv + 1
          output_text = output_text + rc[1] + '\n'

    return [rv,output_text]
  
#-------------------------------------------------------------------------
  def undo(self,mol=''):
#-------------------------------------------------------------------------
    self.gesamt_matches = ""
    # Undo any pre-existing transformation 
    if mol:
      mollist = [mol]
    else:
      mollist = self.supModel.keys()     
    for mol in mollist:
      self.supModel[mol].superpose_undo()

#-------------------------------------------------------------------------
  def handle_change_match(self,mol='',value=''):
#-------------------------------------------------------------------------
    # has user changed which match they want displayed?
    import model

    #print "handle_change_match",mol,value
    
    if not self.supModel.has_key(mol): return
    if value == 'undo':
      self.supModel[mol].superpose_undo()
    else:
      if value == 'best': value = self.supModel[mol].best_match  
      self.supModel[mol].superpose_SSM(match=int(value))

#------------------------------------------------------------------------
  def getRMS(self,mol):
#------------------------------------------------------------------------
    if not self.supModel.has_key(mol): return ''
    return self.supModel[mol].getRMS()
  
    
#-------------------------------------------------------------------------
  def get_data(self,**keywords):
#-------------------------------------------------------------------------
    results = []
    if ['ssm','close'].count(self.method):
      for model,supModel in self.supModel.items():
        if model != self.fixed and supModel.on and data(model):
          fixed_MolData = data(supModel.fixed_MolData_name)
          moving_MolData = data(supModel.MolData_name)
          thisResult = {'fixed':supModel.fixed_MolData_name,'moving':supModel.MolData_name,'matrix':moving_MolData.molHnd.GetTransformString()}
          if fixed_MolData.molHnd.GetIsTransformed():
            thisResult['fixMatrix'] = fixed_MolData.molHnd.GetTransformString()
          else:
            thisResult['fixMatrix'] = None
          results.append(thisResult)
      return results
      
    elif self.method == 'interactive':
      for model,supModel in self.supModel.items():
        if model != self.fixed and supModel.on and data(model):
          fixed_MolData = data(supModel.fixed_MolData_name)
          moving_MolData = data(supModel.MolData_name)
          thisResult = {'fixed':supModel.fixed_MolData_name,'moving':supModel.MolData_name,'matrix':moving_MolData.molHnd.GetTransformString()}
          mat = thisResult['matrix'].split()
          matvals = []
          for val in mat:
            matvals.append(float(val))
          import pygl_coord
          m =  pygl_coord.matrix(4,4,matvals)
          tm =  pygl_coord.matrix(4,4,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])
          if (m*tm - tm).isNull():
            continue
          if fixed_MolData.molHnd.GetIsTransformed():
            thisResult['fixMatrix'] = fixed_MolData.molHnd.GetTransformString()
          else:
            thisResult['fixMatrix'] = None
          results.append(thisResult)
      return results

#-------------------------------------------------------------------------
  def list_data(self,**keywords):
#-------------------------------------------------------------------------

    # Recreate list_data with current status
    text = ''

    if ['ssm','close'].count(self.method):
      for model,supModel in self.supModel.items():
        if model != self.fixed and supModel.on and data(model):
          text = text + supModel.Print()
      
    elif self.method == 'interactive':
      supModel_list = []
      if self.apply_interactive_all:
        for model,supModel in  self.supModel.items():
          if model != self.fixed and supModel.on and data(model):
            supModel_list.append(supModel)    
      elif self.show_equiv_mol:
        supModel_list.append(self.supModel.get(self.show_equiv_mol))
        
      for supModel in supModel_list:
        supModel.conn2.UpdateCoordinates(True)
        #print 'list_data',supModel.MolData_name,supModel.conn2.GetNofConnections()
        spaces='                       '
        fxd = data(self.fixed).name_label[0:20]
        mov = data(self.show_equiv_mol).name_label[0:20]
        text = text + 'Fixed model          Moving model         Distance\n' + fxd +  spaces[0:21-len(fxd)] +mov + spaces[0:21-len(mov)] + supModel.getRMS()+ '\n\n'
        text =  text + supModel.conn2.Print()
        # Append transform matrix
        text =  text +"\n\nTransformation applied to the moving model\n"+ data(supModel.MolData_name).molHnd.GetTransformString()
        if data(self.fixed).molHnd.GetIsTransformed():
          text = text + "NOTE: The fixed model is also transformed by\n"+data(self.fixed).molHnd.GetTransformString()
          
    if self.gesamt_out:
        text = text + "\n\n" + self.gesamt_out
    return text
  


#------------------------------------------------------------------------
  def get_unmatched_menu(self,molobj):
#------------------------------------------------------------------------

    MP = self.supModel.get(self.show_equiv_mol)
    if not MP:
      return []
    else:
      return MP.get_unmatched_menu(molobj=molobj)
    
#------------------------------------------------------------------------
  def get_unfinished_range(self,molobj):
#------------------------------------------------------------------------

    MP = self.supModel.get(self.show_equiv_mol)
    if not MP:
      return []
    else:
      return MP.get_unfinished_range(molobj=molobj)


#----------------------------------------------------------------------
  def get_user_selected_atoms(self):
#----------------------------------------------------------------------
    # Return selected_atoms_selectino as a text string
    if not self.selected_atoms_selection: return ''
    txt = self.selected_atoms_selection[0]
    for item in self.selected_atoms_selection[1:]:
      txt = txt + ','+item
    #print 'get_user_selected_atoms',txt
    return txt

#----------------------------------------------------------------------
  def set_user_selected_atoms(self,selection_text=''):
#----------------------------------------------------------------------
    sele_list0 = selection_text.split(',')
    sele_list = []
    for item in sele_list0:
      sele_list.extend(item.split())
    self.selected_atoms_selection = sele_list
    #print 'set__user_selected_atoms',self.selected_atoms_selection
    

#----------------------------------------------------------------------
  def handle_atom_popup(self,select='',match=-1,end_range='',mode='',
                        undo='',clear_unmatched =0,clear_all=0, undo_last='',
                        MolData_name='',atomID='',**keywords):
#----------------------------------------------------------------------
    imatch = -1
    
    if MolData_name:
      molobj = data(MolData_name)
    else:
      molobj = None
    if molobj and atomID:
      atomptr = molobj.interpretAtomID(mol=molobj,atom_name=atomID,force_one_atom=1)
    else:
      atomptr = None
    #print 'handle_atom_popup',select,'match',match,MolData_name,atomID,molobj,atomptr
    
    MP=self.supModel.get(self.show_equiv_mol)
    if not MP: return
    update_conn = 0

    if end_range:
      imatch = end_range
      if imatch>=0:
        MP.end_range(match=imatch,atomptr=atomptr,molobj=molobj)
      
    elif select:
      imatch = MP.new_match(molobj=molobj,atomptr=atomptr,mode=select)
      #print 'handle_atom_popup add_selection rv',rv
      if imatch>=0: update_conn = 1
       
    if match>=0:
      #print "match",match
      imatch = MP.add_match(molobj=molobj,atomptr=atomptr,match=match,mode=mode)
      if imatch>=0: update_conn = 1
      
    
    if undo:
      rv = MP.undo(item=undo,molobj=molobj,atomptr=atomptr)
      if not rv: update_conn = 1

    if clear_unmatched:
      if hasattr(self,'select_range'): del self.select_range
      MP.clear_unmatched()
      self.do_redraw = 1
    
    if clear_all:
      if hasattr(self,'select_range'): del self.select_range
      MP.clear_all()
      update_conn = 1

    if undo_last:
      #print "undo last"
      MP.stack(restore=1)
      update_conn = 1

    if update_conn:
      MP.update_Connectivity2()
    return imatch

    
#-----------------------------------------------------------------------
  def draw(self):
#-----------------------------------------------------------------------
    import cprim
    #print 'Superpose.draw'

    for model,supModel in self.supModel.items():
      supModel.draw()


#--------------------------------------------------------------------
  def import_matches(self,filename=''):
#--------------------------------------------------------------------
    import mmdb2 as mmdb
    if not os.path.exists(filename): return [1,'No vector file specified']
    CIF =  mmdb.Data()
    RC = CIF.ReadMMCIFData ( filename )
    if RC:
      err = GetErrorDescription(RC)
      print "Error reading vectors CIF",RC,err
      return [1,"Error reading file: "+err]

    # Read if any model files are required to be loaded   
    molobj_list = []
    RC_ptr = inta(1)
    Loop = CIF.GetLoop ( "_model" );
    if Loop:
      maxV =  Loop.GetLoopLength()
      row = 0
      while row < maxV:
        id = ''
        filn = ''
        id = Loop.GetString( "id",row,RC_ptr)
        filn =  Loop.GetString( "filename",row,RC_ptr)
        molobj = get_dataobj(object_type='MolData',name=id)
        if molobj:
          molobj_list.append(molobj[0])
        else:
          return [1,'Vectors in file for: '+id+ ' does not correspond to currently loaded model']
        row = row + 1
    del Loop


    '''
    Problem here is that the current fixed model might not correspond to one of
    the models in the file
    '''
    
    Loop = CIF.GetLoop ( "_vector" );
    if not Loop: return [1,'No vectors defined in file']
    maxV =  Loop.GetLoopLength()
    #print 'Reading',maxV,'vectors from file',filename   
    row = 0
    failed_interpretAtomID = []
    while row < maxV:
      start_atom = ''
      end_atom = ''
      # Get atoms - or if there are none the point or displacement
      start_atom = Loop.GetString( "start_atom",row,RC_ptr)
      if  not RC_ptr[0]:
        mol1 =  Loop.GetString( "start_model_id",row,RC_ptr)
        molobj1 = get_dataobj(object_type='MolData',name=mol1)
      end_atom = Loop.GetString( "end_atom",row,RC_ptr)
      if  not RC_ptr[0]:
        mol2 = Loop.GetString( "end_model_id",row,RC_ptr)
        molobj2 = get_dataobj(object_type='MolData',name=mol2)
      if molobj1 and molobj2:
        pAtom1 = molobj1[0].interpretAtomID(mol=molobj1[0],atom_name=start_atom)
        pAtom2 = molobj2[0].interpretAtomID(mol=molobj2[0],atom_name=end_atom)
        #print "pAtom1,pAtom2",start_atom,end_atom,pAtom1,pAtom2
        
        if self.method == 'interactive':
          MP=self.get_MolPair( mol1,mol2,create=1)
          if MP and pAtom1 and pAtom2:
            if  mol1==MP.mol1.name :
              MP.Connectivity2.AddConnection(pAtom1,pAtom2)
            else:
              MP.Connectivity2.AddConnection(pAtom2,pAtom1)
        else:
          failed_interpretAtomID.append([start_atom,end_atom])
      else:
        pass   
      row=row + 1
    return [0,'']


#-----------------------------------------------------------------------
  def emitUpdateSignal(self,MolData_name):
#-----------------------------------------------------------------------
    ''' The model MolData_name has been transformed and we need to ensure
    gui for that model is updated.  Superpose is not a QtObject so can not emit
    signal so call method in GMolData'''

    obj = DISPLAYTABLE().getDataObj(MolData_name)
    if not obj: return
    obj.updateGuiAfterTransformation()

#-----------------------------------------------------------------------
  def checkForMultiModels(self):
#-----------------------------------------------------------------------
    multiModels = []
    for model,supModel in self.supModel.items():
      if supModel.on and supModel.nofModelsSelected()>1:
        multiModels.append(model)
    # ..and make sure we have checked the fixed model
    if self.supModel.get(self.fixed,'') and not self.supModel[self.fixed].on and \
       self.supModel[self.fixed].nofModelsSelected()>1:
      multiModels.append(self.fixed)
    return multiModels
        
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
class SuperposeModel:
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
  def __init__(self,parent=None,MolData_name='',fixed_MolData_name='',on=1,params={},**kw):
    import mmut,VectorsDispobj,model
    self.parent=parent
    self.MolData_name = MolData_name
    self.fixed_MolData_name = fixed_MolData_name
    self.selection = 'all'
    self.on = on
    self.show_conn = 0
    self.show_mask = 0

    # Save and display matched atoms v. a fixed model
    self.conn2 = mmut.Connectivity2(mmut.CONN_ATOM_ATOM,0,1)
    self.graphobj =  VectorsDispobj.Vectorsgraphicsmodel()
    BUILD().append(self.graphobj)

    #'Temporary' display of unmatched atoms in interactive mode
    # or of mask in other modes
    self.MolDisp = None    
    self.do_redraw = 1

    params.update(kw)
    # Lists of manually selected matched atoms
    self.matches = []
    self.restore_matches()

    # Handling SSM
    self.MGAlign = None
    self.nof_matches = 0
    self.best_match = -1
    self.displayed_match = -1
    # note the match number returned by CMGAlign::GetMatchNumber is 0-n-1
    # but here we use 1-n
    self.last_rms = ''

    self.list_stack = []
    self.gesamt_matches = ""

    
#-----------------------------------------------------------------------
  def delete(self):
#-----------------------------------------------------------------------
    self.save_matches()
    del self.conn2
    if self.graphobj:
      MAINWINDOW().removeGLDisplayObject(self.graphobj)
      BUILD().deletemod(self.graphobj)
    if self.MolDisp: self.MolDisp.delete()
    self.MolDisp = None


#-----------------------------------------------------------------------
  def set_fixed(self,fixed):
#-----------------------------------------------------------------------
    if fixed == self.fixed_MolData_name: return
    #print 'set_fixed',self.MolData_name,self.fixed_MolData_name,'fixed:',fixed
    self.fixed_MolData_name = copy.deepcopy(fixed)
    self.conn2.Clear()
    self.clear_all()
    self.restore_matches()
    self.do_redraw = 1

    
#-----------------------------------------------------------------------
  def handle_close_application(self):
#-----------------------------------------------------------------------
    self.set_show_connectivity(0)
    self.set_show_mask(0)

#-----------------------------------------------------------------------
  def handle_unclose_application(self):
#-----------------------------------------------------------------------
    self.do_redraw = 1


#-----------------------------------------------------------------------
  def save_matches(self):
#-----------------------------------------------------------------------
    #print 'save_matches',self.MolData_name,self.get_matches(),self.get_matches(swap_selections=1)
    if self.fixed_MolData_name != self.MolData_name:
      MODELINFO().add_info(set=self.MolData_name,info='superpose_matches',
            key=self.fixed_MolData_name,value=self.get_matches(),save=1)
      MODELINFO().add_info(set=self.fixed_MolData_name,info='superpose_matches',
            key=self.MolData_name,value=self.get_matches(swap_selections=1),save=1)
   
#-----------------------------------------------------------------------
  def restore_matches(self):
#-----------------------------------------------------------------------
    if self.fixed_MolData_name != self.MolData_name:
      status,info = MODELINFO().get_info(set=self.MolData_name,info='superpose_matches',
            key=self.fixed_MolData_name)
      if not status and info.has_key(self.fixed_MolData_name):
        self.set_matches(info[self.fixed_MolData_name])
      status,info = MODELINFO().get_info(set=self.fixed_MolData_name,info='superpose_matches',
              key=self.MolData_name)
      if not status and info.has_key(self.MolData_name):
        self.set_matches(info[self.MolData_name],append=1,swap_selections=1)

#-----------------------------------------------------------------------
  def get_matches(self,swap_selections=0):
#-----------------------------------------------------------------------
    match_info = []
    for match in self.matches:
      match_info.append(match.getparams(swap_selections=swap_selections))
    #print 'get_matches',self.MolData_name,self.fixed_MolData_name,swap_selections,match_info
    return match_info
    
#----------------------------------------------------------------------
  def set_matches(self,match_info=[],append=0,swap_selections=0):
#-----------------------------------------------------------------------
    if not append: self.matches = []
    #print 'set_matches',self.MolData_name,self.fixed_MolData_name,match_info,append,swap_selections
      
    for info in match_info:
        new_match = SuperposeMatch()
        if swap_selections:
          sele1 = info.get('sele1','')
          info['sele1'] = info.get('sele2','')
          info['sele2'] = sele1
        new_match.setparams(info)
        same = 0
        if self.matches:
          for old_match in  self.matches:
            same = same + old_match.isSame(new_match)
        if not same :
          self.matches.append(new_match)
        #else:
        #  print 'set_matches deleting repeat',info

#-----------------------------------------------------------------------
  def getparams(self):
#-----------------------------------------------------------------------     
    pars= {
      'MolData_name' : self.MolData_name,
      'fixed_MolData_name' : self.fixed_MolData_name,
      'on' : self.on,
      'selection' : self.selection,
      'matches' : self. get_matches() }
    return pars

#-----------------------------------------------------------------------
  def setparams(self,params={},**kw):
#-----------------------------------------------------------------------
    #print 'SuperposeModel.setparams',self.MolData_name,params
    params.update(kw)
    if params.has_key('on'): self.on = params.get('on')
    if params.has_key('selection'):
      self.selection = params.get('selection')
      self.do_redraw = 1
    if params.has_key('matches'):
      self.set_matches(params['matches'])

#-----------------------------------------------------------------------
  def nofMatches(self):
#-----------------------------------------------------------------------
    return len(self.matches)

  def getRMS(self):
    return self.last_rms

#-----------------------------------------------------------------------
  def getmatchesinfo(self):
#-----------------------------------------------------------------------
    return { 'nof_matches' : getattr(self,'nof_matches'),
             'best_match' : getattr(self,'best_match'),
             'displayed_match' : getattr(self,'displayed_match'),
             'rms' :  getattr(self,'last_rms') }

#-----------------------------------------------------------------------
  def set_show_connectivity(self,show_conn):
#-----------------------------------------------------------------------
    if show_conn != self.show_conn: self.do_redraw = 1
    self.show_conn = show_conn
    self.do_redraw = 1
    
#-----------------------------------------------------------------------
  def set_show_mask(self,show_mask):
#-----------------------------------------------------------------------
    if show_mask != self.show_mask: self.do_redraw = 1
    self.show_mask = show_mask
    self.do_redraw = 1
    

#-----------------------------------------------------------------------
  def extract_unmatched(self,model=2):
#-----------------------------------------------------------------------
    '''
    Extract any selection on either this model (=2) or the fixed
    model (=1) that is not matched
    '''
    
    unmatched = []
    for match in self.matches:
      info = match.getUnmatched(model)
      #print 'extract_unmatched',match,info
      if info: unmatched.append(info)
    #print 'extract_unmatched',model,unmatched
    return unmatched
      

#-----------------------------------------------------------------------
  def get_unmatched_selection_command(self,unmatched_list):
#-----------------------------------------------------------------------
    '''
     Convert a list of unmatched groups into a parseable selection command
     This method is independent of the MolData object
    '''
    #print "draw_unmatched",self.parent.selected_atoms_mode
    selected_atoms_mode = self.parent.selected_atoms_mode
    selected_atoms_selection = self.parent.selected_atoms_selection
    import string
    sele = ''
    for unmatched in unmatched_list:
      if ['atom','residue','monomer'].count(unmatched[0]):
         sele = sele + unmatched[1] + ' or '
      elif ['main','side','base','backbone'].count(unmatched[0]):
        sele = sele + "{ "+unmatched[0]+" and "+unmatched[1] + ' } or '
      elif ['range'].count(unmatched[0]):
        if selected_atoms_mode == 'all' or (selected_atoms_mode=='user' and selected_atoms_selection==''):
          sele = sele + unmatched[1] + ' or '
        elif selected_atoms_mode == 'user':
          sele = sele + "{ /1/*/*,*/"
          for atm in selected_atoms_selection: sele = sele + atm +','
          sele = sele[0:-1] + " and "+unmatched[1] + ' } or '
        else:
          sele = sele + "{ "+selected_atoms_mode+" and "+unmatched[1] + ' } or '

    #print "draw_unmatched sele",sele
    if len(sele)>4 and sele[-3:] == 'or ': sele = sele[0:-3]
    return sele

#-----------------------------------------------------------------------
  def draw_unmatched(self,command,colour=''):
#-----------------------------------------------------------------------
      rv = data(self.MolData_name).parse_selection(command=command,test=1)
      if (not rv[0]) and rv[1]:
        #print 'draw_unmatched parse_selection',command,rv,self.MolDisp
        if not self.MolDisp: self.create_MolDisp()
        if not getattr(self.MolDisp,'SelHandle'): return
        self.MolDisp.SelHandle.setCommand(command)
        if colour: self.MolDisp.set_colour(colour)
        self.MolDisp.do_redraw = 1        
        self.MolDisp.set_visibility(visibility=1)
        self.MolDisp.reapply_selection = 1
      
      else:
        if self.MolDisp:
          self.MolDisp.set_visibility(visibility=0)

    

#-----------------------------------------------------------------------
  def draw_mask(self,colour=''):
#-----------------------------------------------------------------------
    #print 'superposeModel.draw_mask',self.MolData_name,colour
    
    if ['','all'].count(self.selection.strip()):
      if self.MolDisp:
        self.MolDisp.set_visibility(visibility=0)
    else:
      if not self.MolDisp: self.create_MolDisp()
      self.MolDisp.SelHandle.setCommand(self.selection)
      if colour: self.MolDisp.set_colour(colour)
      self.MolDisp.do_redraw = 1        
      self.MolDisp.set_visibility(visibility=1)
      self.MolDisp.reapply_selection = 1

#-----------------------------------------------------------------------
  def draw(self,force=0):
#-----------------------------------------------------------------------

    #print "SuperposeModel do_redraw",self.MolData_name,self.do_redraw,self.show_mask
    if not self.do_redraw and not force: return
    self.draw_matched()
    fixed = self.parent.supModel.get(self.fixed_MolData_name,None)
    if self.show_mask:
      #if not self.MolDisp: self.create_MolDisp()
      #self.MolDisp.set_visibility(visibility=1)
      if self.parent.method == 'interactive':
        # Draw pyramids on the selected but unmatched groups
        unmatched_list = self.extract_unmatched(2)
        #print 'draw unmatched_list',unmatched_list
        command = self.get_unmatched_selection_command(unmatched_list)
        #print 'draw command',command,self.parent.moving_mask_colour
        self.draw_unmatched(command,colour=self.parent.moving_mask_colour)
        # Draw the unmatched on the 'fixed' model
        if fixed:
          unmatched_list = self.extract_unmatched(1)
          #print 'draw fixed unmatched_list',unmatched_list
          command = self.get_unmatched_selection_command(unmatched_list)
          #print 'draw fixed command',command,self.parent.fixed_mask_colour
          if fixed: fixed.draw_unmatched(command,colour=self.parent.fixed_mask_colour)
          
      else:
        self.draw_mask(colour=self.parent.moving_mask_colour)
        if fixed: fixed.draw_mask(colour=self.parent.fixed_mask_colour)
          
    elif self.MolDisp:
      self.MolDisp.set_visibility(visibility=0)
      self.MolDisp.draw()
      if fixed and fixed.MolDisp:
       fixed.MolDisp.set_visibility(visibility=0)
       fixed.MolDisp.draw()
       
    self.do_redraw = 0

#-----------------------------------------------------------------------
  def draw_matched(self):
#----------------------------------------------------------------------
    nconn=self.conn2.GetNofConnections()
    #print "SuperposeModel.draw_matched nconn",nconn,self.show_conn
    self.graphobj.obj.clear_prims();
    self.graphobj.obj.DeleteText();

    if nconn>0 and self.show_conn:
      self.conn2.UpdateCoordinates(True)
   
      import VectorsDispobj, pygl_coord, cprim
      col = MGCOLOUR().RGB(self.parent.conn_colour)
      colv = pygl_coord.DoubleVector(col)
      #print "SuperposeModel col,colv",col,colv
      if self.parent.show_distances:
        label_mode = cprim.LABELLEDCENTRE
      else:
        label_mode = cprim.NOTLABELLED
      cprim.DrawSimpleConnection(self.graphobj.obj,self.conn2.connected,colv,
        cprim.DASHLINE,VectorsDispobj.VectorsDispobj.line_width,label_mode,self.parent.conn_colour)

      
      font = FONTMANAGER().getFont('atom_label')
      self.graphobj.obj.SetTextFont( str(font['family']), str(font['weight']),
                                     str(font['slant']),font['size'],font['underline'])
      
      
    self.graphobj.rebuild()
    MAINWINDOW().rebuildGLDisplayObject(self.graphobj)

  def get_match_menu(self,iMatch=-1):
     menu = []
     if iMatch>=0 and iMatch<len(self.matches):
       rng = [iMatch]
     else:                            
       rng = range(len(self.matches))
     for ii in rng:
       menu.append([self.matches[ii].on,'%-8s %-12s %-12s'%(self.matches[ii].mode,self.matches[ii].sele2,self.matches[ii].sele1)])
     return menu
     
    

#-----------------------------------------------------------------------
  def get_unmatched_menu(self,molobj=''):
#-----------------------------------------------------------------------
    '''
    Return a menu of the unmatched items in the match list
    '''
    menu = []
    # If user has picked the fixed model then we want a list of the unmatched
    # items in the moving model (and visa versa)
    if molobj.name == self.MolData_name:
      model = 1
    else:
      model = 2
    for ii in range(len(self.matches)):
      menu_item_list = self.matches[ii].getUnmatchedMenu(model=model)
      #print 'get_unmatched_menu menu_item_list',ii,menu_item_list
      for menu_item in menu_item_list: menu.append( [ii,menu_item[0],menu_item[1]] )
    return menu

#-----------------------------------------------------------------------
  def get_unfinished_range(self,molobj=''):
#-----------------------------------------------------------------------
    '''
    Return a menu of the unfinished ranges in the match list
    '''
    menu = []
    if molobj.name == self.MolData_name:
      model = 2
    else:
      model = 1
    for ii in range(len(self.matches)):
      menu_item_list = self.matches[ii].getUnmatchedMenu(model=model,start=1)
      for menu_item in menu_item_list: menu.append( [ii,menu_item] )
    return menu




#-----------------------------------------------------------------------
  def set_close(self):
#-----------------------------------------------------------------------
    for ii in range(0,len(self.list2)):
      if self.list2[ii]=='' and ['rang','residue','main','side'].count(self.list1[ii][0:4]):
        self.list2[ii]='close'
                                                                    
    for ii in range(0,len(self.list1)):
      if self.list1[ii]=='' and ['rang','residue','main','side'].count(self.list2[ii][0:4]):
        self.list1[ii]='close'

#-----------------------------------------------------------------------
  def clear_unmatched(self):
#-----------------------------------------------------------------------
    matches = []
    for match in self.matches:
      if match.get('sele1') and match.get('sele2'):
        matches.append(match)
    self.matches = matches
    self.do_redraw = 1


#-----------------------------------------------------------------------
  def clear_all(self):
#-----------------------------------------------------------------------
    self.matches = []
    self.do_redraw = 1
    #print 'MP.clear_all'

#-----------------------------------------------------------------------
  def delete_match(self,match):
#-----------------------------------------------------------------------
    if match>=0 and match<len(self.matches):
      del self.matches[match]

#-----------------------------------------------------------------------
  def new_match(self,molobj=None,atomptr=None,mode='atom',match_closest=0):
#-----------------------------------------------------------------------
    if molobj and atomptr:
      label=self.label(mode=mode,molobj=molobj,atomptr=atomptr)
      if not label: return -1
      self.stack()
      if ['range','start'].count(mode):
        mode = 'range'
        qualifier = 'first'
      else:
        qualifier = ''
      if molobj.name == self.fixed_MolData_name:
        self.matches.append(SuperposeMatch( mode = mode, sele1 = label,qualifier=qualifier ))
      else:
        self.matches.append(SuperposeMatch( mode = mode, sele2 = label,qualifier=qualifier ))
    else:
      self.matches.append(SuperposeMatch())
    #print 'SuperposeMatch.new_match',len(self.matches)-1,self.matches[-1].getparams()
    return len(self.matches)-1
    
#-----------------------------------------------------------------------
  def end_range(self,molobj=None,atomptr=None,match=-1):
#-----------------------------------------------------------------------
    if match<0 or match>len(self.matches):
      #print 'End range: invalid match number',match
      return -1
    if molobj.name == self.fixed_MolData_name:
      start_res = self.matches[match].get('sele1')
      model = 1
    else:
      start_res = self.matches[match].get('sele2')
      model = 2
      
    label=self.rangelabel(start_res=start_res,molobj=molobj,atomptr=atomptr)
    #print 'end_range rangelabel',start_res,label
    if not label: return -1
    
    self.stack()
    if model == 1:
      self.matches[match].set('sele1',label)
      self.matches[match].set('qualifier','')
    else:
      self.matches[match].set('sele2',label)
      self.matches[match].set('qualifier','')
    #print 'end_range',match,self.matches[match].getparams()
    return match
  
#-----------------------------------------------------------------------
  def add_match(self,molobj=None,atomptr=None,match=-1,mode=''):
#-----------------------------------------------------------------------
    #print "add_match match",match,mode
    import string
    '''
    if match[0:4]=='rang':
      label = self.label(mode='start',molobj=molobj,atomptr=atomptr)
      if string.find(match,'start')>0:
        sele='start '+label
        match=match[0:-6]
      elif string.find(match,'end')>0:
        sele='end   '+label
        match=match[0:-4]
    else:
      label = self.label(mode=match,molobj=molobj,atomptr=atomptr)
      sele=string.split(match)[0]+' '+label
      print "add_match match,sele",molobj,match,'*',sele
    '''

    
    if match<0 or match>len(self.matches):
      #print 'Add match: invalid match number',match
      return -1
    if molobj.name == self.fixed_MolData_name:
      model = 1
    elif molobj.name == self.MolData_name:
      model = 2
    
    label = self.label(mode=mode,molobj=molobj,atomptr=atomptr)
    #print 'add_match',model,label
    if not label: return -1
    self.stack()

    if model == 1:
      self.matches[ match].set('sele1',label)
    else:
      self.matches[ match].set('sele2',label)
      
    if ['start','end'].count(mode):
      if self.matches[match].get('qualifier')!='first':
        rv = self.matches[match].setEndOfRange(mode,self.fixed_MolData_name,self.MolData_name)
        if rv[0]:
          #print rv[1]       
          self.matches[match].set('qualifier',mode)
        else:
          self.matches[match].set('qualifier','')        
    else:
      self.matches[match].set('qualifier','')

    #print 'add_match',match,self.matches[match].getparams()
    self.do_redraw = 1
    return match


#-----------------------------------------------------------------------
  def undo(self,item,molobj=None,atomptr=None):
#-----------------------------------------------------------------------
    if ['atom','residue'].count(item):
      pass
    else:
      import utils
      item=utils.safeInt(item,-1)
      if item>=0 and item<=len(self.list1):
        self.stack()
        self.list1.pop(item)
        self.list2.pop(item)
        self.do_redraw = 1
        return 0
      else:
        return 1
      
    
    #if mode == 'atom':
    #  self.conn2.RemoveConnection(atomptr ,0 )
    #elif mode == 'residue':
    #  import mmdb
    #  resAtoms = mmdb.newPPCAtom()
    #  p = intp()
    #  atomptr.GetResidue().GetAtomTable(resAtoms,p)
    #  for i in range(p.value()):
    #    self.conn2.RemoveConnection(CAtomPtr(getPCAtom(resAtoms,i)),0 )
    #elif mode == 'all':
    #  self.conn2.DeleteConnections()
 
#-----------------------------------------------------------------------
  def get_listitem(self,molobj=None,atomptr=None):
#-----------------------------------------------------------------------
    # try to find a list item which includes the atom atomptr
    if molobj.name==self.fixed_MolData_name:
      list = self.list1
    elif molobj.name==self.MolDaa_name:
      list = self.list2
    ii=-1
    
    label = self.label(mode='atom',molobj=molobj,atomptr=atomptr)
    if list.count('atom '+label):
      ii=list.index('atom '+label)
    else:
      label = self.label(mode='residue',molobj=molobj,atomptr=atomptr)
      for mode in ['residue','main','side','backbone','base']:
        if list.count(mode+' '+label): ii=list.index(mode+' '+label)
      if ii<0:
        label = self.label(mode='start',molobj=molobj,atomptr=atomptr)
        if list.count('start '+label): ii=list.index('start '+label)
        if ii<0:
          import mmdb2 as mmdb
          selHnd=molobj.molHnd.GetSelection()
          for jj in range(0,len(list)):
            if list[jj][0:4]=='rang':
              molobj.molHnd.Select(selHnd,mmdb.STYPE_ATOM,list[jj][6:],mmdb.SKEY_NEW)
              if atomptr.isInSelection(selHnd):
                ii=jj
                break
              
          molobj.molHnd.DeleteSelection(selHnd)
    if ii>=0:
      return list[ii]
    else:
      return ''

#-----------------------------------------------------------------------
  def clear_Connectivity2(self):
#-----------------------------------------------------------------------
    self.do_redraw = 1
    self.conn2.DeleteConnections([],True)

#-----------------------------------------------------------------------
  def update_Connectivity2(self,match=-1):
#-----------------------------------------------------------------------
    if not self.fixed_MolData_name: return
    import string,selection_protocols
    
    if match<0 or match>=(self.matches):
      mrng = range(0,len(self.matches))
      self.conn2.DeleteConnections([],True)
    else:
      mrng = [match]
      self.conn2.DeleteConnections(mrng,True)

    '''
    print '-------------------------------'
    print "update_Connectivity2",len(self.matches),mrng
    import inspect
    stack =  inspect.stack()
    for item in stack[1:]: print item
    print '-------------------------------'
    '''
    
    atnam_list = []
    centralAtom = 'CA'
    if self.parent.selected_atoms_mode == 'main':
      atnam_list = ['CA','N','O','C']
      centralAtom = 'CA'
    elif self.parent.selected_atoms_mode =='catrace':
      atnam_list = ['CA']
      centralAtom = 'CA'
    elif self.parent.selected_atoms_mode == 'nucleic':
      atnam_list = ['P',"C1'",'C1*']
      centralAtom = "C1',C1*"
    elif self.parent.selected_atoms_mode == 'user':
      atnam_list = self.parent.selected_atoms_selection
      centralAtom = atnam_list[0]
    #print "update_Connectivity2 atnam_list",atnam_list,centralAtom
    self.do_redraw = 1

    mol1 = data(self.fixed_MolData_name)
    mol2 = data(self.MolData_name)


    for ii in mrng:
      on = self.matches[ii].get('on')
      if on:
        sele1 = self.matches[ii].get('sele1')
        sele2 = self.matches[ii].get('sele2')
        mode = self.matches[ii].get('mode')
        close = self.matches[ii].get('close')
        #print 'update_Connectivity2',mode,sele1,sele2
        qualifier = self.matches[ii].get('qualifier')
      
        '''
        elif mode2 == 'close':
        pending_res = self.get_respointers(range=sele1,molobj=mol1)
        if len(pending_res)==1: pending_res.append(pending_res[0])
        #print "pending_res",pending_res
        if len(pending_res)==2:
          nc = self.conn2.AddCloseRangeConnections(1, \
           pending_res[0],pending_res[1],mol2.molHnd,
           self.parent.close_atoms_res_cutoff,self.parent.close_atoms_cutoff,atnam_list,centralAtom,ii)
          #print "close nc",nc
        elif mode1 == 'close':
        pending_res = self.get_respointers(range=sele2,molobj=mol2)
        if len(pending_res)==1: pending_res.append(pending_res[0])
        #print "pending_res",pending_res
        if len(pending_res)==2:
          nc = self.conn2.AddCloseRangeConnections(2, \
           pending_res[0],pending_res[1],mol1.molHnd,
           self.parent.close_atoms_res_cutoff,self.parent.close_atoms_cutoff,atnam_list,centralAtom,ii)
          #print "close nc",nc
        '''
        
        if mode == 'atom':
          if sele1 and sele2:
            pAtom1 = mol1.interpretAtomID(mol1,sele1,force_one_atom=1)
            pAtom2 = mol2.interpretAtomID(mol2,sele2,force_one_atom=1)
            if pAtom1 and pAtom2:
              self.conn2.AddConnection(pAtom1,pAtom2,'',ii)
        elif ['monomer'].count(mode):
          if sele1 and sele2 and sele1.split('/')[1] != '' and sele2.split('/')[1] != '':
            nr1,pRes1 = mol1.interpretResidueID(sele1)
            nr2,pRes2 = mol2.interpretResidueID(sele2)
            #print 'update_Connectivity2 monomer',sele1,sele2,nr1,nr2
            if nr1 and nr2 and pRes1 and pRes2:
              self.conn2.MatchGraphs(pRes1,"",pRes2,"",1,ii,0.9,bool(self.parent.monomer_keep_matches))

        elif ['residue','side','main','backbone','base'].count(mode):
          if ['backbone','base'].count(mode):
            main_chain = selection_protocols.backbone_atoms['ORDER']
          else:
            main_chain =selection_protocols.main_chain['ORDER']
          nr1,pRes1 = mol1.interpretResidueID(sele1)
          nr2,pRes2 = mol2.interpretResidueID(sele2)

          if (nr1==1 and nr2==1):
            import mmdb2 as mmdb
            sele = [[1,1],[1,0],[0,1],[1,0],[0,1]][['residue','side','main','base','backbone'].index(mode)]
            resAtoms1 = mmdb.newPPCAtom()
            p1 = intp()
            resAtoms2 = mmdb.newPPCAtom()
            p2 = intp()
            #pRes1.GetAtomTable(resAtoms1,p1)
            #pRes2.GetAtomTable(resAtoms2,p2)
            resAtoms1 = mmut.GetAtomTable(pRes1,p1)
            resAtoms2 = mmut.GetAtomTable(pRes2,p2)
            for i in range(p1.value()):
              atomptr1 = getPCAtom(resAtoms1,i)
              #print "atomptr1",atomptr1.name, main_chain.count(string.strip(atomptr1.name))
              if sele[main_chain.count(string.strip(atomptr1.name))]:
                for j in range(p2.value()):
                  atomptr2 = getPCAtom(resAtoms2,j)
                  if atomptr1.name == atomptr2.name:
                    self.conn2.AddConnection(atomptr1,atomptr2,'',ii)


        elif mode == 'range':
          pres1=self.matches[ii].getResPointers(sele1,self.fixed_MolData_name)
          pres2=self.matches[ii].getResPointers(sele2,self.MolData_name)
          if len(pres1)==2 and len(pres2)==2:
            nc = self.conn2.AddRangeConnections(pres1[0],pres1[1],pres2[0],pres2[1],atnam_list,ii)
          #print 'update_Connectivity2',self.list1,self.list2,self.conn2.GetNofConnections()
          elif close and len(pres1)==2 and len(pres2)==0:
            nc = self.conn2.AddCloseRangeConnections(1,pres1[0],pres1[1],data(self.MolData_name).molHnd,
             self.parent.close_atoms_res_cutoff,self.parent.close_atoms_cutoff,atnam_list,centralAtom,ii)
          elif close and len(pres1)==0 and len(pres2)==2:
            nc = self.conn2.AddCloseRangeConnections(2,pres2[0],pres2[1],data(self.fixed_MolData_name).molHnd,
             self.parent.close_atoms_res_cutoff,self.parent.close_atoms_cutoff,atnam_list,centralAtom,ii)

        elif mode == 'range_same_seqid':
          import mmdb2 as mmdb
          selHnd1 = mol1.molHnd.NewSelection()
          mol1.molHnd.Select ( selHnd1, mmdb.STYPE_RESIDUE, sele1, mmdb.SKEY_NEW)
          selHnd2 = mol2.molHnd.NewSelection()
          mol2.molHnd.Select ( selHnd2, mmdb.STYPE_RESIDUE, sele2, mmdb.SKEY_NEW)
          nc = self.conn2.AddRangeWithSameIdConnections( mol1.molHnd, selHnd1,
                                             mol2.molHnd,selHnd2,atnam_list , ii)   

        elif ['selection','selection_close'].count(mode):
          selHnd1 = -1
          selHnd2 = -1
          if sele1:
            rv1 = data(self.fixed_MolData_name).parse_selection(command=sele1)
            #print 'update_Connectivity2 selection rv1',rv1
            if not rv1[0]: selHnd1 = rv1[1]
          if sele2:
            rv2 = data(self.MolData_name).parse_selection(command=sele2)
            #print 'update_Connectivity2 selection rv2',rv2
            if not rv2[0]: selHnd2 = rv2[1]
          if mode == 'selection_close' and (selHnd1>0 or selHnd2>0):
            nClose=self.conn2.AddCloseAtoms(data(self.fixed_MolData_name).molHnd, selHnd1, data(self.MolData_name).molHnd, selHnd2, self.parent.close_atoms_res_cutoff, self.parent.close_atoms_cutoff , self.parent.close_atoms_central_atom, 0)
            #print 'update_Connectivity2 selection_close',sele1,sele2,selHnd1,selHnd2,nClose
          elif  mode == 'selection' and selHnd1>0 and selHnd2>0:
            self.conn2.AddContactFromSelHandle(data(self.fixed_MolData_name).molHnd,selHnd1,
                                            data(self.MolData_name).molHnd,  selHnd2)
          if selHnd1>0: data(self.fixed_MolData_name).molHnd.DeleteSelection(selHnd1)
          if selHnd2>0: data(self.MolData_name).molHnd.DeleteSelection(selHnd2)

        
  def label(self,mode='atom',molobj=None,atomptr=None):
    #  0  Molecule
    #  1  Model
    #  2  Chain Id
    #  3  Sequence number
    #  4  Insertion code 
    #  5  Residue name
    #  6  Atom id
    #  7  Alternate position
    #  8  Element

    mask = inta(9)
    if ['resi','side','main','mono','back','base'].count(mode[0:4]):
      py_mask = [0,1,1,1,1,1,0,0,0]
    elif ['star'].count(mode[0:4]):
      py_mask = [0,1,1,1,1,0,0,0,0]
    elif mode[0:4]=='rang':
      py_mask = [0,0,0,1,1,0,0,0,0]
    else:
      py_mask = [0,1,1,1,1,1,1,1,0]
    for i in range(0,9): mask[i]=py_mask[i]
    return molobj.molHnd.AtomLabel_mask(atomptr,mask)

  def rangelabel(self,start_res='',molobj=None,atomptr=None):
    reverse = 0
    pres1 = molobj.interpretResidueID(start_res)
    #print 'Superpose rangelabel',atomptr,pres1
    if pres1[0]==1 and pres1[1].GetResidueNo()< atomptr.GetResidueNo():
      label = start_res + '-' + \
             self.label(mode='range',molobj=molobj,atomptr=atomptr)
    else:
      import string
      label = self.label(mode='start',molobj=molobj,atomptr=atomptr) \
                + '-' +  string.split(start_res,'/')[-1]
      reverse=1
    return label


  def stack(self,restore=0):
    if not restore:
      match_params_list = []
      for match in self.matches:
        match_params_list.append(match.getparams())
      self.list_stack.append(match_params_list)
    else:
       if len(self.list_stack)>0:
        #Remove the current status
        match_params_list=self.list_stack.pop()
        self.matches = []
        for params in match_params_list:
          self.matches.append(SuperposeMatch(params=params))

  def superpose_undo(self):
    self.gesamt_matches = ""
    molobj = data(self.MolData_name)
    molobj.molHnd.UnSetTransform(True)
    molobj.update_dependents('transform')
    self.parent.emitUpdateSignal(self.MolData_name)
    self.best_match = 0
    self.last_rms = ''
    self.do_redraw = 1
    self.displayed_match = -1
    
  def parseGesamtOutput(self,lines):
    """
    Parse gesamt (output as list of lines) to get matrix, rmsd and matches.
    """

    rmsd = 0.0

    match_start = -1
    match_end = -1

    mat = []

    for il,l in zip(range(len(lines)),lines[:]):
      #if l.startswith("        Rx         Ry         Rz           T") and (il+6)<len(lines):
      if l.startswith("          Rx           Ry           Rz             T") and (il+6)<len(lines) and len(mat)==0:
        mat.extend([float(x) for x in lines[il+1].split()])
        mat.extend([float(x) for x in lines[il+2].split()])
        mat.extend([float(x) for x in lines[il+3].split()])
        mat.extend([0.0,0.0,0.0,1.0])
 
      if l.startswith(' at RMSD ='):
        rmsd = float(l[len(' at RMSD ='):].split()[0])

      if l.startswith('     r.m.s.d:'):
        rmsd = float(l[len('     r.m.s.d:'):].split()[0])

      if l.startswith(' RMSD'):
        rmsd = float(l.split(":")[1].strip())

      if l.startswith(' RESIDUE ALIGNMENT'):
        match_start = il+7
      if l.startswith(' Residue alignment:'):
        match_start = il+6
      if l.startswith('$TEXT:Residue alignment:'):
        match_start = il+5
      if (l.startswith('`-------------') or  l.startswith(' `-------------')) and match_start>-1:
        match_end = il

    matches = []
    if match_start >-1 and match_end > match_start:
      for il in range(match_start,match_end):
        q,d,t = lines[il].split("|")[1:4]
        if not len(d.strip())==0:
          i1 = ""
          i2 = ""
          r1a = q.strip()[-4:].strip()
          r2a = t.strip()[-4:].strip()
          if r1a[-1].isalpha():
            i1 = r1a[-1]
            r1a = r1a[:-1]
          if r2a[-1].isalpha():
            i2 = r2a[-1]
            r2a = r2a[:-1]
          r1,dist,r2 = int(r1a),float(d.strip()[3:-3]),int(r2a)
          c1 = q[3]
          c2 = t[3]
          rn1 = q[5:8]
          rn2 = t[5:8]
          matches.append((r1,r2,c1,c2,dist,rn1,rn2,i1,i2))

    return {'transform':mat,'rmsd':rmsd,'matches':matches }

  def runGesamt(self):
    import pygl_coord
    command = ["gesamt",self.gesamt_file1,'-d',self.gesamt_d1,self.gesamt_file2,'-d',self.gesamt_d2,'-o',self.gesamt_output_file]
    print command
    env = copy.deepcopy(os.environ)
    gesamt_txt_output = tempfile.NamedTemporaryFile(suffix="_gesamt_out.txt",prefix="ccp4mg"+str(os.getpid()),delete=False)
    proc = subprocess.Popen(command,stdout=gesamt_txt_output,universal_newlines=True,stderr=subprocess.STDOUT,env=env,shell=False)
    self.retval = None
    import time
    t1 = time.time()
    while self.retval == None:
      self.retval = proc.poll()
    outname = gesamt_txt_output.name
    gesamt_txt_output.close()
    fo = open(outname)
    progout = fo.readlines()
    fo.close()
    print "time for gesamt",time.time()-t1
    print "Written",self.gesamt_output_file
    print "Moving",self.MolData_name
    mol2 = data(self.MolData_name)
    mol1 = data(self.fixed_MolData_name)
    # These 4 lines are a way of checking that gesamt run Ok. Probably a better way.
    fcheck = open(self.gesamt_output_file)
    outlines = fcheck.readlines()
    fcheck.close()
    if len(outlines)>3:
      pgo = self.parseGesamtOutput(progout)
      mat = pygl_coord.matrix(4,4,pgo['transform'])
      mat = mat.Inverse()
      mol2.molHnd.SetTransform(mat,True)
      mol2.update_dependents('transform')
      self.parent.emitUpdateSignal(self.MolData_name)
      self.do_redraw = 1
      m1 = []
      m2 = []
      c1 = []
      c2 = []
      i1 = []
      i2 = []
      labels = []
      # Now need to convert the indices in pgo["matches"] to carts for self.conn2
      for match in pgo["matches"]:
        m1.append(match[0]) # Query, moving
        m2.append(match[1]) # Target, fixed
        c1.append(match[2])
        c2.append(match[3])
        i1.append(match[7])
        i2.append(match[8])
        labels.append(str(match[4]))
      self.conn2.Clear()
      self.conn2.AddConnectionsFromMatches(mol2.molHnd,mol1.molHnd,m1,m2,c1,c2,i1,i2,labels)
      self.gesamt_matches = "\n== Moving == Fixed == Distance ==\n"
      for match in pgo["matches"]:
        self.gesamt_matches += "%4d(%3s)%4d(%3s)%12.3f\n" % (match[0], match[5], match[1], match[6], match[4])
      self.last_rms = '%.2f/%i'%(pgo["rmsd"],self.conn2.GetNofConnections())

  def superpose_Gesamt(self):
    mol2 = data(self.fixed_MolData_name)
    mol1 = data(self.MolData_name)
    #print dir(mol1)

    f1 = mol1.filename[2]
    f2 = mol2.filename[2]
    if os.path.isfile(f1) and os.path.isfile(f2):
      self.gesamt_file1 = f1
      self.gesamt_file2 = f2
      gesamt_output = tempfile.NamedTemporaryFile(suffix="_gesamt_out.pdb",prefix="ccp4mg"+str(os.getpid()),delete=False)
      self.gesamt_output_file = gesamt_output.name
      gesamt_output.close()
      mask_selHnd1 = self.get_selHnd()
      mask_selHnd2 = self.parent.supModel[self.fixed_MolData_name].get_selHnd()
      self.gesamt_d1 = mol1.molHnd.SelectionToSCOP(mask_selHnd1)
      self.gesamt_d2 = mol2.molHnd.SelectionToSCOP(mask_selHnd2)
      if len(self.gesamt_d1) == 0:
        self.gesamt_d1 = '*'
      if len(self.gesamt_d2) == 0:
        self.gesamt_d2 = '*'
      self.runGesamt()

  def superpose_SSM(self,match=0,undo=0,best=0):
    # NEEDS to handle the undo/best options
    #print 'superpose_SSM',self.MolData_name,self.fixed_MolData_name
    
    if not self.fixed_MolData_name: return [1,'Error with fixed model']

    mol1 = data(self.fixed_MolData_name)
    mol2 = data(self.MolData_name)
    if not mol1 or not mol2: return [1,'Error - possibly model deleted?']
    
    mask_selHnd2 = self.get_selHnd()
    mask_selHnd1 = self.parent.supModel[self.fixed_MolData_name].get_selHnd()
    if mask_selHnd2<0: return [1,'Error selecting mask for moving model']
    if mask_selHnd1<0:return [1,'Error selecting mask for fixed model']
    new_MGAlign = CMGAlign()
    current_transform = mol2.molHnd.GetTransform()
    mol2.molHnd.UnSetTransform(True)
    RC = new_MGAlign.MGAlign(
                   mol2.molHnd, mol1.molHnd,3,1,
                   mask_selHnd2,mask_selHnd1,match-1,self.conn2 )
    print "Superpose status (0=success)",mol2.name_label,RC
    if RC==0:
      self.MGAlign = new_MGAlign
      if match>0:
        self.displayed_match = copy.deepcopy(match)
      else:
        self.displayed_match = self.MGAlign.GetMatchNumber()+1
        self.nof_matches = self.MGAlign.GetNumberofSSEMatches()
        self.best_match = self.MGAlign.GetMatchNumber()+1
      self.last_rms = '%.2f/%i'%(self.MGAlign.GetRMSD(),self.conn2.GetNofConnections()) 
      #print 'from GetRMSD',self.last_rms; sys.stdout.flush()
      mol2.update_dependents('transform')
      self.parent.emitUpdateSignal(self.MolData_name)
      self.do_redraw = 1
      return [0,'']
            
    else :
      import pygl_coord
      # Thus getInvMatrix is potentially wrong: 8/2/2010 changes for quat bug.
      rotmat = pygl_coord.Quat(pygl_coord.matrix(4,4,current_transform)).getInvMatrix()
      mol2.molHnd.SetTransform(rotmat,True)
      return [1,self.ssm_error_message(RC)]
      
#-----------------------------------------------------------------------
  def ssm_error_message(self,RC):
#-----------------------------------------------------------------------
    import model
    moving_MolData = data(self.MolData_name)
    fixed_MolData = data(self.fixed_MolData_name)
    
    output_text = 'Superposing '+moving_MolData.name+ \
         ' onto '+fixed_MolData.name + ' FAILED error code='+str(RC)+'\n\n'

    if RC == SPSN_noVertices:
      output_text = output_text + 'No secondary structure elements in the selection for '+moving_MolData.name+'\n\n'
    elif RC == SPSN_noVertices2:
      output_text = output_text + 'No secondary structure elements in the selection for '+fixed_MolData.name+'\n\n'
    elif RC == SPSN_noHits:
      output_text = output_text + 'No matching secondary structures found for '+moving_MolData.name+'\n\n'
    elif RC == SPSN_noSPSN:
      output_text = output_text +'Can not superpose the apparently matching SSEs for '+moving_MolData.name+'\n\n'
    elif [SPSN_noGraph,SPSN_noGraph2].count(RC):
      output_text = output_text +'Failed to create graphs.\nPossibly model does not have secondary structure.\nPossibly program error.\nContact ccp4mg@ccp4.ac.uk\n'
    print "Superpose error:",output_text
    return output_text
            


#-----------------------------------------------------------------------------
  def superpose_LSQ (self, fixed_selHnd=-1,moving_selHnd=-1):
#-----------------------------------------------------------------------------
    import mmdb2 as mmdb

    #print 'superpose_LSQ',fixed.name_label, fixed_selHnd, moving.name_label , moving_selHnd 

    #***Limitations***
    # Amino acids
    # Does not handle not sequence-order matching

    if self.parent.selected_atoms_mode == 'main':
      central_atom = 'CA,N,O,C'
      res_type = 'amino_acid'
    elif self.parent.selected_atoms_mode =='catrace':
      central_atom = 'CA'
      res_type = 'amino_acid'
    elif self.parent.selected_atoms_mode == 'nucleic':
      central_atom = "P,C1',C1*"
      res_type = 'nucleic_acid'
    elif self.parent.selected_atoms_mode == 'all':
      res_type = 'amino_acid'
      central_atom = central_atom = "*"
    elif self.parent.selected_atoms_mode == 'user':
      res_type = ''
      central_atom =  self.parent.get_user_selected_atoms()
      
    # Get all the CA in amino acids
    fixed_molobj = data(self.fixed_MolData_name)
    if not fixed_molobj: return [-1,'Error with fixed model']
    
    fixedHnd = fixed_molobj.molHnd.NewSelection()
    if res_type:
      fx_amino_acid = MODELINFO().get_names_string( key=res_type, set=fixed_molobj.name_label)
    else:
      fx_amino_acid = '*'
    fixed_olobj.molHnd.Select(fixedHnd ,mmdb.STYPE_ATOM,0,'*', \
        mmdb.ANY_RES,'*',mmdb.ANY_RES,'*',fx_amino_acid,central_atom,'*','*',SKEY_NEW)
    if fixed_selHnd >0:
      fixed_molobj.molHnd.Select(fixedHnd,mmdb.STYPE_ATOM,fixed_selHnd,mmdb.SKEY_AND)
    
    selindexp = intp()
    fixed_atom = newPPCAtom()
    #fixed_nat = fixed_molobj.molHnd.GetSelIndex(fixedHnd,fixed_atom)
    fixed_atom = mmut.GetAtomSelIndex(fixed_molobj.molHnd,fixedHnd,selindexp)
    fixed_nat = selindexp.value()
    if ( fixed_nat < 3 ) :
      fixed_molobj.molHnd.DeleteSelection(fixedHnd)
      return [1,'Less than three residues containing '+central_atom+' selected for '+fixed_molobj.name_label]

    moving_molobj = data(self.MolData_name)
    movingHnd = moving_molobj.molHnd.NewSelection()
    moving_atom = newPPCAtom()
    #moving_nat = moving_molobj.molHnd.GetSelIndex(moving_selHnd,moving_atom)
    moving_atom = mmut.GetAtomSelIndex(moving_molobj.molHnd,moving_selHnd,selindexp)
    moving_nat = selindexp.value()
    #print 'superpose_LSQ moving_selHnd nat',moving_nat
    if res_type:
      mv_amino_acid = MODELINFO().get_names_string(key=res_type, set=moving_molobj.name_label)
    else:
      mv_amino_acid = '*'
    #print 'superpose mv_amino_acid',mv_amino_acid,central_atom
    moving_molobj.molHnd.Select(movingHnd ,mmdb.STYPE_ATOM,0,'*', \
        mmdb.ANY_RES,'*',mmdb.ANY_RES,'*',mv_amino_acid,central_atom,'*','*',SKEY_NEW)
    if moving_selHnd > 0:
      moving_molobj.molHnd.Select(movingHnd,mmdb.STYPE_ATOM,moving_selHnd,mmdb.SKEY_AND)
    #moving_nat = moving_molobj.molHnd.GetSelIndex(movingHnd,moving_atom)
    moving_atom = mmut.GetAtomSelIndex(moving_molobj.molHnd,movingHnd,selindexp)
    moving_nat = selindexp.value()
    if ( moving_nat < 3 ) :
      fixed_molobj.molHnd.DeleteSelection(fixedHnd)
      moving_molobj.molHnd.DeleteSelection(movingHnd)
      return [1,'Less than three residues containing '+central_atom+' selected for '+moving.name_label]

    #print 'superpose_LSQ moving_nat',moving_nat,fixed_nat
    if ( moving_nat != fixed_nat ):
      fixed_molobj.molHnd.DeleteSelection(fixedHnd)
      moving_molobj.molHnd.DeleteSelection(movingHnd)
      return [1,'Different number of residues selected for models\n' + \
                    fixed_molobj.name_label+" has "+str(fixed_nat)+ '\n' + \
                    moving_molobj.name_label+" has "+str(moving_nat)]

    #rv = mmdb.SuperposeAtoms (TMatrix , moving_atom, moving_nat,fixed_atom,fixed_nat )
    rv = moving_molobj.molHnd.TransformToSuperposeAtoms ( moving_atom, moving_nat,fixed_atom )
    rms = moving_molobj.molHnd.AtomicRMSDistance(moving_atom, moving_nat,fixed_atom)
    #print "superpose_LSQ rv,rms",rv,rms
    self.last_rms = '%.2f/%i'%(rms,moving_nat)
    moving_molobj.update_dependents('transform')
    self.parent.emitUpdateSignal(self.MolData_name)

    self.displayed_match = 0
    fixed_molobj.molHnd.DeleteSelection(fixedHnd)
    moving_molobj.molHnd.DeleteSelection(movingHnd)
    return [0,'']

#-----------------------------------------------------------------------------
  def superpose_Close (self):
#-----------------------------------------------------------------------------
    import mmdb2 as mmdb
    #print 'superpose_close',self.close_atoms_res_cutoff, self.close_atoms_cutoff
    fixed = data(self.fixed_MolData_name)
    moving = data(self.MolData_name)
    if not fixed or not moving: return [-1,'Fixed model not defined']
    
    # Find the close atoms between the two models but use the selection masks
    moving_selHnd = self.get_selHnd()
    if moving_selHnd<0: return [-2,'Error selecting atoms for '+moving.name_label]
    fixed_selHnd = self.parent.supModel[self.fixed_MolData_name].get_selHnd()
    if fixed_selHnd<0: return [-2,'Error selecting atoms for '+fixed.name_label]

    self.conn2.Clear()
    nClose=self.conn2.AddCloseAtoms(fixed.molHnd, fixed_selHnd, moving.molHnd, moving_selHnd,
          self.parent.close_atoms_res_cutoff, self.parent.close_atoms_cutoff ,self.parent.close_atoms_central_atom, 0 )
    #print 'superpose_Close',self.MolData_name,nClose
    if nClose<0:
      return [-1,'Error serching for close atoms']
    elif nClose<3:
      return [-2,'Less than three close atoms found']
    self.conn2.Superpose(1)
    rms = self.conn2.GetRMSD()
    moving_nat = self.conn2.GetNofConnections()
    self.last_rms = '%.2f/%i'%(rms,moving_nat)
    moving.update_dependents('transform')
    self.parent.emitUpdateSignal(self.MolData_name)
    self.displayed_match = 0
    self.do_redraw = 1
    return [0,'']


#-----------------------------------------------------------------------
  def superpose_Matched(self):
#-----------------------------------------------------------------------
    self.update_Connectivity2()
    #print 'superpose_Matched',self.MolData_name,self.conn2.GetNofConnections()
    if self.conn2.GetNofConnections()<3:
      return [-1,'Less than three matched atoms']

    self.conn2.Superpose(1)
    rms = self.conn2.GetRMSD()
    self.last_rms = '%.2f/%i'%(rms,self.conn2.GetNofConnections())
    #print ' superpose_Matched last_rms',self.last_rms
    #self.update_Connectivity2()
    self.parent.emitUpdateSignal(self.MolData_name)
    data(self.MolData_name).update_dependents('transform')

    return [0,'']

#-----------------------------------------------------------------------
  def get_selHnd (self):
#-----------------------------------------------------------------------
    self.selection = self.selection.strip()
    if self.selection:
      sele = self.selection
    else:
      sele = 'all'
      
    rv = data(self.MolData_name).parse_selection(command=sele)
    #print 'Superpose  get_selHnd',self.MolData_name,sele,rv
    if not rv[0]: return rv[1]

    return -1

#-----------------------------------------------------------------------
  def nofModelsSelected(self):
#-----------------------------------------------------------------------
    # Return the number of symmetry/NMR models in current selection
    # If this is >1 the gui will query user to continue
    molobj = data(self.MolData_name)
    if molobj.nmr_models <=1 and \
        len(molobj.model_symmetry.get_symmetry_models())<=0:
      return 1
    selHnd = self.get_selHnd()
    if selHnd<0: return 1

    import mmdb2 as mmdb
    selindexp = intp()
    model_selHnd = molobj.molHnd.NewSelection()
    molobj.molHnd.Select(model_selHnd,mmdb.STYPE_MODEL,selHnd,mmdb.SKEY_NEW)
    pModels = newPPCModel()
    #nModels = molobj.molHnd.GetSelIndex(model_selHnd,pModels)
    pModels = mmut.GetModelSelIndex(molobj.molHnd,model_selHnd,selindexp)
    nModels = selindexp.value()
    #print 'nofModelsSelected',self.MolData_name,nModels
    molobj.molHnd.DeleteSelection(model_selHnd)
    return nModels
    
#-------------------------------------------------------------------------
  def create_MolDisp(self):
#-------------------------------------------------------------------------
    import model
    molobj =data(self.MolData_name)
    if not self.MolDisp:
      self.MolDisp = model.MolDisp(parent=molobj,
                  name=self.MolData_name+'_superpose',master_application=self.parent)
      self.MolDisp.set_style(style_mode='PYRAMIDS')
      self.MolDisp.set_colour(self.parent.moving_mask_colour)
      #print 'create_MolDisp',mol,self.last_MolDisp_colour
      if not ['all','',' '].count(self.selection):
        #print 'Superpose.create_MolDisp',mol,self.status[mol]['selection']
        self.MolDisp.set_selection(self.selection)
        self.MolDisp.SelHandle.mergeSelection(command='one_atom',op='and')

        
#-------------------------------------------------------------------------
  def Print(self):
#-------------------------------------------------------------------------
    
    moving_MolData = data(self.MolData_name)
    fixed_MolData = data(self.fixed_MolData_name)
    if not fixed_MolData: return 'Fixed model not set for: '+self.MolData_name

    if self.parent.method == 'close':
      output_text = 'Superposing '+moving_MolData.name_label + ' (atoms close to those selected for fixed model)\n'
    else:
      output_text = 'Moving: '+moving_MolData.name_label + ' (' + self.selection + ')\n'
      
    output_text= output_text+ 'Fixed:  '+fixed_MolData.name_label  + ' (' + self.parent.supModel[self.fixed_MolData_name].selection +')\n'

    if self.last_rms:
      output_text =  output_text +  '\nRMS of selected atoms: '+str(self.last_rms)+'\n\n'

    if self.MGAlign and self.parent.submethod == "ssm":
      output_text =  output_text +  \
        '\n\n'+'Match number '+str(self.displayed_match)+ \
        ' of '+str(self.nof_matches)+ \
        ' (best result is number '+str(self.best_match)+')\n\n'+ \
        self.MGAlign.Print(self.MolData_name,self.fixed_MolData_name )+'\n\n\n'

    if self.gesamt_matches and self.parent.submethod == "gesamt":
      output_text =  '\n\n' + output_text +  self.gesamt_matches +'\n\n'

    output_text =  output_text +"\n\nTransformation applied to the moving model\n"+ moving_MolData.molHnd.GetTransformString()
    if fixed_MolData.molHnd.GetIsTransformed():
       output_text = output_text + "NOTE: The fixed model is also transformed by\n"+fixed_MolData.molHnd.GetTransformString()
    return output_text
                                       
#-----------------------------------------------------------------
  def export_matches(self,filename='',all=0,comments=[]):
#-----------------------------------------------------------------
    import time

    if (not self.conn2) and self.conn2.GetNofConnections()<0:
      return [1,'No matches defined']

    self.conn2.UpdateCoordinates(True)
    
    text='''loop_
_vector.start_model_id
_vector.start_atom
_vector.end_model_id
_vector.end_atom
_vector.label\n'''
    nV= self.conn2.GetNofConnections()
    #print "export_matches nV",nV
    for iV in range(0,nV):
        atom1 = self.conn2.GetAtomID(iV,1)
        atom2 = self.conn2.GetAtomID(iV,2)
        if atom1 and atom2:
          text=text+'"'+self.fixed_MolData_name+'"  "'+atom1+'" ' \
             +'"'+self.MolData_name+'"  "'+atom2+'"  ' \
             +'"'+self.conn2.GetLabel(iV)+'" \n'

    # Create header
    head = '#Definition of vectors for display with CCP4mg\n'+ \
           '# Matched atoms in Superposition\n' + \
           '#Created on '+time.asctime(time.localtime(time.time()))+"\n"

    head = head+'\ndata_VECTORS\n'
    
    for item in comments:
      head = head+"# "+item+"\n"
    head = head+"\n\n"
    
    head = head+'''loop_
_model.id
_model.filename\n'''
    head = head + self.fixed_MolData_name + '  ' + data(self.fixed_MolData_name).getFilename() + '\n'
    head = head + self.MolData_name + '  ' + data(self.MolData_name).getFilename() + '\n'

      
    if self.parent.show_distances ==  cprim.LABELLEDCENTRE:
      labmo = '"label centre"'
    else:
      labmo = '"no label"'
      
    head = head+'''\n\nloop_
_display_objects.line_colour
_display_objects.line_style
_display_objects.line_width
_display_objects.label_mode
_display_objects.label_colour
"complement" "dashed"  2 '''+ labmo + ' "complement"\n\n'


    text = head + "\n" + text 

    if filename:
      import utils
      utils.saveFile(filename=filename,text=text)
      
    return [0,text]

#-----------------------------------------------------------
#-----------------------------------------------------------
#-----------------------------------------------------------
class SuperposeMatch:
#-----------------------------------------------------------
#-----------------------------------------------------------
#-----------------------------------------------------------

  initialise = { 'mode' : 'range',
                 'close' : 0,
                 'on' : 1,
                 'sele1' : '',
                 'sele2' : '',
                 'qualifier' : '' }

  def __init__(self,params={},mode='range',close=0,sele1='',sele2='',qualifier='',on=1):

    self.mode = mode
    self.close = close
    self.on = on
    self.sele1= sele1
    self.sele2 = sele2
    self.qualifier = qualifier
    self.setparams(params)

  def set(self,key,value):
    setattr(self,key,value)

  def get(self,key):
    return getattr(self,key,'')

  def isSame(self,other):
    if self.mode != other.mode or self.close != other.close or self.qualifier != other.qualifier:
      return 0
    if self.sele1 == other.sele1 and self.sele2 == other.sele2:
      return 1
    elif self.sele1 == other.sele2 and self.sele2 == other.sele1:
      return -1
    else:
      return 0

  def getparams(self,swap_selections=0):
    pars = {}
    for key in SuperposeMatch.initialise:
      pars[key] = getattr(self,key,'')
    if swap_selections:
       pars['sele1'] = getattr(self,'sele2','')
       pars['sele2'] = getattr(self,'sele1','')
    return pars

  def setparams(self,params):
    for key,value in params.items():
      setattr(self,key,value)


  def getUnmatched(self,model=1):
    if model == 1 and not self.sele2:
      return [self.mode,self.sele1]
    elif model == 2 and not self.sele1:
      return [self.mode,self.sele2]
    else:
      return []


  def getUnmatchedMenu(self,model=1,start=0):
    
    conversions = {  }
    if start:
      if self.mode=='range'and self.qualifier=='first' :
        if model == 1 and self.sele1:
          return ['End range from ' + self.sele1]
        elif model == 2 and  self.sele2:
          return ['End range from ' + self.sele2]
        else:
          return []
      else:
        return []
    else:
      unmatched = self.getUnmatched(model)
      if unmatched:
        if unmatched[0]=='range':
          menu_item_list = [ ['start','Start of range ' + unmatched[1]] ]
          if self.qualifier != 'first': menu_item_list.append(['end','End of range ' + unmatched[1]])
          return menu_item_list
        elif conversions.has_key(unmatched[0]):
           return [ [conversions[unmatched[0]],conversions[unmatched[0]] + ' '+unmatched[1] ] ]
        else:
           return [ [  unmatched[0],unmatched[0]+' '+unmatched[1] ] ]
      else:
        return []
      

    '''
    else:
      for ii in range(0,len(list1)):
        if ['close',''].count(list2[ii]):
          if list1[ii][0:4]=='rang':
            menu.append(list1[ii]+' start')
            menu.append(list1[ii]+' end')
          else:
            menu.append(list1[ii])
    '''
    
#-----------------------------------------------------------------------
  def setEndOfRange(self,mode='start',fixed_MolData_name='',MolData_name=''):
#-----------------------------------------------------------------------
    #print 'setEndOfRange',mode,fixed_MolData_name,MolData_name
    if mode == 'end':
      direction = -1
    else:
      direction = 1
      
    pres1=self.getResPointers(self.sele1,fixed_MolData_name)
    pres2=self.getResPointers(self.sele2,MolData_name)
    pr1 = 0
    pr2 = 0
    irdif1 = 0
    irdif2 = 0
    if len(pres1)==2:
      if (pres1[0].GetChain().GetChainID() != pres1[1].GetChain().GetChainID()): return [1,'The selected residues in the fixed model are not in the same chain']
      irdif1 = pres1[1].GetResidueNo() -  pres1[0].GetResidueNo()
    if len(pres2)==2:
      if (pres2[0].GetChain().GetChainID() != pres2[1].GetChain().GetChainID()): return [1,'The selected residues in the moving model are not in the same chain']
      irdif2 = pres2[1].GetResidueNo() -  pres2[0].GetResidueNo()
    #print 'setEndOfRange irdif',irdif1,irdif2
    if irdif1 and irdif2:
      if irdif1 != irdif2:
        return  [1,'The selected ranges have different numbers of residues']
      else:
        return [0,'']
    elif (not irdif1) and (not irdif2):
      return [2,'No range selected yet']
    elif irdif1:
      if len(pres2):
        pr2 = pres2[0].GetChain().GetResidue( pres2[0].GetResidueNo()+(direction*irdif1) )
      #print 'setEndOfRange',irdif1,'pr2',pr2
      if not pr2:
        return  [1,'The selected range in the fixed model can not be matched in the moving model']
      if direction<0:
        rv = data(MolData_name).getResidueID(pr2,pres2[0])
      else:
        rv = data(MolData_name).getResidueID(pres2[0],pr2)
      self.sele2 = rv[0]
    elif irdif2:
      if len(pres1):
        pr1 = pres1[0].GetChain().GetResidue(pres1[0].GetResidueNo()+(direction*irdif2))
      #print 'setEndOfRange',irdif2,'pr1',pr1
      if not pr1:
        return  [1,'The selected range in the moving model can not be matched in the fixed model']
      if direction<0:
        rv = data(fixed_MolData_name).getResidueID(pr1,pres1[0])
      else:
        rv = data(fixed_MolData_name).getResidueID(pres1[0],pr1)
      self.sele1 = rv[0]
    #print 'setEndOfRange',self.sele1,self.sele2
    return [0,'']
  
      
#-----------------------------------------------------------------------
  def getResPointers(self,sele='',MolData_name=''):
#-----------------------------------------------------------------------
    molobj = data(MolData_name)
    import string
    pslash = sele.rfind('/')
    if pslash<0: return []
    pdash = sele.rfind('-')
    #print 'getResPointers',sele,   pslash, pdash
    rv = []
    if  pdash<0:
      r1 = molobj.interpretResidueID(sele)
      if r1[0]==1: rv.append(r1[1])
    else:   
      r1 = molobj.interpretResidueID(sele[0:pdash])
      r2 = molobj.interpretResidueID(sele[0:pslash+1]+sele[pdash+1:])
      if r1[0]==1: rv.append(r1[1])
      if r2[0]==1: rv.append(r2[1])
    return rv