"""
Classes and functions that are shared between the Glide GridGen and Docking
panels.
Copyright Schrodinger, LLC. All rights reserved.
"""
#Contributors: Matvey Adzhigirey
import schrodinger
from schrodinger.structutils import analyze
from schrodinger.ui.qt.appframework2.markers import MarkerMixin
maestro = schrodinger.get_maestro()
try:
    from schrodinger.graphics3d import sphere
except ImportError:
    sphere = None
[docs]def find_equivalent_atoms(st, anum):
    """
    Given a structure atom, return a list of atoms that are identical
    to it. If no identical atoms are found, and empty list is returned.
    """
    for atoms in analyze.find_equivalent_atoms(st, span_molecules=False):
        if anum in atoms:
            atoms.remove(anum)
            return atoms
    return [] 
[docs]class GlideWorkspaceMarkers(MarkerMixin):
    """
    Class for marking constraints and excluded volumes in the Workspace. Along
    with the marker, optional labels are also drawn.
    """
[docs]    def __init__(self):
        super().__init__()
        self._shown = True
        self._labels_shown = True
        self._marker_group = sphere.Group()
        # List of (label object, entry ID, atom number by entry). For labels
        # not associated with atoms, entry ID and atom number are None.
        self._label_info = []
        maestro.workspace_changed_function_add(self._workspaceChangedCallback) 
    def __del__(self):
        self.clear()
        maestro.workspace_changed_function_remove(
            self._workspaceChangedCallback)
[docs]    def clear(self):
        """
        Remove all added labels.
        """
        self._marker_group.clear()
        # Clear atom markers:
        self.removeAllMarkers()
        # Clear labels:
        for obj_handle, _, _ in self._label_info:
            maestro.remove_object(obj_handle)
        self._label_info = [] 
[docs]    def add(self,
            center=None,
            radius=None,
            atom=None,
            color=None,
            name=None,
            opacity=0.8):
        """
        Add another marker to draw.
        Either center and radius need to be specified (to draw a sphere) or an
        atom object (to draw atom markers).
        """
        if center is None and not atom:
            raise ValueError(
                "Must specify a center coordinate or an atom object.")
        if center is not None and radius:
            x, y, z = center[:3]
            sphere_obj = sphere.MaestroSphere(
                x=x,
                y=y,
                z=z,
                radius=radius,
                opacity=opacity,
                color=color,
            )
            self._marker_group.add(sphere_obj)
        elif atom:
            self.addMarker([atom], color=color)
            # TODO consider using self.addAtomMarker()
        if name:
            if center is None:
                center = atom.xyz
            obj_handle = maestro.create_single_line_text(name, *center)
            if atom:
                self._label_info.append(
                    (obj_handle, atom.entry_id, atom.number_by_entry))
            else:
                self._label_info.append((obj_handle, None, None))
            if not self._shown or not self._labels_shown:
                maestro.hide_object(obj_handle)
        if self._shown:
            self.show() 
[docs]    def show(self):
        """
        Show the markers in the Workspace.
        """
        self._shown = True
        self._marker_group.show()
        self.showAllMarkers()
        if self._labels_shown:
            self._showDesiredLabels() 
[docs]    def hide(self):
        """
        Hide the markers from the Workspace.
        """
        self._shown = False
        self._marker_group.hide()
        self.hideAllMarkers()
        for obj_handle, _, _ in self._label_info:
            maestro.hide_object(obj_handle) 
[docs]    def setVisible(self, visible):
        """
        Show the markers if "visible" is True, hide otherwise.
        """
        if visible:
            self.show()
        else:
            self.hide() 
[docs]    def showLabels(self):
        """
        Enable labels.
        """
        self._labels_shown = True
        if self._shown:
            self._showDesiredLabels() 
    def _showDesiredLabels(self):
        """
        Show labels for markers - except those whose atoms are no
        longer present in the Workspace.
        """
        # Show atoms for atoms that were included, and hide labels for
        # atoms that were excluded:
        st = maestro.workspace_get()
        for label_obj, eid, anum in self._label_info:
            if eid is not None:
                asl = f'(entry.id {eid} AND atom.entrynum {anum})'
                if analyze.evaluate_asl(st, asl):
                    maestro.show_object(label_obj)
                else:
                    maestro.hide_object(label_obj)
            else:
                # This label is not associated with an atom; always show it
                maestro.show_object(label_obj)
[docs]    def hideLabels(self):
        """
        Disable labels.
        """
        self._labels_shown = False
        for obj_handle, _, _ in self._label_info:
            maestro.hide_object(obj_handle) 
[docs]    def setLabelsVisible(self, visible):
        """
        Show the labels if "visible" is True, hide otherwise.
        """
        if visible:
            self.showLabels()
        else:
            self.hideLabels() 
    def _workspaceChangedCallback(self, what_changed):
        """
        Workspace changed callback for hiding labels when marked atoms are
        excluded from the Workspace, and re-showing them when the are
        re-included.  This is already done for marker objects themselves
        by the MarkerMixin.
        """
        if what_changed not in (maestro.WORKSPACE_CHANGED_EVERYTHING,
                                maestro.WORKSPACE_CHANGED_APPEND,
                                maestro.WORKSPACE_CHANGED_CONNECTIVITY):
            return
        if self._shown and self._labels_shown:
            # Show labels, only for those atoms that are still in Workspace:
            self._showDesiredLabels()