"""
     qtgui/mgTreeView.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2011 University of York

     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.
"""
from PyQt4 import QtCore, QtGui
import pygl_coord
import mmdb2 as mmdb
import global_definitions,guiUtils
import sys,string
import mmut

ATOMTYPE = 1001
RESTYPE = 1002
RESTYPE_A = 1003
CHAINTYPE = 1004
SECSTRTYPE = 1005
ALLTYPE = 1001

class AbstractMolData(QtGui.QStandardItemModel):

  composite = {}

  composite['monomers'] = dict ( label = 'Monomers',
                                 tree = [ 'res','atom'],
                                 selection_mask = 'monomers' )
  composite['nucleic'] = dict ( label = 'Nucleic acid',
                               selection_mask = 'nucleic'  )
  composite[ 'saccharide'] = dict ( label = 'Saccharides',
                                selection_mask = 'saccharide' )
  composite[ 'peptide'] = dict ( label = 'Peptide',
                                selection_mask = 'peptide' )
  composite ['secstr'] = dict ( label = 'Peptide - secondary structure',
                                tree = [ 'chain','secstr','res','atom'],
                         selection_mask = 'amino_acid' )
                         
  composite['solvent'] = dict ( label = 'Solvent',
                                selection_mask = 'solvent'  )
  composite['solute'] = dict ( label = 'Solute',
                                tree = [ 'res','atom'],
                                selection_mask = 'solute'  )
  composite['metal'] = dict ( label = 'Metal ions',
                                tree = [ 'chain','res','atom'],
                                selection_mask = 'metal'  )
  
  composite['all'] = dict ( label = 'Chain/residue/atom',
                                selection_mask = 'all'  )

  

#-------------------------------------------------------------------------
  def __init__(self,molHnd,MolDataObj):
#-------------------------------------------------------------------------
    ##print "into AbstractMolData"
    QtGui.QStandardItemModel.__init__(self)
    import time
    start = time.clock()

    # Sort out data to help lookup
    self.lookup = {}
    self.groupItems = {}
      
    self.molHnd = molHnd
    self.MolDataObj = MolDataObj
    self.parentItem = self.invisibleRootItem()
    #self.allItem = MGStandardItem(QtCore.QString('All'),ALLTYPE)
    #self.parentItem.appendRow(self.allItem)
    
    self.setHeaderData(0,QtCore.Qt.Horizontal,QtCore.QVariant(self.MolDataObj.name_label),QtCore.Qt.DisplayRole)

    self.udd_chn_chn = self.molHnd.GetUDDHandle( mmdb.UDR_CHAIN,"qt_chn_row")
    if self.udd_chn_chn <= 0: self.udd_chn_chn = self.molHnd.RegisterUDInteger(mmdb.UDR_CHAIN,"qt_chn_row")
   
    self.udd_chn = self.molHnd.GetUDDHandle( mmdb.UDR_RESIDUE,"qt_chn_row")
    if self.udd_chn <= 0: self.udd_chn = self.molHnd.RegisterUDInteger(mmdb.UDR_RESIDUE,"qt_chn_row")
    self.udd_res=self.molHnd.GetUDDHandle( mmdb.UDR_RESIDUE,"udr_res_row")
    if self.udd_res<=0: self.udd_res = self.molHnd.RegisterUDInteger(mmdb.UDR_RESIDUE,"qt_res_row")
    nChn = self.molHnd.GetNumberOfChains(1)
    for ic in range(0,nChn):
      self.molHnd.PutUDData(self.udd_chn_chn,-1)
      resTable = mmdb.newPPCResidue()
      nRes = pygl_coord.inta(1)
      #self.molHnd.GetResidueTable ( 1,ic,resTable,nRes )
      resTable = mmut.GetResidueTable(self.molHnd,1,ic,nRes)
      for ir in range(0,nRes[0]):
        self.molHnd.PutUDData(self.udd_chn,-1)
        
    #print "loading abstact model",time.clock() - start


#--------------------------------------------------------------------
  def loadGroup(self,groupName='',definition={},**keywords):
#--------------------------------------------------------------------

    datatype = 0
    for key,value in self.lookup.items():
      if value['groupName'] == groupName: datatype = key
    if datatype>0: return datatype
    
    definition.update(keywords)
    #print "loadGroup",groupName,definition

    # Are there any atoms that match selection criteria?
    masterHnd = definition.get('masterHnd',0)
    if not masterHnd:
      selection_mask = definition.get('selection_mask','all')
      parent_selection =definition.get('parent_selection','/1/')    
      masterHnd = self.getAtomSelection(selection_mask,parent_selection)
      #print 'masterHnd',masterHnd
      if masterHnd<0: return -1

    # Create a node for this component
    groupCode = len(self.lookup)+1
    label = definition.get('label',self.MolDataObj.name_label)
    item = MGStandardItem(QtCore.QString(label),groupCode)
    self.parentItem.appendRow(item)

    tree = definition.get('tree',['chain','res','atom'])
    self.addChildren(groupCode,item,masterHnd,tree)
    self.molHnd.DeleteSelection(masterHnd)
                          
    child = item.child(0,0).index()
    if child:
      self.groupItems[groupName]=item
      self.lookup[groupCode] = {
                        'groupName' : groupName,
                        'tree' : tree,
                        'selection_mask' : selection_mask,
                        'parent_selection' : parent_selection }

    return groupCode

#----------------------------------------------------------------------
  def getGroups(self):
#----------------------------------------------------------------------
    return self.groupItems.keys()

  def getGroupRow(self,group):
    row = 0
    for key,value in self.lookup.items():
      if value['groupName'] == group: row = key
    return row-1

#------------------------------------------------------------------------
  def getAtomSelection(self,selection_mask,parent_selection):
#------------------------------------------------------------------------
    #print "getAtomSelection selection_mask",selection_mask
    rv = self.MolDataObj.parse_selection(command=selection_mask)
    if rv[0] or rv[1] <= 0:
      print "ERROR selecting ",selection_mask
      return -1
    
    atmHnd = rv[1]
    self.molHnd.Select(atmHnd,mmdb.STYPE_ATOM,parent_selection,mmdb.SKEY_AND)
    nAtoms = self.MolDataObj.nAtoms(atmHnd)
    #print "nAtoms",nAtoms
    if nAtoms<=0:
      self.molHnd.DeleteSelection(rv[1])
      return -1
    else:
      return  atmHnd


#----------------------------------------------------------------------
  def addChildren(self,groupCode,parent_item,masterHnd,tree,loadAtoms=0):
#----------------------------------------------------------------------
    selindexp = pygl_coord.intp()
    # Be prepared for recursive call to addChildren
    if len(tree)>1 and (tree[1] != 'atom' or loadAtoms):
      nxtHnd = self.molHnd.NewSelection()
    else:
      nxtHnd = 0

    if tree[0] == 'chain':
      chnHnd = self.molHnd.NewSelection()
      self.molHnd.Select(chnHnd,mmdb.STYPE_CHAIN,masterHnd,mmdb.SKEY_NEW)
      chnSel = mmdb.newPPCChain()
      #nChn = self.molHnd.GetSelIndex(chnHnd,chnSel)
      chnSel = mmut.GetChainSelIndex(self.molHnd,chnHnd,selindexp)
      nChn = selindexp.value()
      #print "nChn",nChn
            
      for ic in range(0,nChn):
        pChn = mmdb.getPCChain(chnSel,ic)
        name = self.chainName(pChn)
        try:
          dum = int(name.rstrip('/'))
          name = '//'+str(dum)+'/'
        except:
          pass
        item = MGStandardItem(QtCore.QString(name),CHAINTYPE)      
        parent_item.appendRow(item)
        pChn.PutUDData(self.udd_chn_chn,groupCode + (10*item.row()))
        # So long as its not atoms load the next layer down
        if nxtHnd>0:          
          self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,masterHnd,mmdb.SKEY_NEW)
          self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,0,pChn.GetChainID(),mmdb.ANY_RES,'*',mmdb.ANY_RES,'*','*','*','*','*',mmdb.SKEY_AND)
          self.addChildren(groupCode,item,nxtHnd,tree[1:],loadAtoms)
      
      self.molHnd.DeleteSelection(chnHnd)

    if tree[0] == 'res':
      resHnd = self.molHnd.NewSelection()
      self.molHnd.Select(resHnd,mmdb.STYPE_RESIDUE,masterHnd,mmdb.SKEY_NEW)
      resSel = mmdb.newPPCResidue()
      #nRes = self.molHnd.GetSelIndex(resHnd,resSel)
      resSel = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nRes = selindexp.value()
      #print "nRes",nRes

      if tree[-1]=='atom':
        datatype = RESTYPE_A
      else:
        datatype = RESTYPE
      #print "res type",type
            
      for ir in range(0,nRes):
        pRes = mmdb.getPCResidue(resSel,ir)
        name = self.residueName(pRes)
        item = MGStandardItem(QtCore.QString(name),datatype)      
        parent_item.appendRow(item)
        pRes.PutUDData(self.udd_res,item.row())
        pRes.PutUDData(self.udd_chn,groupCode + (10*parent_item.row()))
        # So long as its not atoms load the next layer down
        if nxtHnd>0:
          self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,masterHnd,mmdb.SKEY_NEW)
          seqNum = pRes.GetSeqNum()
          insCode = pRes.GetInsCode()
          self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,pRes.GetModelNum(),pRes.GetChainId(),seqNum,'*',seqNum,'*','*','*','*','*',mmdb.SKEY_AND)
          self.addChildren(groupCode,item,nxtHnd,tree[1:],loadAtoms)
      
      self.molHnd.DeleteSelection(resHnd)


    elif tree[0] == 'atom':
      atmHnd = self.molHnd.NewSelection()
      self.molHnd.Select(atmHnd,mmdb.STYPE_ATOM,masterHnd,mmdb.SKEY_NEW)
      atmSel = mmdb.newPPCAtom()
      #nAtm = self.molHnd.GetSelIndex(atmHnd,atmSel)
      atmSel = mmut.GetAtomSelIndex(self.molHnd,atmHnd,selindexp)
      nAtm = selindexp.value()
      #print "nAtm",nAtm
            
      for ia in range(0,nAtm):
        pAtm = mmdb.getPCAtom(atmSel,ia)
        name = self.atomName(pAtm)
        item = MGStandardItem(QtCore.QString(name),ATOMTYPE)      
        parent_item.appendRow(item)
        #print "signal",pAtm,pAtm.name
        self.emit(QtCore.SIGNAL('addedAtom'),item,pAtm)
              
      self.molHnd.DeleteSelection(atmHnd)

    elif tree[0] == 'secstr':
      import copy
      resHnd = self.molHnd.NewSelection()
      self.molHnd.Select(resHnd,mmdb.STYPE_RESIDUE,masterHnd,mmdb.SKEY_NEW)
      resSel = mmdb.newPPCResidue()
      #nRes = self.molHnd.GetSelIndex(resHnd,resSel)
      selRes = mmut.GetResidueSelIndex(self.molHnd,resHnd,selindexp)
      nRes = selindexp.value()
      #print "secstr nres",nRes
      if nRes>=2:
        # NOSECSTR, BETA, BULGE, TURN3, TURN4, TURN5, ALPHA
        mask = [ 0 , 1 , 1 , 0, 0, 0, 2]
        output = ['L','S','H']
        first_i = 0
        first_sec = mask[mmdb.getPCResidue(resSel,0).SSE]
        i = 1
        # NB **** one extra time around loop below to flush last secstr
        while i <= nRes:
          if i < nRes:
            sec = mask[mmdb.getPCResidue(resSel,i).SSE]
          else:
            sec = 999
          if sec != first_sec:
            if first_sec>=0:
              name =  self.resRangeName(mmdb.getPCResidue(resSel,first_i),mmdb.getPCResidue(resSel,i-1))
              #print "secstr name",name
              item = MGStandardItem(QtCore.QString(output[first_sec]+' '+name),SECSTRTYPE)      
              parent_item.appendRow(item)
                                        
              if nxtHnd>0:          
                self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,masterHnd,mmdb.SKEY_NEW)
                self.molHnd.Select(nxtHnd,mmdb.STYPE_ATOM,name,mmdb.SKEY_AND)
                self.addChildren(groupCode,item,nxtHnd,tree[1:],loadAtoms)

            first_i = copy.deepcopy(i)
            first_sec = copy.deepcopy(sec)
          i = i + 1
              
    if nxtHnd>0: self.molHnd.DeleteSelection(nxtHnd)

      
#--------------------------------------------------------------------------
  def interpretTreeSelection(self,Index_list,returnCommand=0):
#--------------------------------------------------------------------------
    '''
    Convert a list of selected items (QModelIndex) to an mg selelction
    command or and MMDB selection handle
    Input:
    Index_list - list of QModelIndex
    '''
    
    import re

    if not Index_list:
      if returnCommand:     
        return [0,'']
      else:
        return [0,'',-1]
    
    if returnCommand:
      pass
    else:
      selHnd = self.molHnd.NewSelection()
    #Index_list.sort(self.QModelIndexSort)
    selected_list = []
    top_selected_list = []
    ii = 0
    
    while ii < len(Index_list):

      name = ''
      datatype,group,stack,parent_selected = self.interpretIndex(Index_list[ii],Index_list)
      #print 'interpretTreeSelection',datatype,group,stack,parent_selected
      index = Index_list[ii]

      if len(stack) == 0 or parent_selected:
        # A parent/grand-parent object already selected
        name = ''
      elif len(stack)==1 and datatype < ATOMTYPE:
      # Have clicked top level monomer/peptide etc
        selected_list.append([1,'',0,self.lookup[datatype].get('selection_mask')])
      else:
        
        # Residue - test if its first of a range
        if [RESTYPE,RESTYPE_A].count(datatype):
          ir,ic = row,column = index.row(),index.column()
          #if stack[0] == 'Monomers':
          name = stack[-1]
          #else:
          #  name = re.sub(r'\(.*\)','',stack[-1])
          chn_sort = stack[-2][0:-1]
          res_sort = string.split(name,'/')[-1]
          #print "testing next res",ii,ir,str(index.sibling(ir+1,column).data().toString())
          sibling_index =  index.sibling(ir+1,column)
          doSiblingCheck = True
          if len(stack)>0 and stack[0] == "Monomers":
            doSiblingCheck = False
          while doSiblingCheck and sibling_index.isValid() and Index_list.count(sibling_index):
            ir = ir +1
            last_res_name = str(index.sibling(ir,column).data().toString())
            jj = Index_list.index(index.sibling(ir,column))           
            Index_list[jj] =  QtCore.QModelIndex()
            sibling_index = index.sibling(ir+1,column)
            #print "testing next res",ii,ir,str(index.sibling(ir,column).data().toString())
          if ir != row:
            name = re.sub(r'\(.*\)','',name) + '-' + re.sub(r'\(.*\)','',string.split(last_res_name,'/')[-1])
          selected_list.append([2,chn_sort,res_sort,name])

            
        elif datatype == ATOMTYPE:
          # its an atom - test if there are other selected atoms in residue
          # Complication .. mmdb does not  altlocs in atom list of form ..'A/23/CA:A,CB:A'
          # but does understand 'A/23/CA,CB:A'
          altloc_lists = {}
          #name  =re.sub(r'\(.*\)','', stack[-1])
          name  = stack[-1] 
          if name.count(':'):
            altloc_lists[name.split(':')[1]] = name.split(':')[0]
          else:
            altloc_lists[''] = name
          #print 'interpretTreeSelection ATOMTYPE',name,altloc_lists
          chn_sort = stack[-3][0:-1]
          res_sort = string.split(name,'/')[-2]
          tt = string.split(name,'/')[-2]
          ir = 0
          sib_index = index.parent().child(ir,0)
          while sib_index.isValid():
            if Index_list.count(sib_index):
              jj = Index_list.index(sib_index)
              if jj != ii:
                atm_name = str(Index_list[jj].data().toString())
                #print 'interpretTreeSelection testing same residue',atm_name
                if atm_name.count(':'):
                  alt = atm_name.split(':')[1]
                  if altloc_lists.has_key(alt):
                    altloc_lists[alt]=altloc_lists[alt] + ',' + string.split(string.split(atm_name,'/')[-1],':')[0]
                  else:
                     altloc_lists[alt]=atm_name.split(':')[0]
                else:
                  alt = ''
                  if altloc_lists.has_key(alt):
                    altloc_lists[alt]=altloc_lists[alt] + ',' + string.split(atm_name,'/')[-1]
                  else:
                     altloc_lists[alt]=atm_name
                #print "zeroing jj",jj
                Index_list[jj ] = QtCore.QModelIndex()
            ir = ir + 1
            sib_index = index.parent().child(ir,0)
          for key,value in altloc_lists.items():
            if key:
              selected_list.append([2,chn_sort,res_sort,value+':'+key])
            else:
              selected_list.append([2,chn_sort,res_sort,value])
              
        elif datatype == SECSTRTYPE:
          name = stack[-1][2:]
          chn_sort = stack[-2][0:-1]
          res_sort = string.split(string.split(name,'/')[-1],'-')[0]
          selected_list.append([2,chn_sort,res_sort,name])

        # Asume chain level
        else:
          name = stack[-1]
          if hasattr(self.molHnd,"duplicateChainIDS") and self.molHnd.duplicateChainIDS:
            for key,val in self.composite.items():
              if val.has_key('label') and val['label'] == stack[0]:
                selected_list.append([2,name[0:-1],'','{'+key+ ' and '+name+'}'])
                break
            else:
              selected_list.append([2,name[0:-1],'',name])
          else:
            #selected_list.append([2,name[0:-1],'',name])
            compname = [ cnkey for cnkey,cnval in self.composite.items() if (cnval.has_key('label') and cnval['label'] == stack[0])]
            if len(compname)>0:
              selected_list.append([2,name[0:-1],'','{'+compname[0]+' and '+name+'}'])
        
        #print "interpretTreeSelection name",name

      if not returnCommand and name:
        self.molHnd.Select(int(selHnd),int(mmdb.STYPE_ATOM),str(name),int(mmdb.SKEY_OR))
      ii = ii + 1

    #print "selected_list",selected_list
    if selected_list:
      selected_list.sort()
      comstring = selected_list[0][3]
      for item in selected_list[1:]: comstring = comstring + ' or ' + item[3]
    else: 
      comstring = ''
      
    if returnCommand:
      return [0,comstring]
    else:
      return [0,self.molHnd,selHnd]



#------------------------------------------------------------------------
  def interpretIndex (self,index,Index_list=[]):
#------------------------------------------------------------------------
    '''
    Return details about a selected item from tree
    Input:
    index : QModelIndex
    Index_list=[] : List of indices other selected items
    Output datatype,group,stack,parent_selected
    datatype : int MGStandardItem.datatype() of the item
    group: int MGStandardItem.datatype() of the top level item
    stack: list of QStandardItem.data() for item, parent, grand-parent - in reverse order
    parent_selected: true if Index_list contains an ancestor of index    
    '''
    
    if not index.isValid(): return 0,0,[],0
    stack = []
    datatype = int(self.itemFromIndex(index).datatype())
    parent_selected = 0
    while index.isValid():
      stack.append(str(index.data().toString()))
      if index.parent().isValid():
        if Index_list.count(index.parent()): parent_selected = 1
      else:
        group = int(self.itemFromIndex(index).datatype())
      index = index.parent()
    stack.reverse()
    #print "stack",stack,'parent_selected',parent_selected,'type',datatype,'group',group
    return datatype,group,stack,parent_selected


  def QModelIndexSort(self,i1,i2):
    if i1 == i2:
      return 0
    elif i1 < i2:
      return -1
    else:
      return 1

  def MolData(self):
    return self.MolDataObj

#------------------------------------------------------------------------
  def canFetchMore(self,index):
#------------------------------------------------------------------------
    '''
    Reimplement QStandardItemModel.canFetchMore() to return true when
    extra atoms can be loaded
    '''
    
    if (not index.isValid()) or index.child(0,0).isValid(): return 0
    #print "canFetchMore",self.itemFromIndex(index).datatype(),index.child(0,0).isValid()
    if self.itemFromIndex(index).datatype() == RESTYPE_A:
      return 1
    else:
      return 0
          
#-----------------------------------------------------------------------    
  def fetchMore(self,index):
#-----------------------------------------------------------------------
    '''
    Reimplement QStandardItemModel.fetchMore() to load atom data
    '''
    #print "fetchMore",index

    # Check index is valid and that it does not already have children
    if (not index.isValid()) or index.child(0,0).isValid(): return

    item = self.itemFromIndex(index)
    if item.datatype() == RESTYPE_A:
      datatype,groupCode,stack,parent_selected = self.interpretIndex(index)
      parent_selection = str(item.data(QtCore.Qt.DisplayRole).toString())
      # As ever beware the unnamed chain
      if parent_selection[0:4] != '// /':
        parent_selection = '/1/' + parent_selection      
      #print 'fetchMore fetchMoreparent_selection',parent_selection
      atmHnd = self.getAtomSelection(self.lookup[groupCode]['selection_mask'],parent_selection)
      if atmHnd>0:
        self.addChildren(groupCode,item,atmHnd,['atom'],1)
        self.molHnd.DeleteSelection(atmHnd)
        self.emit(QtCore.SIGNAL('moreFetched'),index)
      
#----------------------------------------------------------------------
  def hasChildren(self,index):
#----------------------------------------------------------------------
    '''
    Reimplement QStandardItemModel.hasChildren() to return true
    for residues which could have atom children
    '''
    if not index.isValid(): return 1
    datatype = int(self.itemFromIndex(index).datatype())
    if datatype == RESTYPE_A:      
      return 1
    else:
      return QtGui.QStandardItemModel.hasChildren(self,index)



#---------------------------------------------------------------------------
  def atomName(self,pAtom,inclRes=1,inclResName=1):
#---------------------------------------------------------------------------
    name = string.strip(pAtom.GetAtomName())
    alt = pAtom.GetAltLoc()
    if alt: name = name + ':' + alt
    if inclRes:
      return self.residueName(pAtom,inclResName) +'/'+name
    else:
      return name
    
#---------------------------------------------------------------------------
  def residueName(self,pAtom=None,inclResName=1,inclChainName=1):
#---------------------------------------------------------------------------
    if inclChainName:
      resname = self.chainName(pAtom) + str(pAtom.GetSeqNum())
    else:
      resname = str(pAtom.GetSeqNum())
    inscode = str(pAtom.GetInsCode())
    if inscode: resname = resname  + '.' + inscode
    if not inclResName: return resname
    resname = resname  + "(" + pAtom.GetResName() + ')'
    return resname
  
#---------------------------------------------------------------------------
  def chainName(self,pAtom):
#---------------------------------------------------------------------------
    chnid = str(pAtom.GetChainID())
    if not chnid or chnid == ' ':  chnid = '// '
    return chnid + '/'

#---------------------------------------------------------------------------
  def resRangeName(self,pRes1,pRes2):
#---------------------------------------------------------------------------
    
    name = self.residueName(pRes1,inclResName=0) + '-' + \
           self.residueName(pRes2,inclResName=0,inclChainName=0)
    return name
    


#------------------------------------------------------------------------
  def getModelIndex(self,selRes,ir):
#------------------------------------------------------------------------
    # get table   groupCode, chain row and residue row from
    # the mmdb information
    chn_row = pygl_coord.inta(1)
    res_row = pygl_coord.inta(1)
    pcres = mmdb.getPCResidue(selRes,ir)
    rv = pcres.GetUDData(self.udd_res,res_row)
    rv = pcres.GetUDData(self.udd_chn,chn_row)
    resRow = res_row[0]
    groupCode = chn_row[0]%10
    chnRow = chn_row[0]/10
    #print "getModelIndex",resRow,groupCode,chnRow
    if groupCode<=0: return None
    if not groupCode in self.lookup or not 'groupName' in self.lookup[groupCode] or not self.lookup[groupCode]['groupName'] in  self.groupItems: return None
    groupItem = self.groupItems[self.lookup[groupCode]['groupName']]
    if self.lookup[groupCode]['tree'][0]=='res':
      return groupItem.child(resRow,0).index()
    elif self.lookup[groupCode]['tree'][0:2]==['chain','res']:
      return groupItem.child(chnRow,0).child(resRow,0).index()
    else:
      return None
    
#------------------------------------------------------------------------
  def getChainIndex(self,selChn,ic):
#------------------------------------------------------------------------
    # get table   groupCode, chain row and residue row from
    # the mmdb information
    chn_row = pygl_coord.inta(1)
    pcchn = mmdb.getPCChain(selChn,ic)
    rv = pcchn.GetUDData(self.udd_chn_chn,chn_row)
    groupCode = chn_row[0]%10
    chnRow = chn_row[0]/10
    
    if groupCode<=0: return None
    groupItem = self.groupItems[self.lookup[groupCode]['groupName']]
    if self.lookup[groupCode]['tree'][0:2]==['chain','res']:
      return groupItem.child(chnRow,0).index()
    else:
      return None



#------------------------------------------------------------------------
class mgTreeView ( QtGui.QTreeView):
#------------------------------------------------------------------------

  def __init__(self,parent=None,abstractModel=None):
    super(mgTreeView,self).__init__(parent)

    # Don't display the header
    self.header().setVisible(0)

    self.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)
    self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)

    self.setModel(abstractModel)
    self.setExpanded(QtCore.QModelIndex(),1)
    for group,item in abstractModel.groupItems.items():
      #print "expanded",group,item
      self.setExpanded(item.index(),1)

    self.loadedAtomSelHnd = -1
    self.lastSelection = ''

    self.connect(self.model(),QtCore.SIGNAL('addedAtom'),self.handleAddedAtom)
    self.connect(self.selectionModel(),QtCore.SIGNAL('selectionChanged(QItemSelection,QItemSelection)'),self.changed)
    
  def __del__(self):
    if self.loadedAtomSelHnd>0:
      self.model().molHnd.DeleteSelection(self.loadedAtomSelHnd)

              
#---------------------------------------------------------------------------
  def changed(self):
#---------------------------------------------------------------------------
    #print "mgTreeView.changed emitting"
    self.emit(QtCore.SIGNAL('changed'))

#---------------------------------------------------------------------------
  def loadGroups(self,groups=[]):
#---------------------------------------------------------------------------
    self.selectionModel().blockSignals(True)
    for group in groups:
      self.model().loadGroup(group,definition=AbstractMolData.composite[group])

    for group in  self.model().getGroups():
      if not  groups.count(group):
        self.collapse(self.model().groupItems[group].index())
        row = self.model().getGroupRow(group)
        #print "getGroupRow",row
        if row>0:
          self.setRowHidden(row,self.model().groupItems[group].index().parent(),1)
    self.selectionModel().blockSignals(False)
    #self.changed()
    
#--------------------------------------------------------------------------
  def setGroupSelection(self,command='all'):      
#--------------------------------------------------------------------------
    import string
    rv = 1
    groupList =  self.model().getGroups()
    #print "mgTreeView.setGroupSelection groupList",command,groupList
    self.selectionModel().blockSignals(True)

    if command == 'all':
      for group in groupList:
        self.selectionModel().select(self.model().groupItems[group].index(),
                                   QtGui.QItemSelectionModel.Select)
      rv = 0
    elif groupList.count(command):
      self.selectionModel().select(self.model().groupItems[command].index(),
                                   QtGui.QItemSelectionModel.Select)
      rv = 0
    elif string.find(command,' or '):
      ok = 1
      for item in string.split(command,' or '):
        if not groupList.count(string.strip(item)): ok = 0
      if ok:
        for item in string.split(command,' or '):
          self.selectionModel().select(self.model().
            groupItems[string.strip(item)].index(),QtGui.QItemSelectionModel.Select)
        rv = 0

    self.selectionModel().blockSignals(False)

    return rv
  
#---------------------------------------------------------------------
  def loadMMDBSelection(self,selHnd):
#---------------------------------------------------------------------

    self.selectionModel().blockSignals(True)

    molHnd = self.model().molHnd
    selAtoms = mmdb.newPPCAtom()
    selRes = mmdb.newPPCResidue()
    selChains = mmdb.newPPCChain()

    selindexp = pygl_coord.intp()

    if self.loadedAtomSelHnd<0:
      self.loadedAtomSelHnd = molHnd.NewSelection()
    molHnd.Select(self.loadedAtomSelHnd,mmdb.STYPE_ATOM,selHnd,mmdb.SKEY_NEW)

    # make sure these are just in the first model
    molHnd.SelectAtoms(self.loadedAtomSelHnd,
       self.model().MolDataObj.first_nmr_model,
       "*",mmdb.ANY_RES,"*",mmdb.ANY_RES,"*","*","*","*","*",mmdb.SKEY_AND )
    #nselAtom =molHnd.GetSelIndex(self.loadedAtomSelHnd,selAtoms)
    selAtoms = mmut.GetAtomSelIndex(molHnd,self.loadedAtomSelHnd,selindexp)
    nselAtom = selindexp.value()
    #print "loadMMDBSelection initially nselAtom",nselAtom
    if nselAtom == 0:
      self.selectionModel().blockSignals(False)
      return 0

    # Set up mask for all atoms/residues/chains
    allAtomHnd = molHnd.NewSelection()
   
    molHnd.SelectAtoms(allAtomHnd, 1,
       "*",mmdb.ANY_RES,"*",mmdb.ANY_RES,"*","*","*","*","*",mmdb.SKEY_NEW )
    allResHnd= molHnd.NewSelection()
    molHnd.Select(allResHnd,mmdb.STYPE_RESIDUE,allAtomHnd,mmdb.SKEY_NEW)
    allChnHnd= molHnd.NewSelection()
    molHnd.Select(allChnHnd,mmdb.STYPE_CHAIN,allResHnd,mmdb.SKEY_NEW)
    wrkAtomHnd = molHnd.NewSelection()
    wrkResHnd = molHnd.NewSelection()
    wrkChnHnd = molHnd.NewSelection()


    #### CHAINS
    
    # Get Residues for which *all* atoms are selected
    molHnd.Select(wrkAtomHnd,mmdb.STYPE_ATOM,allAtomHnd,mmdb.SKEY_NEW)
    # Get all the non-selected atoms (in first model)
    molHnd.Select(wrkAtomHnd,mmdb.STYPE_ATOM,self.loadedAtomSelHnd,mmdb.SKEY_XOR)
    # Get all chains containing non-selected atoms
    molHnd.Select(wrkChnHnd,mmdb.STYPE_CHAIN,wrkAtomHnd,mmdb.SKEY_NEW)
    # Get chains not containing non-selected atoms - i.e. all atoms
    # in the chain are selected
    molHnd.Select(wrkChnHnd,mmdb.STYPE_CHAIN,allChnHnd,mmdb.SKEY_XOR)

    # Retreive these chains and select in the tree
    selChn = mmdb.newPPCChain()
    #nSelChn=molHnd.GetSelIndex(wrkChnHnd,selChn)
    selChn = mmut.GetAtomSelIndex(molHnd,wrkChnHnd,selindexp)
    nSelChn = selindexp.value()
    #print "wrkChnHnd nSelChn",nSelChn

    for ic in range(0,nSelChn):
      index = self.model().getChainIndex(selChn,ic)
      if index:
        self.selectionModel().select(index,QtGui.QItemSelectionModel.Select)
            
    
    #### RESIDUES
    
    # Get Residues for which *all* atoms are selected
    molHnd.Select(wrkAtomHnd,mmdb.STYPE_ATOM,allAtomHnd,mmdb.SKEY_NEW)
    # Get all the non-selected atoms (in first model)
    molHnd.Select(wrkAtomHnd,mmdb.STYPE_ATOM,self.loadedAtomSelHnd,mmdb.SKEY_XOR)
    # Get all residues containing non-selected atoms
    molHnd.Select(wrkResHnd,mmdb.STYPE_RESIDUE,wrkAtomHnd,mmdb.SKEY_NEW)
    # Get residues not containing non-selected atoms - i.e. all atoms
    # in the residue are selected
    molHnd.Select(wrkResHnd,mmdb.STYPE_RESIDUE,allResHnd,mmdb.SKEY_XOR)
    # And exclude any residues in completely selected chains
    molHnd.Select(wrkResHnd,mmdb.STYPE_RESIDUE,wrkChnHnd,mmdb.SKEY_CLR)

    # Retreive these residues and select in the tree
    #nSelRes=molHnd.GetSelIndex(wrkResHnd,selRes)
    selRes = mmut.GetResidueSelIndex(molHnd,wrkResHnd,selindexp)
    nSelRes = selindexp.value()
    #print "wrkResHnd nSelRes",nSelRes

    for ir in range(0,nSelRes):
      index = self.model().getModelIndex(selRes,ir)
      if index:
        self.selectionModel().select(index,QtGui.QItemSelectionModel.Select)
        
    # Find residues with some (but not all) atoms selected
    someResHnd= molHnd.NewSelection()
    molHnd.Select(someResHnd,mmdb.STYPE_RESIDUE,self.loadedAtomSelHnd,mmdb.SKEY_NEW)
    molHnd.Select(someResHnd,mmdb.STYPE_RESIDUE,wrkResHnd,mmdb.SKEY_CLR)
    #nSelRes=molHnd.GetSelIndex(someResHnd,selRes)
    selRes = mmut.GetResidueSelIndex(molHnd,someResHnd,selindexp)
    nSelRes = selindexp.value()
    #print "semi-selected residues",nSelRes

    ###### ATOMS

    # Get table of atoms in the residue and loop over these testing
    # if they are in the  self.loadedAtomSelHnd selection
    # BIG ASSUMPTION -- order of atoms in treeView is same as that returned
    # by GetAtomTable - expect this to be true but possibly could be broken by
    # atom editting

    if nSelRes>0:
      atomTable = mmdb.newPPCAtom()
      nAtom = mmdb.intp()
      for ir in range(0,nSelRes):
        index = self.model().getModelIndex(selRes,ir)
        #print "ir,index",ir,str(index.data().toString()),self.isExpanded(index)
        if index and self.isExpanded(index):
          atomTable = mmut.GetAtomTable(mmdb.getPCResidue(selRes,ir),nAtom)
          #pRes = mmdb.getPCResidue(selRes,ir).GetAtomTable(atomTable,nAtom)
          for ia in range(0,nAtom.value()):
            if mmdb.getPCAtom(atomTable,ia).isInSelection(self.loadedAtomSelHnd):
              self.selectionModel().select(index.child(ia,0),QtGui.QItemSelectionModel.Select)
    else:
      # We do not need to keep the selection
      molHnd.DeleteSelection(self.loadedAtomSelHnd)
      self.loadedAtomSelHnd = -1

    molHnd.DeleteSelection(allAtomHnd)
    molHnd.DeleteSelection(allResHnd)
    molHnd.DeleteSelection(wrkAtomHnd)
    molHnd.DeleteSelection(wrkResHnd)
    molHnd.DeleteSelection(someResHnd)    
    molHnd.DeleteSelection(wrkChnHnd)
    molHnd.DeleteSelection(allChnHnd)
    #molHnd.DeleteSelection(someChnHnd)
 
    self.selectionModel().blockSignals(False)
    #self.changed()
    
#--------------------------------------------------------------------------
  def getMMDBSelection(self):
#--------------------------------------------------------------------------    
    selected = self.selectedIndexes()
    #print "treeView.getMMDBSelection selected",selected
    ret = self.model().interpretTreeSelection(selected)
    #print "getMMDBSelection ret",ret
    if ret[0]:
      return -1
    else:
      return ret[2]

#---------------------------------------------------------------------------
  def getSelectionCommand(self):
#---------------------------------------------------------------------------
    selected = self.selectedIndexes()
    #print "selected",selected
    ret = self.model().interpretTreeSelection(selected,returnCommand=1)
    return ret

#---------------------------------------------------------------------------
  def handleExpanded(self,index):
#---------------------------------------------------------------------------
    item = self.model().itemFromIndex(index)
    if item.datatype() != RESTYPE_A or not item.hasChildren(): return
    #print "handleExpanded",index.data().toString()
    
    ii = 0
    child = index.child(ii,0)
      
#---------------------------------------------------------------------------
  def handleAddedAtom(self,item,pAtom):
#---------------------------------------------------------------------------
    #print "handleAddedAtom",item.text(),pAtom,pAtom.name
    if self.loadedAtomSelHnd > 0 and \
           pAtom.isInSelection(self.loadedAtomSelHnd):
      self.selectionModel().select(self.model().indexFromItem(item),QtGui.QItemSelectionModel.Select)

'''
#---------------------------------------------------------------------------
  def focusOutEvent(self,event):
#---------------------------------------------------------------------------
    #print 'mgTreeView.focusOutEvent'
    self.emit(QtCore.SIGNAL('focusOut'))
    QtGui.QTreeView.focusOutEvent(self,event)
    event.ignore()
'''
    
#--------------------------------------------------------------------------
class MGStandardItem(QtGui.QStandardItem):
#--------------------------------------------------------------------------

  def __init__(self,text,mgtype=1001):
    super(MGStandardItem,self).__init__(text)
    self.mgtype=mgtype
    #self.setCheckable(1)

  def datatype(self):
    #print "MGStandardItem type",self.mgtype
    return  self.mgtype

  


