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

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

import sys
import os
import functools
import dataobj,model_selection,model_colour
from global_definitions import *
     
#=============================================================== 
#=============================================================== 
#=============================================================== 
class SurfaceDispobj(dataobj.DispObj):
#=============================================================== 
#=============================================================== 
#===============================================================

  ElectroColourScheme = None
  HydroColourScheme = None
  MapColourScheme = None
  class_dependents = {}

  selection_initialisation = {
        'select':'allnotsolv',
        'nothydrogen': 1,
        'neighb_wholeres':0,
        'neighb_excl':1,
        'neighb_excl_solvent':1,
        'neighb_rad': 7.0 }
  
  surface_type_menu = ['Normal','van der Waals','Solvent accessible']
  surface_type_alias= ['surface_type_normal','surface_type_vdw','surface_type_accessible' ]

  def toggleBiomolecule(self):
      self.doBiomolecule = not self.doBiomolecule
      if hasattr(self,"graphmod") and hasattr(self.graphmod,"obj") and hasattr(self.graphmod.obj,"SetSymmetryMatrices"):
         self.graphmod.doBiomolecule = self.doBiomolecule
         self.graphmod.obj.SetDrawSymmetry(1)
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()

  def toggleSymmetryStatus(self):
      #print "toggleSymmetryStatus"; sys.stdout.flush()
      self.doSymmetry = not self.doSymmetry
      #print "toggleSymmetryStatus status",self.doSymmetry; sys.stdout.flush()
      if hasattr(self,"graphmod") and hasattr(self.graphmod,"obj") and hasattr(self.graphmod.obj,"SetSymmetryMatrices"):
         #print "OK"; sys.stdout.flush()
         self.graphmod.doSymmetry = self.doSymmetry
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()
         """
         if self.doSymmetry:
            mats = self.graphmod.symmetry.GetSymmetryMatrices()
            self.graphmod.obj.SetSymmetryMatrices(mats)
         else:
            self.graphmod.obj.SetSymmetryMatrices([])
         if hasattr(MAINWINDOW(),"glWidget"):
           MAINWINDOW().glWidget.SetupSymmetry()
         import rebuild
         rebuild.UpdateDisplay()
         """

  def insideShiny(self):
      pm =  self.getDrawingStylePM()    
      return pm.get('inside_shiny')

  def insideColour(self):
      pm =  self.getDrawingStylePM()    
      return pm.get('inside_colour')

  def isDoubleSided(self):
      pm =  self.getDrawingStylePM()    
      return pm.get('double_side')

#--------------------------------------------------------------------
  def removeCustomDrawingStyle(self):
#--------------------------------------------------------------------
    pm = self.getDrawingStylePM()     
    pm.remove_dependent(self.handleDrawingStylePM)
    if hasattr(self,'drawingStylePM'):
      del self.drawingStylePM
    pm = self.getDrawingStylePM()     
    pm.add_dependent(self.handleDrawingStylePM)
    for gobj_name in ['graphmod']:
      if hasattr(self,gobj_name):
         getattr(self,gobj_name).setparams(parameters = pm)

#---------------------------------------------------------------------
  def __init__(self,parent='',visible=0,name='', \
               selparams= {'command':'all'}, colparams= {},styleparams={}, \
               params={},restore_from_version=0, **keywords):
#--------------------------------------------------------------------- 
    '''
    Initialise the surface display object
    '''

    if not SurfaceDispobj.ElectroColourScheme:
      createElectroParamsManager()
    # Request a signal if colour scheme is changed
    SurfaceDispobj.ElectroColourScheme.add_dependent(self.handleColourScheme)
    SurfaceDispobj.MapColourScheme.add_dependent(self.handleColourScheme)

    self.surface_style = 'solid'
    self.surface_type = 'normal'
    self.update_surface_style = 1
    self.surf = None
    self.done_electrostatic = 0
    self.done_map = 0
    self.reapply_selection = 1
    self.hide_on_selection_update = 1

    self.doSymmetry=False
    self.doBiomolecule = False

    #Switch off visibility on restore
    if params and self.hide_on_selection_update:
      params['visible'] = 0

    dataobj.DispObj.__init__(self,'SurfaceDispobj',name,parent= parent, \
          visible=visible,params=params)
    
    #Interface.__init__(self,contact_MolDisp=params.get('contact_MolDisp',''))


    # If the selparams are not defined (which means this
    # object is not been recreated on startup) set the
    # neighbourhood defaults more appropriate for surfaces
    if not selparams:
      selparams.update(SurfaceDispobj.selection_initialisation)
    else:
      if restore_from_version and restore_from_version[0] ==1 and restore_from_version[1]<108:
          selparams = model_selection.fix_v108(selparams)  
          styleparams = model_selection.fix_v108(styleparams)  

    self.SelHandle1 = model_selection.SelHandle(self.parent.name,selparams=selparams,dispobj_name=self.name)
    if not styleparams: styleparams = { 'same_as' : self.name }
    # Update to v2.0
    if styleparams.get('select','') == 'same_as' and not styleparams.get('same_as',''):
      styleparams['same_as'] = self.name

    self.SelHandle2 = model_selection.SelHandle(self.parent.name,selparams=styleparams,dispobj_name=self.name)

    if not colparams:
      colparams = {'colour_mode': 'electrostatic'}
    self.model_colour =model_colour.model_colour(colparams=colparams,MolData=self.parent,SelHandle=self.SelHandle1)
    self.loaded_maps = []
    
    self.update_context = 0

    # create a graphics object
    self.graphmod = surfobject(ui=self)
    BUILD().append(self.graphmod)
    self.graphmod.obj.SetAlpha(self.opacity)


    # Call the Dependency method to ensure that any dependents
    # of SurfaceDispobj know that this one has been created
    # (improbable that any such dependents exist!)
    self.update_dependents('create')

    if params.has_key('drawing_style'):
      # Force creating custom drawing stype PM
      pm = self.getDrawingStylePM(name=params['drawing_style'],custom=1)
    else:
      pm = self.getDrawingStylePM(name=params.get('drawing_style',''))
    pm.add_dependent(self.handleDrawingStylePM)

    dispobjs = self.parent.get_dispobj()
    for dispobj in dispobjs:
       if hasattr(dispobj,"masterCrystal") and dispobj.masterCrystal!='':
          self.setMasterCrystal(dispobj.masterCrystal)
          break

#-------------------------------------------------------------------
  def delete(self,**keywords):
#-------------------------------------------------------------------
    if hasattr(self,'surf') and self.surf: del self.surf
    if SurfaceDispobj.ElectroColourScheme:
      SurfaceDispobj.ElectroColourScheme.remove_dependent(self.handle_colour_change)
    self.closeDrawingStylePM()
    dataobj.DispObj.delete(self)

#---------------------------------------------------------------------------------
  def set_visibility ( self, toggle_visibility='', visibility='',*args,**keywords):
#---------------------------------------------------------------------------------
    # toggling visibility should force a redraw if no self.surf defined.
    # This should enable user to calc surface if had previously interrupted
    # or failed
    dataobj.DispObj.set_visibility( self, toggle_visibility=toggle_visibility, visibility=visibility )
    if not self.surf: self.update_surface_style = 1


#------------------------------------------------------------------
  def set_do_redraw(self):
#------------------------------------------------------------------
    self.update_surface_style = 1


  def set_reapply_selection(self,keep_visible=0):
    self.reapply_selection = 1
    if self.hide_on_selection_update and not keep_visible: self.visible = 0
    
#------------------------------------------------------------------
  def params(self,all=1):
#------------------------------------------------------------------
    pars = dataobj.DispObj.params(self,all=all)
    if hasattr(self,'drawingStylePM'):
      pars['drawing_style'] = self.drawingStylePM.name
    #for item in ['surface_style','contact_MolDisp']:
    for item in ['surface_style','surface_type']:
      pars[item] = getattr(self,item)
    return pars
    

#------------------------------------------------------------------
  def selparams(self,all=1):
#------------------------------------------------------------------
    return self.SelHandle1.getSaveParams(all=all)
  
#------------------------------------------------------------------
  def styleparams(self,all=1):
#------------------------------------------------------------------
    return self.SelHandle2.getSaveParams(all=all)

#---------------------------------------------------------------------
  def colparams(self,all=1):
#---------------------------------------------------------------------
    '''
    Bundle the colour parameters
    '''
    pars = self.model_colour.colparams(all=all)
    if hasattr(self,'map'): pars['map']=self.map
    return pars

#-----------------------------------------------------------------
  def update_data_status(self,shadowdisp):
#-----------------------------------------------------------------
    import utils
    rv = 0
    diffs = utils.dict_diffs(self.selparams(),shadowdisp.selparams)[1]
    #print "SurfaceDispobj.update_data_status selparams diffs",diffs
    if diffs:
      rv = 1
      self.SelHandle1.setSaveParams(diffs)
      self.set_reapply_selection(keep_visible=1)
      
    diffs = utils.dict_diffs(self.colparams(),shadowdisp.colparams)[1]
    #print "colparams diffs",diffs
    if diffs:
      rv = 1
      self.model_colour.set_colour(colparams=diffs)
        
    diffs =  utils.dict_diffs(self.styleparams(),shadowdisp.styleparams)[1]
    #print "Surface.update_data_status style diffs",diffs
    #print "self",self.styleparams()
    #print "shadowdisp",shadowdisp.styleparams
    if diffs:
      rv = 1
      self.SelHandle2.setSaveParams(diffs)
      

    rv,diffs,undiffs = utils.dict_diffs(self.params(),shadowdisp.params)
    #"Surface.update_data_status",diffs,undiffs
    if diffs.has_key('surface_type'):
      self.set_surface_type(diffs['surface_type'])
      self.set_do_redraw()
    if diffs.has_key('surface_style'):
      self.set_surface_style(diffs['surface_style'])
      self.set_do_redraw()
    if diffs.has_key('drawing_style'):
        #Add a drawing style PM
        pm = self.getDrawingStylePM(name=diffs['drawing_style'],custom=1)
        #print "diffs drawing_style",pm.name,pm.params
        pm.add_dependent(self.handleDrawingStylePM)
        if self.graphmod: self.graphmod.setparams(parameters=pm)
        self.set_do_redraw()
        HISTORY().update_gui.append(['add_PM',diffs['drawing_style']])
        rv = 1

    if undiffs.has_key('drawing_style'):
      HISTORY().update_gui.append(['delete_PM',undiffs['drawing_style']])
      self.closeDrawingStylePM()
      self.set_do_redraw()
      rv = 1

    rv = max(rv,dataobj.DispObj.update_data_status0(self,shadowdisp))
    if rv: self.update_gui.append(['update'])
    return rv

#------------------------------------------------------------------------
  def canBeTransparent(self):
#------------------------------------------------------------------------
    return 1

#------------------------------------------------------------------------
  def isLegendPossible(self):
#------------------------------------------------------------------------
    return 1

#------------------------------------------------------------------------
  def getLegend(self):
#------------------------------------------------------------------------
    return self.model_colour.getLegend()
  
#
#----------------------------------------------------------------------
  def set_colour ( self, colour='',colparams={},**keywords):
#-----------------------------------------------------------------------
    '''
    Handle colour related commands from the GUI
    '''
    colparams.update(keywords)
    self.model_colour.set_colour(colparams=colparams,one_colour=colour)

    #print "Surface set_colour sel.colour_mode colour_mode", self.colour_mode,co               
    if len(self.graphmod.obj.GetSurfacePrimitives())>0:
      self.graphmod.obj.forceRegenerateSurfaceArrays()
      if MAINWINDOW() and hasattr(MAINWINDOW(),"glWidget") and hasattr(MAINWINDOW().glWidget,"updateGL"):
         MAINWINDOW().glWidget.updateGL()
    self.update_dependents('colour')

#---------------------------------------------------------------------
  def openmapfile(self,ccp4mapfile='',exit='',**keywords):
#---------------------------------------------------------------------
    if ccp4mapfile and exit == '1':
      pass  

  

#---------------------------------------------------------------------
  def set_surface_type(self,surface_type='normal'):
#---------------------------------------------------------------------
    if surface_type != self.surface_type:
      self.set_reapply_selection()
      self.surface_type = surface_type

#---------------------------------------------------------------------
  def set_surface_style(self,surface_style='solid'):
#---------------------------------------------------------------------
    if surface_style != self.surface_style:
      self.set_do_redraw()
      self.surface_style = surface_style
    #print "set_surface_style", self.surface_style

#---------------------------------------------------------------
  def handle_icon(self,colour='',help='',centre_on='', \
                 loadsave='', listdata='', \
                 save_electro_file='',load_electro_file='',\
                  legend = '', **keywords):
#---------------------------------------------------------------
    '''
    Handle assorted icon menu input
    '''


    if legend:
       self.model_colour.create_colour_legend(legend='1')



    if loadsave:
      if loadsave == 'save_grasp':
        if not self.surf:
          MGGUI().WarningMessage('Need to calculate surface before saving')
          return
        else:
          title = 'Save surface as Grasp file'
          type = '_grasp_file'
          format = 'GRASP'
          filter = '*.grasp'
          unknown = '-unknown'
      elif loadsave == 'load_grasp':
        title = 'Load Grasp surface file'
        type = '_grasp_file'
        format = 'GRASP'
        filter = '*.grasp'
        unknown = ''
      elif loadsave == 'save_electro':
        if not self.done_electrostatic:
          return
        else:
          title = 'Save electrostatic as map file'
          type = '_map_file'
          format = 'MAP'
          filter = '*.map'
          unknown = '-unknown'
      elif loadsave == 'load_electro':
        title = 'Load electrostatic map file'
        type = '_map_file'
        format = 'MAP'
        filter = '*.map'
        unknown = ''
        
          
      

    elif save_electro_file and exit == 'ok':
      rv = self.surf.writePhiMap(save_electro_file)
      #print "writePhiMap rv ",rv

    elif load_electro_file and exit == 'ok':
      rv = self.surf.readPhiMapAndColourWithScheme(save_electro_file, \
          SurfaceDispobj.ElectroColourScheme.CColourScheme())


      '''
    elif filename and ['ok','1'].count(exit):
      import utils
      file = utils.get_filename(DIR_filename,filename)
      print "file",file
      if mode == 'save_grasp':
        if not self.surf:
          return
        rv = self.surf.writeGraspFile(file)
      elif mode == 'load_grasp':
        if self.surf: del self.surf
        import surface
        self.surf = surface.surface()
        self.surf.readGraspFile(file)
        # Get the surface drawn but nothing updated!
        self.update_surface_style = 1
        self.reapply_selection = 0
        self.update_context = 0
      
        
      elif mode == 'save_electro':
        if not self.surf or not self.done_electrostatic:
          return
        rv = self.surf.writePhiMap(file)
        print "writePhiMap rv ",rv
      elif mode == 'load_electro':
        rv = self.surf.readPhiMapAndColourWithScheme(file, \
            SurfaceDispobj.ElectroColourScheme.CColourScheme())
      '''
                                                            
#---------------------------------------------------------------------
  def list_data(self,filename='',overwrite='query',**keywords):
#---------------------------------------------------------------------
    if not self.surf: return
    listing = self.surf.report()

    if filename:
      import utils
      utils.saveFile(filename,text=text,warning=1,overwrite=overwrite)
    else:
      return listing
    
#------------------------------------------------------------------------
  def getDrawingStylePM(self,custom=0,name='',auto_save=0):
#------------------------------------------------------------------------
    if name and PM(name):
      self.drawingStylePM = PM(name)
      self.drawingStylePM.add_dependent(self.handleDrawingStylePM)
      return self.drawingStylePM     

    if custom and not hasattr(self,'drawingStylePM'):
      if not name: name = 'surface_drawing_style'+self.name
      PM('surface_drawing_style').remove_dependent(self.handleDrawingStylePM)
      self.drawingStylePM = createDrawingStylePM(name=name,copy_from='surface_drawing_style')
      self.drawingStylePM.add_dependent(self.handleDrawingStylePM)
      if auto_save: HISTORY().autoSave()

    if hasattr(self,'drawingStylePM'):
      return self.drawingStylePM
    else:
      return PM('surface_drawing_style')


#-----------------------------------------------------------------
  def handleDrawingStylePM(self,ParamsManager='',updated={},**keywords):
#-----------------------------------------------------------------
    '''
    User has changed drawing style
    '''
    if ParamsManager:
      # Changes to dot surface?
      if updated.has_key('line_width'):
        if self.surface_style=='mesh': self.update_surface_style = 1
      if updated.has_key('dot_spacing'):
        if self.surface_style=='dots': self.update_surface_style = 1
      if updated.has_key('dot_size'):
        if self.surface_style=='dots': self.update_surface_style = 1

      # Changes are to calculation parameters so need complete recalc of surface
      for item in ['context_cutoff','probe_radius','blend_edges','resolution_delta','surface_type']:
        if  updated.has_key(item): self.reapply_selection = 1

    #print "in SurfaceDispObj.handleDrawingStylePM",self.update_surface_style,self.reapply_selection

#------------------------------------------------------------------------
  def closeDrawingStylePM(self,reset=1):
#------------------------------------------------------------------------
    if hasattr(self,'drawingStylePM'):
      #print "closeDrawingStylePM",self.drawingStylePM.name
      self.drawingStylePM.remove_dependent(self.handleDrawingStylePM)
      self.drawingStylePM.delete()
      del self.drawingStylePM
      if reset:
        if self.graphmod: self.graphmod.setparams(parameters=PM('surface_drawing_style'))
        PM('surface_drawing_style').add_dependent(self.handleDrawingStylePM)

    
#-----------------------------------------------------------------
  def handleColourScheme(self,ColourSchemeManager='',**kw):
#-----------------------------------------------------------------
    #print 'SurfaceDispobj.handleColourScheme',ColourSchemeManager,kw
    if ColourSchemeManager == 'Surface_electro' and \
                              self.model_colour.colour_mode == 'electrostatic':
      self.model_colour.do_recolour = 1
    elif ColourSchemeManager == 'Surface_map' and self.model_colour.colour_mode == 'map':
      self.model_colour.do_recolour = 1

    import rebuild
    rebuild.UpdateDisplay()
    
      
#-----------------------------------------------------------------
  def clone(self,**keywords):
#-----------------------------------------------------------------
    '''
    Clone this object
    '''
    SurfaceDispobj(self.parent.name,visible=0, \
       selparams=self.selparams(),colparams=self.colparams(), \
                styleparams=self.styleparams())



#----------------------------------------------------------------
  def handle_moving_data(self,attribute='',master='',**keywords):
#----------------------------------------------------------------
    if attribute == 'transform':
      #self.set_visibility(visibility=0)
      self.reapply_selection=1
    elif attribute == 'reconfigure':
      self.reapply_selection=1
      
#------------------------------------------------------------------
  def draw (self ):
#------------------------------------------------------------------
    '''
    This method is called once at the end of every event loop
    and it should recalculate the surface if flags are set
    to indicate redrawing is necessary
    '''
    #print 'SurfaceDispobj.draw',self.visible,self.reapply_selection,self.model_colour.do_recolour,self.update_surface_style
    if not self.visible: return 0
    # Does it need redrawing?   
    if self.reapply_selection or self.update_context or not self.surf:
      update_surface = 3
    elif self.model_colour.do_recolour:
      update_surface = 2
    elif self.update_surface_style:
      update_surface = 1
    else:
      if self.do_update_opacity and self.graphmod:
        self.graphmod.obj.SetAlpha(self.opacity)
        self.do_rebuildGL.append('graphmod')
        self.do_update_opacity = 0
      return 0
    

    #print "SurfaceDispobj.draw update_surface",update_surface,self.SelHandle1.getNofAtoms(),self.SelHandle2.getNofAtoms(); sys.stdout.flush()
   
    import surface
    import mmdb2 as mmdb
    pm =  self.getDrawingStylePM()    
    surfHandle=None
    contextHandle=None

    self.surface_calc_rv = 0

    def restOfSurfaceDraw(surfHandle,contextHandle):
      print "restOfSurfaceDraw",self.surface_calc_rv
      if self.surface_calc_rv!=0:
          #The surface calc has failed or was interrupted
          del self.surf
          self.surf = None
          self.graphmod.setparams(surf=self.surf)
          self.graphmod.setparams(update=1)
          self.graphmod.rebuild()
          self.rebuildGLDisplayObject(MAINWINDOW())
          self.update_surface_style = 0
          MGGUI().WarningMessage("Surface calculation interrupted.\nRestart by toggling visiblility.\nThe calculation will be faster if the\nresolution is set to a larger value\nin the Surface Preferences")
          return 0
      elif update_surface > 2:
          print "Finished calculating surface"
          self.done_electrostatic = 0
          self.done_map = 0
          self.model_colour.do_recolour=max(1,self.model_colour.do_recolour)
          self.update_surface_style = 1

      if hasattr(self,"surf_temp"):
        self.surf = self.surf_temp
        del self.surf_temp

      # Surface colour
      if update_surface > 1:
        if self.model_colour.colour_mode == 'map':
          ColourScheme = SurfaceDispobj.MapColourScheme.CColourScheme()
          if not self.done_map:
            self.surf.loadMapAndColourWithScheme(self.colour_map, ColourScheme)
          else:
            self.surf.colourByScalarValue('map', ColourScheme)
        elif  self.model_colour.colour_mode == 'electrostatic':
          ColourScheme = SurfaceDispobj.ElectroColourScheme.CColourScheme()
          #print "electrostatic ColourScheme",ColourScheme
          if not self.done_electrostatic:
            #self.surf.evaluatePhiAndColourWithDefaultScheme(self.molHnd,self.selHnd)
            self.parent.molHnd.LoadCharge('surface_potential_charge')
            print self.parent.molHnd.PrintCharges()
            if contextHandle:
              self.surf.evaluatePhiAndColourWithScheme(self.parent.molHnd,
                  surfHandle.getSelHnd(),contextHandle.getSelHnd(),
                  ColourScheme,self.parent.contains_hydrogen)
            else:            
               self.surf.evaluatePhiAndColourWithScheme(self.parent.molHnd,
                  surfHandle.getSelHnd(),surfHandle.getSelHnd(),
                  ColourScheme,self.parent.contains_hydrogen)
             
            self.done_electrostatic = 1
          else:
            self.surf.colourByScalarValue('potential',ColourScheme)
        else:
          self.model_colour.CMolColour.SetSelHandle(surfHandle.getSelHnd())
          self.model_colour.apply_colour()
          #print "atomColourVector",self.model_colour.atomColourVector
          #print "Applying colour to surface.."
          self.surf.ColourSurface(self.parent.molHnd,surfHandle.getSelHnd(),self.model_colour.atomColourVector )
          #print "Done applying colour to surface"
  
      # Surface style
      if update_surface:
        #print "Update surface style..",pm.get('dot_size'),self.surface_style
        import surface
        alias = [ 'solid', 'dots','mesh']
        code_list = [surface.CCP4MG_SURFACE_SOLID,surface.CCP4MG_SURFACE_DOTS,surface.CCP4MG_SURFACE_MESH]
        self.surf.set_style(code_list[alias.index(self.surface_style)])
        #print "SurfaceDispObj.draw style",self.surface_style
  
        self.surf.SetDotSpacing (pm.get('dot_spacing'))
        self.surf.SetDotSize(pm.get('dot_size'))
        self.surf.SetLineWidth(pm.get('line_width'))
        
      if self.surf:
        self.graphmod.setparams(surf=self.surf,visible=1,update=1)
        #print "Drawing surface.."
        self.graphmod.rebuild()
        self.graphmod.obj.SetAlpha(self.opacity)
  
        #print "Done drawing surface.."
   
      # Cleanup
      if surfHandle: del surfHandle
      # Beware contextHandle may be set to self.SelHandle2
      if contextHandle: del contextHandle
  
      self.update_context = 0
      self.model_colour.do_recolour = 0
      self.reapply_selection = 0
      self.update_surface_style = 0
      if update_surface: self.do_rebuildGL.append('graphmod')
      #return update_surface

    if update_surface> 1:

      # Get the cleaned up atom selections - needed for recalculating and
      # recolouring
      surfHandle = model_selection.SelHandle(moldata_name=self.parent.name)
      surfHandle.setSelHnd(self.SelHandle1.getSelHnd())
      #print 'SurfaceDispobj.draw surfHandle.nAtoms',surfHandle.getNofAtoms()

      # Remove hydrogen atoms and close atoms from the surface selection
      surfHandle.cleanSelection(hydrogens=1,close=0.01)
      rv = surfHandle.cleanAltLocs(allow=1)
      
      if not self.SelHandle2.getSameAs():
        contextHandle= model_selection.SelHandle(moldata_name=self.parent.name)
        # Initialise context will the users selected context
        contextHandle.setSelHnd(self.SelHandle2.getSelHnd())
        # make sure to include all atoms in the original surface
        self.parent.molHnd.Select(contextHandle.getSelHnd(),mmdb.STYPE_ATOM,surfHandle.getSelHnd(),mmdb.SKEY_OR)
        # Limit to atoms around surfHandle to minimise calcs
        nselatoms,selAtoms =surfHandle.getSelIndex()
        self.parent.molHnd.SelectNeighbours(contextHandle.getSelHnd(),mmdb.STYPE_ATOM, \
            selAtoms,nselatoms,0.0,pm.get('context_cutoff'),mmdb.SKEY_AND)
        
      #Recalculate the surface if atom selection changed
      if update_surface > 2:
        import math
        
        if self.surf:
          self.graphmod.setparams(surf=None)
          self.graphmod.setparams(update=1)
          self.graphmod.rebuild()
          del self.surf
        self.surf=surface.surface()
        self.rebuildGLDisplayObject(MAINWINDOW())
          
        probe_radius = pm.get('probe_radius')
        resolution_delta = pm.get('resolution_delta')*math.pi/180.0        
        blend_edges =  pm.get('blend_edges')

        #print 'SurfaceDispobj.draw contextHandle',contextHandle, self.SelHandle2.getSameAs()
        def calcSurface(surfHandle,contextHandle):
          #self.surf_temp = surface.surface()
          if contextHandle:
            if self.surface_type == "vdw":
              self.surface_calc_rv = self.surf.calculate_vdw_with_context(self.parent.molHnd,surfHandle.getSelHnd(),contextHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))
            elif self.surface_type == "accessible":
              self.surface_calc_rv = self.surf.calculate_accessible_with_context(self.parent.molHnd,surfHandle.getSelHnd(),contextHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))
            else:
              self.surface_calc_rv = self.surf.calculate_with_context(self.parent.molHnd,surfHandle.getSelHnd(),contextHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))
          else:
            if self.surface_type == "vdw":
              self.surface_calc_rv = self.surf.calculate_vdw(self.parent.molHnd,surfHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))
            elif self.surface_type == "accessible":
              self.surface_calc_rv = self.surf.calculate_accessible(self.parent.molHnd,surfHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))
            else:
              self.surface_calc_rv = self.surf.calculate(self.parent.molHnd,surfHandle.getSelHnd(),probe_radius,resolution_delta,bool(blend_edges))

        if sys.platform == 'linux2':
            from PyQt4 import QtCore, QtGui
            import mginterrupt
            import UtilityThread
            mainwin = MAINWINDOW()
            self.interruptButton = QtGui.QToolButton(mainwin)
            self.interruptButton.setText("Interrupt surface calculation")
            self.interruptButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon);
            icondir = os.path.abspath(os.path.join(os.path.dirname(__file__),'..','..','qticons'))
            icon = QtGui.QIcon(os.path.join(icondir,"stop_sign_128x128.png"))
            self.interruptButton.setIcon(icon)
            mainwin.connect(self.interruptButton,QtCore.SIGNAL("clicked(bool)"),functools.partial(mginterrupt.mginterrupt.SetStatus,1))
            while hasattr(self,"surfaceThread") and (self.surfaceThread.isRunning() or not self.surfaceThread.isFinished()):
              mginterrupt.mginterrupt.SetStatus(1)
              self.surfaceThread.wait()
            mginterrupt.mginterrupt.SetStatus(0)
            self.surfaceThread = UtilityThread.UtilityThread(functools.partial(calcSurface,surfHandle,contextHandle))
            mainwin.connect(self.surfaceThread,QtCore.SIGNAL("finished()"), functools.partial(mainwin.statusBar().removeWidget,self.interruptButton))
            mainwin.connect(self.surfaceThread,QtCore.SIGNAL("finished()"),functools.partial(restOfSurfaceDraw,surfHandle,contextHandle))
            self.surfaceThread.start()
            mainwin.statusBar().addPermanentWidget(self.interruptButton)
        else:
          calcSurface(surfHandle,contextHandle)
          restOfSurfaceDraw(surfHandle,contextHandle)
      else:
        restOfSurfaceDraw(surfHandle,contextHandle)
    else:
      restOfSurfaceDraw(surfHandle,contextHandle)

import graphicsmodel
import cprim
import time


#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
class surfobject(graphicsmodel.graphicsmodel):
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# The graphics object which is responsible for generating a
# graphical representation from the input

   def delete(self):
     #print "surfobject.delete"
     pass

   def setparams(self,**keywords):
      for key in keywords.keys():
         setattr(self,key,keywords[key])
           
   def DrawUnitCell(self,draw_unit_cell=0,xshift=0,yshift=0,zshift=0):
       self.draw_unit_cell = draw_unit_cell
       self.xshifts = xshift
       self.yshifts = yshift
       self.zshifts = zshift

   def setparams(self,**keywords):
     graphicsmodel.graphicsmodel.setparams(self,**keywords)
     for key in keywords.keys():
         if key == 'symmetry_radius':
            self.symmetry_radius = float(keywords[key])
         elif key == 'draw_symmetry':
            self.obj.SetDrawSymmetry(int(keywords[key]))

   def SetupSymmetry(self,point,extents,**keywords):
     #print "surfobject.SetupSymmetry"
     if self.doBiomolecule and self.ui and hasattr(self.ui,"parent") and hasattr(self.ui.parent,"molHnd"):
         if self.obj.GetNumSymmetryMatrices()>0:
           return
         matrices = self.ui.parent.molHnd.GetBiomoleculeAsMatrices(0,1);
         if len(matrices)==0:
           return
         import pygl_coord
         numbers = pygl_coord.IntVector(len(matrices))
         self.obj.SetDrawSymmetry(1)
         self.obj.SetSymmetryMatrices(matrices)
         self.obj.SetSymmetryMatrixNumbers(numbers)
         self.obj.SetDrawSymmetryColoured(0)
         return
     elif self.draw_symmetry == 0 or not self.doSymmetry:
        self.obj.SetSymmetryMatrices([])
        return
     self.setup_symmetry(point,extents)

   def setup_symmetry(self,p,extents):
     import pygl_coord
     import symmetry

     #print "surfobject.setup_symmetry", p, extents
     #print self.draw_unit_cell,self.xshifts,self.yshifts,self.zshifts,self.symmetry_radius
     point = -pygl_coord.Cartesian(p)

     tl = -pygl_coord.Cartesian(extents[0])
     tr = -pygl_coord.Cartesian(extents[1])
     br = -pygl_coord.Cartesian(extents[2])
     bl = -pygl_coord.Cartesian(extents[3])

     if self.ui and hasattr(self.ui,"parent") and hasattr(self.ui.parent,"molHnd") and self.draw_unit_cell == 0 or self.draw_unit_cell != self.old_draw_unit_cell or self.xshifts != self.old_xshifts or self.yshifts != self.old_yshifts or self.zshifts != self.old_zshifts:
       surfHandle = model_selection.SelHandle(moldata_name=self.ui.parent.name)
       selHnd =  surfHandle.getSelHnd()
       self.symmetry = symmetry.Symmetry(self.ui.parent.molHnd,selHnd,point,tl,tr,br,bl,self.draw_unit_cell,self.xshifts,self.yshifts,self.zshifts,self.symmetry_radius);
       self.obj.SetSymmetryMatrices(self.symmetry.GetSymmetryMatrices())
       self.obj.SetUnitCell(self.symmetry.GetUnitCell())
       self.obj.SetSymmetryMatrixNumbers(self.symmetry.GetSymmetryMatrixNumbers())
       if surfHandle: del surfHandle
     self.old_xshifts = self.xshifts
     self.old_yshifts = self.yshifts
     self.old_zshifts = self.zshifts
     self.old_draw_unit_cell = self.draw_unit_cell


   def __init__(self,ui='',visible=1,surf=None):

     self.update = 0
     self.surf = surf
     self.graphicsmodel_init(ui=ui)
     self.obj = cprim.Displayobject()
     self.obj.SetAnchored(True)

     self.ui = ui

     self.draw_symmetry = 0
     self.draw_unit_cell = 0
     self.old_draw_unit_cell = 0
     self.xshifts = 0
     self.yshifts = 0
     self.zshifts = 0
     self.old_xshifts = -9999
     self.old_yshifts = -9999
     self.old_zshifts = -9999
     self.symmetry_radius = 75
     self.symmetry_labels = 0

     self.doSymmetry = False
     self.doBiomolecule = False

   def build_primitives(self):
      """
      Generate surface graphical object
      """
      #print "build_primitives",self.update,self.surf,self.obj
      if not self.update: return
      if self.obj:
        self.obj.clear_prims()
      if not self.surf:
        return
      import surface,math      
      surface.add_surface(self.surf,self.obj)
      self.update = 0
      


#================================================================================================
# 

def createDrawingStylePM(name='surface_drawing_style',copy_from=''):
  import services
  pm = services.ParamsManager( name=name,
              title='Surface drawing style',
              picture_definition = 1,
              copy_from=copy_from,
              apply_mode = 'on_request',
              gui=[ 'probe_radius','context_cutoff','resolution_delta','blend_edges','dot_spacing','dot_size','line_width','double_side','inside_colour','inside_shiny'], \
              default = { \
                        'context_cutoff': 6.0,
                        'probe_radius' : 1.5, 
                        'blend_edges':0, 
                        'resolution_delta' : 30,
                        'dot_spacing' : 0.1,
                        'dot_size' : 1.0, \
                        'line_width' : 1.0, \
                        'double_side':0, 
                        'inside_colour':'default', 
                        'inside_shiny':1, 
                        'warning_limit': 1000, \
                        'warning_on': 1 }, \
              definition={ 'context_cutoff': dict(type=float,label='Cutoff distance for context atoms',style='spinbox',max=20.0,min=0.0,step=0.5),
                           'probe_radius' :  dict(type=float, label='Surface probe radius(0-3)',style='spinbox',max=4.0,min=0.0,step=0.2),
                           'resolution_delta' :  dict(type=float, label='Resolution in degrees(2-89)',style='spinbox',max=89,min=2,step=1),
                           'blend_edges' :  dict(type='bool',label='Blend colour borders',style='checkbox'),
                           'dot_spacing' :  dict(type=float, label='Maximum dot separation(0.005-0.5)',style='spinbox',max=0.5,min=0.005,step=0.01),
                           'dot_size' :  dict(type=float, label='Dot size(1.0-3.0)',style='spinbox',max=3,min=1,step=1),
                           'line_width' :  dict(type=float, label='Mesh line width',style='spinbox',max=5,min=1,step=1),
                           'double_side' :  dict(type='bool',label='Draw both sides of surface',style='checkbox'),
                           'inside_colour' :  dict(type=str,label='Inside colour',style='colourcombo'),
                           'inside_shiny' :  dict(type='bool',label='Inside surface shiny',style='checkbox'),
                           'warning_limit' :  dict(type=int,label='Warn for calculation with more atoms',style='checkbox'),
                           'warning_on' :  dict(type='bool',label='Warning for long calculation',style='checkbox')
                     },
             help = 'surfaces')
  #print "createParamsManager",name,pm
  return pm

def createElectroParamsManager():
  if not SurfaceDispobj.ElectroColourScheme:
    import services
    SurfaceDispobj.ElectroColourScheme = services.ColourSchemeManager(
            name = 'Surface_electro',
            ranges = [0.0,-0.5,0.0,0.5,.0],
            colours = ['red','red','white','blue','blue'],
            mode = 'float',
            definitions = { 'ranges' : dict(type='float',max=5.0,min=-5.0,step=0.05,style='spinbox') },
            title = 'Electrostatic potential')
    SurfaceDispobj.MapColourScheme = services.ColourSchemeManager(
            name = 'Surface_map',
            ranges = [0.0,-0.5,0.0,0.5,.0],
            colours = ['red','red','white','blue','blue'],
            title = 'Surface map colouring')
  return SurfaceDispobj.ElectroColourScheme