#A* -------------------------------------------------------------------
#B* This file contains source code for the PyMOL computer program
#C* Copyright (c) Schrodinger, LLC. 
#D* -------------------------------------------------------------------
#E* It is unlawful to modify or remove this copyright notice.
#F* -------------------------------------------------------------------
#G* Please see the accompanying LICENSE file for further information. 
#H* -------------------------------------------------------------------
#I* Additional authors of this source file include:
#-* 
#-* 
#-*
#Z* -------------------------------------------------------------------

from __future__ import print_function

if __name__=='pymol.querying':

    import time
    from . import selector
    import pymol
    cmd = __import__("sys").modules["pymol.cmd"]
    from .cmd import _cmd,lock,unlock,Shortcut, \
          _feedback,fb_module,fb_mask,is_list, \
          DEFAULT_ERROR, DEFAULT_SUCCESS, _raising, is_ok, is_error

    def auto_measure(_self=cmd):
        lst = _self.get_names("selections")
        if "pk1" in lst:
            if "pk2" in lst:
                if "pk3" in lst:
                    if "pk4" in lst:
                        _self.dihedral()
                    else:
                        _self.angle()
                else:
                    _self.distance()
        _self.unpick()   

    def get_volume_field(objName, state=1, copy=1, _self=cmd):
        '''
DESCRIPTION

    EXPERIMENTAL AND SUBJECT TO CHANGE - DO NOT USE
    API only. Get the raw data of a map or volume object.

ARGUMENTS

    objName = str: object name

    state = int: state index {default: 1}

    copy = 0/1: {default: 1} WARNING: only use copy=0 if you know what you're
    doing. copy=0 will return a numpy array which is a wrapper of the internal
    memory. If the internal memory gets freed or reallocated, this wrapper
    will become invalid.
        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _self._cmd.get_volume_field(_self._COb, objName, int(state) - 1, int(copy))
        finally:
            _self.unlock(r,_self)
        return r

    def get_volume_histogram(objName, bins=64, range=None, _self=cmd):
        '''
DESCRIPTION

    API ONLY.  Get min, max, mean, stdev and histogram of a map or volume
    object as a list of length bins + 4.
        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _self._cmd.get_volume_histogram(_self._COb,objName,
                    int(bins), range or (0., 0.))
        finally:
            _self.unlock(r,_self)
        return r

    def get_unused_name(prefix="tmp",alwaysnumber=1,_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_unused_name(_self._COb, prefix, alwaysnumber)
        finally:
            _self.unlock(r,_self)
        return r

    def get_modal_draw(_self=cmd,quiet=1):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_modal_draw(_self._COb)
        finally:
            _self.unlock(r,_self)
        return r

    def get_drag_object_name(_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_drag_object_name(_self._COb)
        finally:
            _self.unlock(r,_self)
        return r

    def get_object_matrix(object,state=1, incl_ttt=1, _self=cmd):
        '''
DESCRIPTION

    "get_object_matrix" is an unsupported command that may have
    something to do with querying the transformation matrices
    associated with an object
        '''
        r = DEFAULT_ERROR
        object = str(object)
        try:
            _self.lock(_self)   
            r = _cmd.get_object_matrix(_self._COb,str(object), int(state)-1, int(incl_ttt))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_object_ttt(object, quiet=1, _self=cmd):
        '''
DESCRIPTION

    "get_object_ttt" is an unsupported command
        '''
        quiet = int(quiet)
        with _self.lockcm:
            r = _cmd.get_object_ttt(_self._COb, str(object), -1, quiet)
        if not quiet:
            if r is None:
                print('TTT is None')
            else:
                for i in range(4):
                    if i == 3:
                        print('TTT ---------------------------+---------')
                    print('TTT %8.2f %8.2f %8.2f | %8.2f' % tuple(r[i * 4:i * 4 + 4]))
        return r

    def get_object_list(selection="(all)", quiet=1, _self=cmd):
        '''
        
DESCRIPTION

    "get_object_list" is an unsupported command that may have
    something to do with querying the objects covered by a selection.
    '''
        
        r = DEFAULT_ERROR
        selection = selector.process(selection)
        try:
            _self.lock(_self)
            r = _cmd.get_object_list(_self._COb,str(selection))
            if not quiet:
                if(is_list(r)):
                    print(" get_object_list: ",str(r))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_symmetry(selection="(all)",state=-1,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_symmetry" can be used to obtain the crystal
    and spacegroup parameters for a molecule or map.

USAGE

    get_symmetry object-name-or-selection

PYMOL API

    cmd.get_symmetry(string selection, int state, int quiet)


        '''
        r = DEFAULT_ERROR
        selection = selector.process(selection)
        try:
            _self.lock(_self)
            r = _cmd.get_symmetry(_self._COb,str(selection),int(state))
            if not quiet:
                if(is_list(r)):
                    if(len(r)):
                        print(" get_symmetry: A     = %7.3f B    = %7.3f C     = %7.3f"%tuple(r[0:3]))
                        print(" get_symmetry: Alpha = %7.3f Beta = %7.3f Gamma = %7.3f"%tuple(r[3:6]))
                        print(" get_symmetry: SpaceGroup = %s"%r[6])
                    else:
                        print(" get_symmetry: No symmetry defined.")
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_title(object, state, quiet=1, _self=cmd):
        '''
DESCRIPTION

    "get_title" retrieves a text string to the state of a particular
    object which will be displayed when the state is active.

USAGE

    set_title object, state

PYMOL API

    cmd.set_title(string object, int state, string text)

    '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_title(_self._COb,str(object),int(state)-1)
            if not quiet:
                if r!=None:
                    print(" get_title: %s"%r)      
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r


    def angle(name=None, selection1="(pk1)", selection2="(pk2)", 
	      selection3="(pk3)", mode=None, label=1, reset=0, 
	      zoom=0, state=0, quiet=1, _self=cmd):

        '''
DESCRIPTION

    "angle" shows the angle formed between any three atoms.

USAGE

    angle [ name [, selection1 [, selection2 [, selection3 ]]]]

NOTES

    "angle" alone will show the angle angle formed by selections (pk1),
    (pk2), (pk3) which can be set using the "PkAt" mouse action
    (typically, Ctrl-middle-click)

PYMOL API

    cmd.angle(string name, string selection1, string selection2,
              string selection3)

SEE ALSO

    distance, dihedral
    '''
        
        r = DEFAULT_SUCCESS
        if selection1=="(pk1)":
            if "pk1" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):
                    print("cmd-Error: The 'pk1' selection is undefined.")
                r = DEFAULT_ERROR
        if selection2=="(pk2)":
            if "pk2" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk2' selection is undefined.")
                r = DEFAULT_ERROR
        if selection3=="(pk3)":
            if "pk3" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk3' selection is undefined.")
                r = DEFAULT_ERROR
        if is_ok(r):
            r = DEFAULT_ERROR
            
            # if unlabeled, then get next name in series

            if name!=None:
                nam=name
            else:
                try:
                    _self.lock(_self)
                    cnt = _self.get_setting_int("dist_counter") + 1
                    r = _self.set("dist_counter", cnt)
                    nam = "angle%02.0f" % cnt
                finally:
                    _self.unlock(r,_self)

            # defaults
            if mode == None:
                mode = 0
            # preprocess selections
            selection1 = selector.process(selection1)
            selection2 = selector.process(selection2)
            selection3 = selector.process(selection3)
            # now do the deed
            try:
                _self.lock(_self)
                if selection2!="same":
                    selection2 = "("+selection2+")"
                if selection3!="same":
                    selection3 = "("+selection3+")"
                r = _cmd.angle(_self._COb,str(nam),"("+str(selection1)+")",
                               str(selection2),
                               str(selection3),
                               int(mode),int(label),int(reset),
                               int(zoom),int(quiet),int(state)-1)
            finally:
                _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def dihedral(name=None, selection1="(pk1)", selection2="(pk2)",
		 selection3="(pk3)", selection4="(pk4)", mode=None,
		 label=1, reset=0, zoom=0, state=0, quiet=1, _self=cmd):
        '''
DESCRIPTION

    "dihedral" shows dihedral angles formed between any four atoms.

USAGE

    dihedral [ name [, selection1 [, selection2 [, selection3 [, selection4 ]]]]]


NOTES

    "dihedral" alone will show the dihedral angle formed by selections
    (pk1), (pk2), (pk3), and (pk4), which can be set using the "PkAt"
    mouse action (typically, Ctrl-middle-click)

PYMOL API

    cmd.dihedral(string name, string selection1, string selection2,
                 string selection3, string selection4)

SEE ALSO

    distance, angle
    '''
        r = DEFAULT_SUCCESS      
        if selection1=="(pk1)":
            if "pk1" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):
                    print("cmd-Error: The 'pk1' selection is undefined.")
                r = DEFAULT_ERROR
        if selection2=="(pk2)":
            if "pk2" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk2' selection is undefined.")
                r = DEFAULT_ERROR
        if selection3=="(pk3)":
            if "pk3" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk3' selection is undefined.")
                r = DEFAULT_ERROR
        if selection3=="(pk4)":
            if "pk4" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk4' selection is undefined.")
                r = DEFAULT_ERROR
        if is_ok(r):
            r = DEFAULT_ERROR
            # if unlabeled, then get next name in series

            if name!=None:
                nam=name
            else:
                try:
                    _self.lock(_self)
                    cnt = _self.get_setting_int("dist_counter") + 1
                    r = _self.set("dist_counter", cnt)
                    nam = "dihedral%02.0f" % cnt
                finally:
                    _self.unlock(r,_self)

            # defaults
            if mode == None:
                mode = 0
            # preprocess selections
            selection1 = selector.process(selection1)
            selection2 = selector.process(selection2)
            selection3 = selector.process(selection3)
            selection4 = selector.process(selection4)
            # now do the deed
            try:
                _self.lock(_self)
                if selection2!="same":
                    selection2 = "("+selection2+")"
                if selection3!="same":
                    selection3 = "("+selection3+")"
                if selection4!="same":
                    selection4 = "("+selection4+")"
                r = _cmd.dihedral(_self._COb,str(nam),"("+str(selection1)+")",
                                  str(selection2),
                                  str(selection3),
                                  str(selection4),
                                  int(mode),int(label),
                                  int(reset),int(zoom),
                                  int(quiet),int(state)-1)
            finally:
                _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r
        
    def distance(name=None, selection1="(pk1)", selection2="(pk2)", 
		 cutoff=None, mode=None, zoom=0, width=None, length=None,
                 gap=None, label=1, quiet=1, reset=0, state=0, _self=cmd):
	
        '''
DESCRIPTION

    "distance" creates a new distance object between two selections.

USAGE
    
    distance [name [, selection1 [, selection2 [, cutoff [, mode ]]]]]

ARGUMENTS

    name = string: name of the distance object to create

    selection1 = string: first atom selection

    selection2 = string: second atom selection

    cutoff = float: longest distance to show 
    
    mode = 0: all interatomic distances

    mode = 1: only bond distances

    mode = 2: only show polar contact distances

    mode = 3: like mode=0, but use distance_exclusion setting

    mode = 4: distance between centroids (does not support
              dynamic_measures; new in PyMOL 1.8.2)

EXAMPLES

    distance mydist, 14/CA, 29/CA

    distance hbonds, all, all, 3.2, mode=2

NOTES

    The distance wizard makes measuring distances easier than using
    the "dist" command for real-time operations.

    "dist" alone will show distances between selections (pk1) and (pk1),
    which can be set using the PkAt mouse action (usually CTRL-middle-click).

PYMOL API

    cmd.distance(string name, string selection1, string selection2,
                 string cutoff, string mode )

    '''
        # handle unnamed distance
        r = DEFAULT_SUCCESS
        if name!=None:
            if len(name):
                if name[0]=='(' or ' ' in name or '/' in name: # we're one argument off...
                    if cutoff!=None:
                        mode = cutoff
                    if selection2!="(pk2)":
                        cutoff = selection2
                    if selection1!="(pk1)":
                        selection2 = selection1
                    selection1=name
                    name = None

        if selection1=="(pk1)":
            if "pk1" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):
                    print("cmd-Error: The 'pk1' selection is undefined.")
                r = DEFAULT_ERROR
        if selection2=="(pk2)":
            if "pk2" not in _self.get_names('selections'):
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: The 'pk2' selection is undefined.")
                r = DEFAULT_ERROR
        if is_ok(r):
            r = DEFAULT_ERROR

            # if unlabeled, then get next name in series

            if name!=None:
                nam=name
            else:
                try:
                    _self.lock(_self)
                    cnt = _self.get_setting_int("dist_counter") + 1
                    r = _self.set("dist_counter", cnt)
                    nam = "dist%02.0f" % cnt
                finally:
                    _self.unlock(r,_self)

            # defaults
            if mode == None:
                mode = 0
            if cutoff == None:
                cutoff = -1.0
            # preprocess selections
            selection1 = selector.process(selection1)
            selection2 = selector.process(selection2)
            # now do the deed
            try:
                _self.lock(_self)
                if selection2!="same":
                    selection2 = "("+selection2+")"
                r = _cmd.dist(_self._COb,str(nam),"("+str(selection1)+")",
                              str(selection2),int(mode),float(cutoff),
                              int(label),int(quiet),int(reset),
                              int(state)-1,int(zoom))
                if width!=None:
                    _self.set("dash_width",width,nam)
                if length!=None:
                    _self.set("dash_length",length,nam)
                if gap!=None:
                    _self.set("dash_gap",gap,nam)
            finally:
                _self.unlock(r,_self)
        if (r<0.0) and (not quiet):
            # a negative value is an warning signal from PyMOL...
            r = DEFAULT_ERROR
        if _raising(r,_self): raise pymol.CmdException
        return r

    # LEGACY support for cmd.dist
    dist = distance

    def get_povray(_self=cmd):
        '''
DESCRIPTION

    "get_povray" returns a tuple corresponding to strings for a PovRay
    input file.

PYMOL API

    cmd.get_povray()

        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_povray(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_idtf(quiet=1, _self=cmd):
        '''
DESCRIPTION

    "get_idft" is under development, but should eventually return an
    idtf file containing multiple objects and scenes.

PYMOL API

    cmd.idtf()

        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_idtf(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException

        if not quiet:
            fov = _self.get_setting_float("field_of_view")
            dist = _self.get_view()[11]
            print(" 3Daac=%3.1f, 3Droll=0, 3Dc2c=0 0 1, 3Droo=%1.2f, 3Dcoo=0 0 %1.2f" % (fov, -dist, dist))

        return r

    def get_mtl_obj(_self=cmd):
        '''
DESCRIPTION

    NOTE: this is an incomplete and unsupported feature.

    "get_mtl_obj" returns a tuple containing mtl and obj input files
    for use with Maya.

PYMOL API

    cmd.get_obj_mtl()

        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_mtl_obj(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r
    
    def get_version(quiet=1,_self=cmd):
        '''
DESCRIPTION
 
    "get_version" returns a tuple of length six containing text,
    floating point, and integer representations of the current PyMOL
    version number, build date as unix timestamp, GIT SHA and SVN
    code revision so far available.
   
PYMOL API

    cmd.get_version(int quiet)

        '''
        # get_version doesn't need the _COb and doesn't require a lock
        r = _cmd.get_version()
        if _raising(r,_self):
            raise pymol.CmdException
        else:
            quiet = int(quiet)
            if quiet < 1 and _feedback(fb_module.cmd, fb_mask.results, _self):
                import re
                p = pymol.get_version_message(r)
                print(re.sub(r'^', ' ', p, re.M))
                if quiet < 0:
                    if r[3]:
                        print(' build date:', time.strftime('%c %Z', time.localtime(r[3])))
                    if r[4]:
                        print(' git sha:', r[4])
        return r

    def get_vrml(version=2,_self=cmd): 
        '''
DESCRIPTION

    "get_vrml" returns a VRML2 string representing the content
    currently displayed.

PYMOL API

    cmd.get_vrml()

        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)   
            r = _cmd.get_vrml(_self._COb,int(version))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_collada(version=2, _self=cmd):
        '''
DESCRIPTION

    "get_collada" returns a COLLADA string representing the content
    currently displayed.

PYMOL API

    cmd.get_collada()

        '''
        r = DEFAULT_ERROR
        with _self.lockcm:
            r = _cmd.get_collada(_self._COb,int(version))
        if _raising(r,_self): raise pymol.CmdException
        return r


    def count_states(selection="(all)", quiet=1, _self=cmd):
        '''
DESCRIPTION

    "count_states" returns the number of states in the selection.

USAGE

    count_states
    
PYMOL API

    cmd.count_states(string selection)

SEE ALSO

    frame
    '''
        # preprocess selection
        selection = selector.process(selection)
        #
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.count_states(_self._COb,selection)
        finally:
            _self.unlock(r,_self)
        if is_ok(r):
            if not quiet:
                print(" cmd.count_states: %d states."%r)            
        if _raising(r,_self): raise pymol.CmdException
        return r
    
    def get_movie_length(quiet=1, images=-1, _self=cmd):
        '''
DESCRIPTION

    "get_movie_length" returns the number of frames explicitly defined
    in the movie, not including molecular states.

PYMOL API

    cmd.count_frames()

SEE ALSO

    frame, count_states, count_frames
    '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_movie_length(_self._COb)
            if r<0:
                if images==0:
                    r = 0
                elif images<0:
                    r = -r
            if images == 1:
                if r>0:
                    r = 0
            if r>=0 and not quiet:
                print(" cmd.get_movie_length: %d frames"%r)      
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def count_frames(quiet=1, _self=cmd):
        '''
DESCRIPTION

    "count_frames" returns the number of frames defined for the PyMOL
    movie.

USAGE

    count_frames
    
PYMOL API

    cmd.count_frames()

SEE ALSO

    frame, count_states
    '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.count_frames(_self._COb)
            if not quiet: print(" cmd.count_frames: %d frames"%r)      
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def export_dots(object,state,_self=cmd):  
        '''
DESCRIPTION

    "export_dots" is an old unsupported command that may have
    something to do with returning the coordinates of the dot
    representation back to the Python layer.

    '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.export_dots(_self._COb,object,int(state)-1)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def overlap(selection1, selection2, state1=1, state2=1, adjust=0.0, quiet=1, _self=cmd):
        '''
DESCRIPTION

    "overlap" is an unsupported command that may have something to do
    with measuring the total amount of van der Waals overlap between
    two selections.

NOTES

    For best performance, use the smaller molecule as selection 1.
    
    '''
        
        # preprocess selections
        selection1 = selector.process(selection1)
        selection2 = selector.process(selection2)
        #
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.overlap(_self._COb,str(selection1),str(selection2),
                                  int(state1)-1,int(state2)-1,
                                  float(adjust))
            if not quiet: print(" cmd.overlap: %5.3f Angstroms."%r)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_movie_locked(_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_movie_locked(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_object_color_index(name,_self=cmd):
        name = str(name)
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_object_color_index(_self._COb,name)
        finally:
            _self.unlock(r,_self)      
#        if _raising(r,_self): raise pymol.CmdException
        return r
    
    def get_color_tuple(name,mode=0,_self=cmd):
        name=str(name)
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_color(_self._COb,name,mode)
            if r==None:
                if _feedback(fb_module.cmd,fb_mask.errors,_self):         
                    print("cmd-Error: Unknown color '%s'."%name)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_color_indices(all=0,_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            if all:
                r = _cmd.get_color(_self._COb,'',2)
            else:
                r = _cmd.get_color(_self._COb,'',1)            
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_color_index(color,_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_color(_self._COb,str(color),3)
        finally:
            _self.unlock(r,_self)
        return r
            
    def get_renderer(quiet=1, _self=cmd):
        '''
DESCRIPTION

    Prints OpenGL renderer information.
        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_renderer(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException

        if not int(quiet):
            print(" OpenGL graphics engine:")
            print("  GL_VENDOR:   ", r[0])
            print("  GL_RENDERER: ", r[1])
            print("  GL_VERSION:  ", r[2])

        return r

    def get_phipsi(selection="(name CA)",state=-1,_self=cmd):
        # preprocess selections
        selection = selector.process(selection)
        #   
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_phipsi(_self._COb,"("+str(selection)+")",int(state)-1)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_atom_coords(selection, state=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_atom_coords" returns the 3D coordinates of a single atom.
    
    '''
        # low performance way to get coords for a single atom
        r = []
        selection = selector.process(selection)
        try:
            _self.lock(_self)
            r = _cmd.get_atom_coords(_self._COb,str(selection),int(state)-1,int(quiet))
        finally:
            _self.unlock(r,_self)
        if r==None:
            if _self._raising(_self=_self): raise pymol.CmdException
        elif not quiet:
            for a in r:
                print(" cmd.get_atom_coords: [%8.3f,%8.3f,%8.3f]"%(a))
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_coords(selection='all', state=1, quiet=1, _self=cmd):
        '''
DESCRIPTION

    API only. Get selection coordinates as numpy array.

ARGUMENTS

    selection = str: atom selection {default: all}

    state = int: state index or all states if state=0 {default: 1}
        '''
        selection = selector.process(selection)
        with _self.lockcm:
            r = _cmd.get_coords(_self._COb, selection, int(state) - 1)
            return r

    def get_coordset(name, state=1, copy=1, quiet=1, _self=cmd):
        '''
DESCRIPTION

    API only. Get object coordinates as numpy array.

ARGUMENTS

    selection = str: atom selection {default: all}

    state = int: state index {default: 1}

    copy = 0/1: {default: 1} WARNING: only use copy=0 if you know what you're
    doing. copy=0 will return a numpy array which is a wrapper of the internal
    coordinate set memory. If the internal memory gets freed or reallocated,
    this wrapper will become invalid.
        '''
        with _self.lockcm:
            r = _cmd.get_coordset(_self._COb, name, int(state) - 1, int(copy))
            return r

    
    def get_position(quiet=1, _self=cmd):
        '''
DESCRIPTION

    "get_position" returns the 3D coordinates of the center of the
    viewer window.

    '''
        
        try:
            _self.lock(_self)
            r = _cmd.get_position(_self._COb)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self):
            raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_position: [%8.3f,%8.3f,%8.3f]"%(r[0],r[1],r[2]))
        return r

    def get_distance(atom1="pk1",atom2="pk2",state=-1,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_distance" returns the distance between two atoms.  By default, the
    coordinates used are from the current state, however an alternate
    state identifier can be provided.

USAGE

    get_distance atom1, atom2, [,state ]

EXAMPLES

    get_distance 4/n,4/c
    get_distance 4/n,4/c,state=4
    
PYMOL API

    cmd.get_distance(atom1="pk1",atom2="pk2",state=-1)

        '''
        r = DEFAULT_ERROR
        # preprocess selections
        atom1 = selector.process(atom1)
        atom2 = selector.process(atom2)
        #   
        r = None
        try:
            _self.lock(_self)
            r = _cmd.get_distance(_self._COb,str(atom1),str(atom2),int(state)-1)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self):
            raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_distance: %5.3f Angstroms."%r)
        return r

    def get_angle(atom1="pk1",atom2="pk2",atom3="pk3",state=-1,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_angle" returns the angle between three atoms.  By default, the
    coordinates used are from the current state, however an alternate
    state identifier can be provided.

USAGE

    get_angle atom1, atom2, atom3, [,state ]

EXAMPLES

    get_angle 4/n,4/c,4/ca
    get_angle 4/n,4/c,4/ca,state=4

PYMOL API

    cmd.get_angle(atom1="pk1",atom2="pk2",atom3="pk3",state=-1)

        '''
        # preprocess selections
        atom1 = selector.process(atom1)
        atom2 = selector.process(atom2)
        atom3 = selector.process(atom3)
        #   
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_angle(_self._COb,str(atom1),str(atom2),str(atom3),int(state)-1)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self):
            raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_angle: %5.3f degrees."%r)
        return r
        
    def get_dihedral(atom1="pk1",atom2="pk2",atom3="pk3",atom4="pk4",state=-1,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_dihedral" returns the dihedral angle between four atoms.  By
    default, the coordinates used are from the current state, however
    an alternate state identifier can be provided.

    By convention, positive dihedral angles are right-handed
    (looking down the atom2-atom3 axis).

USAGE

    get_dihedral atom1, atom2, atom3, atom4 [,state ]

EXAMPLES

    get_dihedral 4/n,4/c,4/ca,4/cb
    get_dihedral 4/n,4/c,4/ca,4/cb,state=4

PYMOL API

    cmd.get_dihedral(atom1,atom2,atom3,atom4,state=-1)

        '''
        # preprocess selections
        atom1 = selector.process(atom1)
        atom2 = selector.process(atom2)
        atom3 = selector.process(atom3)
        atom4 = selector.process(atom4)
        #   
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_dihe(_self._COb,str(atom1),str(atom2),str(atom3),str(atom4),int(state)-1)
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self):
            raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_dihedral: %5.3f degrees."%r)
        return r

    def get_model(selection="(all)",state=1,ref='',ref_state=0,_self=cmd):
        '''
DESCRIPTION

    "get_model" returns a ChemPy "Indexed" format model from a selection.

PYMOL API

    cmd.get_model(string selection [,int state] )

        '''
        # preprocess selection
        selection = selector.process(selection)
        #   
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_model(_self._COb,"("+str(selection)+")",int(state)-1,str(ref),int(ref_state)-1)
            if r==None:
                r = DEFAULT_ERROR
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_area(selection="(all)",state=1,load_b=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    Get the surface area of an selection. Depends on the "dot_solvent"
    setting. With "dot_solvent=off" (default) it calculates the solvent
    excluded surface area, else the surface accessible surface.

USAGE

    get_area [ selection [, state [, load_b ]]]

ARGUMENTS

    load_b = bool: store per-atom surface area in b-factors {default: 0}

SEE ALSO

    "dot_solvent" setting, "dots" representation (show dots)
        '''
        # preprocess selection
        selection = selector.process(selection)
        #
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_area(_self._COb,"("+str(selection)+")",int(state)-1,int(load_b))
        finally:
            _self.unlock(r,_self)
        if r<0.0: # negative area signals error condition
            if _self._raising(): raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_area: %5.3f Angstroms^2."%r)
        return r

    def get_chains(selection="(all)",state=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    Print the list of chain identifiers in the given selection.

USAGE

    get_chains [ selection [, state ]]

ARGUMENTS

    selection = str: atom selection {default: all}

    state = int: CURRENTLY IGNORED
        '''
        # preprocess selection
        selection = selector.process(selection)
        #
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_chains(_self._COb,"("+str(selection)+")",int(state)-1)
        finally:
            _self.unlock(r,_self)
        if r==None:
            return []
        if _raising(r,_self):
            raise pymol.CmdException
        elif not quiet:
            print(" cmd.get_chains: ",str(r))
        return r


    def get_names(type='public_objects',enabled_only=0,selection="",_self=cmd):
        '''
DESCRIPTION

    "get_names" returns a list of object and/or selection names.

PYMOL API

    cmd.get_names( [string: "objects"|"selections"|"all"|"public_objects"|"public_selections"] )

NOTES

    The default behavior is to return only object names.

SEE ALSO

    get_type, count_atoms, count_states
        '''
        selection = selector.process(selection)
        r = DEFAULT_ERROR
        # this needs to be replaced with a flag & masking scheme...
        mode = 1
        if type=='objects':
            mode = 1
        elif type=='selections':
            mode = 2
        elif type=='all':
            mode = 0
        elif type=='public':
            mode = 3
        elif type=='public_objects':
            mode = 4
        elif type=='public_selections':
            mode = 5
        elif type=='public_nongroup_objects':
            mode = 6
        elif type=='public_group_objects':
            mode = 7
        elif type=='nongroup_objects':
            mode = 8
        elif type=='group_objects':
            mode = 9
        else:
            print("Error: unknown type: '%s'"%str(type))
            if _raising(-1,_self): raise pymol.CmdException            
        try:
            _self.lock(_self)
            r = _cmd.get_names(_self._COb,int(mode),int(enabled_only),str(selection))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_legal_name(name,_self=cmd):
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_legal_name(_self._COb,str(name))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_type(name,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_type" returns a string describing the named object or
     selection or the string "nonexistent" if the name in unknown.

PYMOL API

    cmd.get_type(string object-name)

NOTES

    Possible return values are

    "object:molecule"
    "object:map"
    "object:mesh"
    "object:slice"
    "object:surface"
    "object:measurement"
    "object:cgo"
    "object:group"
    "object:volume"
    "selection"

SEE ALSO

    get_names
        '''
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_type(_self._COb,str(name))
        finally:
            _self.unlock(r,_self)
        if is_error(r):
            if not quiet and _feedback(fb_module.cmd,fb_mask.errors,_self):      
                print("Cmd-Error: unrecognized name.")
        elif not quiet:
            print(r)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def id_atom(selection,mode=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "id_atom" returns the original source id of a single atom, or
    raises and exception if the atom does not exist or if the selection
    corresponds to multiple atoms.

PYMOL API

    list = cmd.id_atom(string selection)
        '''
        r = DEFAULT_ERROR
        selection = str(selection)
        l = identify(selection, mode)
        ll = len(l)
        if not ll:
            if _feedback(fb_module.cmd,fb_mask.errors,_self):
                print("cmd-Error: atom %s not found by id_atom." % selection)
            if _self._raising(_self=_self): raise pymol.CmdException
        elif ll>1:
            if _feedback(fb_module.cmd,fb_mask.errors,_self):
                print("cmd-Error: multiple atoms %s found by id_atom." % selection)
            if _self._raising(_self=_self): raise pymol.CmdException
        else:
            r = l[0]
            if not quiet:
                if mode:
                    print(" cmd.id_atom: (%s and id %d)"%(r[0],r[1]))
                else:
                    print(" cmd.id_atom: (id %d)"%r)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def identify(selection="(all)",mode=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "identify" returns a list of atom IDs corresponding to the ID code
    of atoms in the selection.

PYMOL API

    list = cmd.identify(string selection="(all)",int mode=0)

NOTES

    mode 0: only return a list of identifiers (default)
    mode 1: return a list of tuples of the object name and the identifier

        '''
        # preprocess selection
        selection = selector.process(selection)
        #
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.identify(_self._COb,"("+str(selection)+")",int(mode)) # 0 = default mode
        finally:
            _self.unlock(r,_self)
        if is_list(r):
            if len(r):
                if not quiet:
                    if mode:
                        for a in r:
                            print(" cmd.identify: (%s and id %d)"%(a[0],a[1]))
                    else:
                        for a in r:
                            print(" cmd.identify: (id %d)"%a)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def index(selection="(all)",quiet=1,_self=cmd):
        '''
DESCRIPTION

    "index" returns a list of tuples corresponding to the
    object name and index of the atoms in the selection.

PYMOL API

    list = cmd.index(string selection="(all)")

NOTE

  Atom indices are fragile and will change as atoms are added
  or deleted.  Whenever possible, use integral atom identifiers
  instead of indices.

        '''
        # preprocess selection
        selection = selector.process(selection)
        #      
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.index(_self._COb,"("+str(selection)+")",0) # 0 = default mode
        finally:
            _self.unlock(r,_self)
        if not quiet:
            if is_list(r):
                if len(r):
                    for a in r:
                        print(" cmd.index: (%s`%d)"%(a[0],a[1]))
        if _raising(r,_self): raise pymol.CmdException
        return r

    def find_pairs(selection1,selection2,state1=1,state2=1,cutoff=3.5,mode=0,angle=45,_self=cmd):
        '''
DESCRIPTION

    API only function. Returns a list of atom pairs. Atoms are represented as
    (model,index) tuples.

    Can be restricted to hydrogen-bonding-like contacts. WARNING: Only checks
    atom orientation, not atom type (so would hydrogen bond between carbons for
    example), so make sure to provide appropriate atom selections.

ARGUMENTS

    selection1, selection2 = string: atom selections

    state1, state2 = integer: state-index (only positive values allowed) {default: 1}

    cutoff = float: distance cutoff {default: 3.5}

    mode = integer: if mode=1, do coarse hydrogen bonding assessment {default: 0}

    angle = float: hydrogen bond angle cutoff, only if mode=1 {default: 45.0}

NOTE

    Although this does a similar job like "distance", it uses a completely
    different routine and the "mode" argument has different meanings!
        '''
        # preprocess selection
        selection1 = selector.process(selection1)
        selection2 = selector.process(selection2)
        #      
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.find_pairs(_self._COb,"("+str(selection1)+")",
                                      "("+str(selection2)+")",
                                      int(state1)-1,int(state2)-1,
                                      int(mode),float(cutoff),float(angle))
            # 0 = default mode
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_extent(selection="(all)",state=0,quiet=1,_self=cmd):
        '''
DESCRIPTION

    "get_extent" returns the minimum and maximum XYZ coordinates of a
    selection as an array:
     [ [ min-X , min-Y , min-Z ],[ max-X, max-Y , max-Z ]]

PYMOL API

    cmd.get_extent(string selection="(all)", state=0 )

        '''
        # preprocess selection
        selection = selector.process(selection)
        #      
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            r = _cmd.get_min_max(_self._COb,str(selection),int(state)-1)
        finally:
            _self.unlock(r,_self)
        if not r:
            if _self._raising(_self=_self): raise pymol.CmdException
        elif not quiet:
            print(" cmd.extent: min: [%8.3f,%8.3f,%8.3f]"%(r[0][0],r[0][1],r[0][2]))
            print(" cmd.extent: max: [%8.3f,%8.3f,%8.3f]"%(r[1][0],r[1][1],r[1][2]))      
        if _raising(r,_self): raise pymol.CmdException
        return r

    def phi_psi(selection="(byres pk1)", quiet=1, _self=cmd):
        '''
DESCRIPTION

    "phi_psi" return the phi and psi angles for a protein atom
    selection.
    
USAGE
        '''
        
        r = cmd.get_phipsi(selection)
        if r!=None:
            kees = list(r.keys())
            kees.sort()
            if not quiet:
                _self.feedback('push')
                _self.feedback('disable','executive','actions')
                for a in kees:
                    _self.iterate("(%s`%d)"%a,"print(' %-9s "+
                                ("( %6.1f, %6.1f )"%r[a])+
                                "'%(resn+'-'+resi+':'))")
                _self.feedback('pop')
        elif _feedback(fb_module.cmd,fb_mask.errors,_self):      
            print("cmd-Error: can't compute phi_psi")
        if _raising(r,_self): raise pymol.CmdException
        return r


    def count_atoms(selection="(all)",quiet=1,state=0,domain='',_self=cmd):
        '''
DESCRIPTION

    "count_atoms" returns a count of atoms in a selection.

USAGE

    count_atoms [ selection [, quiet [, state ]]]
        '''
        r = DEFAULT_ERROR
        # preprocess selection
        selection = selector.process(selection)
        #
        try:
            _self.lock(_self)   
            r = _cmd.select(_self._COb,"_count_tmp","("+str(selection)+")",1,int(state)-1,str(domain))
            _cmd.delete(_self._COb,"_count_tmp")
        finally:
            _self.unlock(r,_self)
        if not quiet: print(" count_atoms: %d atoms"%r)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def count_discrete(selection, quiet=1, _self=cmd):
        '''
DESCRIPTION

    Count the number of discrete objects in selection.

USAGE

    count_discrete selection
        '''
        with _self.lockcm:
            r = _cmd.count_discrete(_self._COb, str(selection))
            if not int(quiet):
                print(' count_discrete: %d' % r)
            return r

    def get_names_of_type(type,public=1,_self=cmd):
        """
DESCRIPTION

    "get_names_of_type" will return a list of names for the given type.

        """
        obj_type = "public_objects" if public==1 else "objects"
        types = []
        mix = []
        obj = None
        try:
            obj = _self.get_names(obj_type)
        except:
            pass
    
        if obj:
            try:
                types = list(map(_self.get_type,obj))
                mix = list(zip(obj,types))
            except:
                pass
        lst = []
        for a in mix:
            if a[1]==type:
                lst.append(a[0])
        return lst


    def get_raw_alignment(name='',active_only=0,_self=cmd):
        '''
DESCRIPTION

    "get_raw_alignment" returns a list of lists of (object,index)
    tuples containing the raw per-atom alignment relationships

PYMOL API

    cmd.get_raw_alignment(name)

        '''
        try:
            _self.lock(_self)
            r = _cmd.get_raw_alignment(_self._COb,str(name),int(active_only))
        finally:
            _self.unlock(r,_self)
        if _raising(r,_self): raise pymol.CmdException
        return r

    def get_object_state(name, _self=cmd):
        '''
DESCRIPTION

    Returns the effective object state.
        '''
        states = _self.count_states('%' + name)
        if states < 2 and _self.get_setting_boolean('static_singletons'):
            return 1
        state = _self.get_setting_int('state', name)
        if state > states:
            raise pymol.CmdException('Invalid state %d for object %s' % (state, name))
        return state

    def get_selection_state(selection, _self=cmd):
        '''
DESCRIPTION

    Returns the effective object state for all objects in given selection.
    Raises exception if objects are in different states.
        '''
        state_set = set(map(_self.get_object_state,
            _self.get_object_list('(' + selection + ')')))
        if len(state_set) != 1:
            if len(state_set) == 0:
                return 1
            raise pymol.CmdException('Selection spans multiple object states')
        return state_set.pop()

    def centerofmass(selection='(all)', state=-1, quiet=1, _self=cmd):
        '''
DESCRIPTION

    Calculates the center of mass. Considers atom mass and occupancy.

ARGUMENTS

    selection = string: atom selection {default: all}

    state = integer: object state, -1 for current state, 0 for all states
    {default: -1}

NOTES

    If occupancy is 0.0 for an atom, set it to 1.0 for the calculation
    (assume it was loaded from a file without occupancy information).

SEE ALSO

    get_extent
        '''
        from chempy import cpv
        state, quiet = int(state), int(quiet)

        if state < 0:
            states = [get_selection_state(selection)]
        elif state == 0:
            states = list(range(1, _self.count_states(selection)+1))
        else:
            states = [state]

        com = cpv.get_null()
        totmass = 0.0

        for state in states:
            model = _self.get_model(selection, state)
            for a in model.atom:
                m = a.get_mass() * (a.q or 1.0)
                com = cpv.add(com, cpv.scale(a.coord, m))
                totmass += m

        if not totmass:
            raise pymol.CmdException('mass is zero')

        com = cpv.scale(com, 1./totmass)
        if not quiet:
            print(' Center of Mass: [%8.3f,%8.3f,%8.3f]' % tuple(com))
        return com

    def cif_get_array(name, key, dtype="s", quiet=1, _self=cmd):
        '''
DESCRIPTION

    EXPERIMENTAL AND SUBJECT TO CHANGE!

ARGUMENTS

    name = string: object name

    key = CIF data item name in lower case

    dtype = str: "s" (str), "i" (int) or "f" (float)
        '''
        with _self.lockcm:
            r = _cmd.cif_get_array(_self._COb, name, key, dtype)
        if r and not int(quiet):
            n = len(r)
            r_print = r if n < 10 else (r[:9] + ['... (%d more items)' % (n - 9)])
            print(" %s:" % (key), ', '.join(map(str, r_print)))
        return r

    def get_assembly_ids(name, quiet=1, _self=cmd):
        '''
DESCRIPTION

    EXPERIMENTAL AND SUBJECT TO CHANGE!
    Get the list of assembly ids for an object loaded from mmCIF.
        '''
        r = cif_get_array(name, "_pdbx_struct_assembly.id", _self=_self)
        if r and not int(quiet):
            print(" Assembly IDs: %s" % ', '.join(r))
        return r
