import numpy as np

from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor
from sas.qtgui.Plotting.Slicers.BoxSum import HorizontalDoubleLine, PointInteractor, VerticalDoubleLine


class BoxMask(BaseInteractor):
    """
    BoxMask Class: determine 2 rectangular area to find the pixel of
    a Data inside of box.

    Uses PointerInteractor , VerticalDoubleLine,HorizontalDoubleLine.

    :param zorder:  Artists with lower zorder values are drawn first.
    :param x_min: the minimum value of the x coordinate
    :param x_max: the maximum value of the x coordinate
    :param y_min: the minimum value of the y coordinate
    :param y_max: the maximum value of the y coordinate

    """
    def __init__(self, base, axes, color='black', zorder=3, side=None,
                 x_min=0.008, x_max=0.008, y_min=0.0025, y_max=0.0025):
        """
        """
        BaseInteractor.__init__(self, base, axes, color=color)
        # list of Boxmask markers
        self.markers = []
        self.axes = axes
        self.mask = None
        self.is_inside = side
        # connect the artist for the motion
        self.connect = self.base.connect
        # when qmax is reached the selected line is reset
        self.qmax = min(self.data.xmax, self.data.xmin)
        # Define the box limits
        self.xmin = -1 * 0.5 * min(np.fabs(self.data.xmax),
                                   np.fabs(self.data.xmin))
        self.ymin = -1 * 0.5 * min(np.fabs(self.data.xmax),
                                   np.fabs(self.data.xmin))
        self.xmax = 0.5 * min(np.fabs(self.data.xmax),
                              np.fabs(self.data.xmin))
        self.ymax = 0.5 * min(np.fabs(self.data.xmax),
                              np.fabs(self.data.xmin))
        # center of the box
        self.center_x = 0.0002
        self.center_y = 0.0003
        # Number of points on the plot
        self.nbins = 20
        # Define initial result the summation
        self.count = 0
        self.error = 0
        # Flag to determine if the current figure has moved
        # set to False == no motion , set to True== motion
        self.has_move = False
        # Create Box edges
        self.horizontal_lines = HorizontalDoubleLine(self,
                                                     self.axes,
                                                     color='blue',
                                                     zorder=zorder,
                                                     y=self.ymax,
                                                     x=self.xmax,
                                                     center_x=self.center_x,
                                                     center_y=self.center_y)
        self.horizontal_lines.qmax = self.qmax

        self.vertical_lines = VerticalDoubleLine(self,
                                                 self.axes,
                                                 color='grey',
                                                 zorder=zorder,
                                                 y=self.ymax,
                                                 x=self.xmax,
                                                 center_x=self.center_x,
                                                 center_y=self.center_y)
        self.vertical_lines.qmax = self.qmax

        self.center = PointInteractor(self,
                                      self.axes, color='grey',
                                      zorder=zorder,
                                      center_x=self.center_x,
                                      center_y=self.center_y)
        # Save the name of the slicer panel associate with this slicer
        self.panel_name = ""
        # Update and post slicer parameters
        self.update()
        self._post_data()

    def clear(self):
        """
        Clear the slicer and all connected events related to this slicer
        """
        self.clear_markers()
        self.horizontal_lines.clear()
        self.vertical_lines.clear()
        self.center.clear()
        self.base.connect.clearall()

    def update(self):
        """
        Respond to changes in the model by recalculating the profiles and
        resetting the widgets.
        """
        # check if the center point has moved and update the figure accordingly
        if self.center.has_move:
            self.center.update()
            self.horizontal_lines.update(center=self.center)
            self.vertical_lines.update(center=self.center)
        # # check if the horizontal lines have moved and update
        # the figure accordingly
        if self.horizontal_lines.has_move:
            self.horizontal_lines.update()
            self.vertical_lines.update(y1=self.horizontal_lines.y1,
                                       y2=self.horizontal_lines.y2,
                                       height=self.horizontal_lines.half_height)
        # # check if the vertical lines have moved and update
        # the figure accordingly
        if self.vertical_lines.has_move:
            self.vertical_lines.update()
            self.horizontal_lines.update(x1=self.vertical_lines.x1,
                                         x2=self.vertical_lines.x2,
                                         width=self.vertical_lines.half_width)
        # if self.is_inside is not None:
        out = self._post_data()
        return out

    def save(self, ev):
        """
        Remember the roughness for this layer and the next so that we
        can restore on Esc.
        """
        self.horizontal_lines.save(ev)
        self.vertical_lines.save(ev)
        self.center.save(ev)

    def _post_data(self):
        """
        Get the limits of the boxsum and compute the sum of the pixel
        contained in that region and the error on that sum
        """
        from sasdata.data_util.manipulations import Boxcut
        # # Data 2D for which the pixel will be summed
        data = self.data
        mask = data.mask
        # # the region of the summation
        x_min = self.horizontal_lines.x2
        x_max = self.horizontal_lines.x1
        y_min = self.vertical_lines.y2
        y_max = self.vertical_lines.y1
        mask = Boxcut(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)

        if self.is_inside:
            out = (mask(data) == False)
        else:
            out = (mask(data))
        # self.base.data.mask=out
        self.mask = mask
        return out

    def moveend(self, ev):
        """
        After a dragging motion this function is called to compute
        the error and the sum of pixel of a given data 2D
        """
        self._post_data()

    def restore(self):
        """
        Restore the roughness for this layer.
        """
        self.horizontal_lines.restore()
        self.vertical_lines.restore()
        self.center.restore()

    def move(self, x, y, ev):
        """
        Process move to a new position, making sure that the move is allowed.
        """
        pass

    def set_cursor(self, x, y):
        pass

    def get_params(self):
        """
        Store a copy of values of parameters of the slicer into a dictionary.

        :return params: the dictionary created
        """
        params = {}
        params["Width"] = np.fabs(self.vertical_lines.half_width) * 2
        params["Height"] = np.fabs(self.horizontal_lines.half_height) * 2
        params["center_x"] = self.center.x
        params["center_y"] = self.center.y
        return params

    def get_mask(self):
        """
        return mask as a result of boxcut
        """
        mask = self.mask
        return mask

    def set_params(self, params):
        """
        Receive a dictionary and reset the slicer with values contained
        in the values of the dictionary.

        :param params: a dictionary containing name of slicer parameters and
           values the user assigned to the slicer.
        """
        x_max = np.fabs(params["Width"]) / 2
        y_max = np.fabs(params["Height"]) / 2

        self.center_x = params["center_x"]
        self.center_y = params["center_y"]
        # update the slicer given values of params
        self.center.update(center_x=self.center_x, center_y=self.center_y)
        self.horizontal_lines.update(center=self.center, width=x_max, height=y_max)
        self.vertical_lines.update(center=self.center, width=x_max, height=y_max)
        # compute the new error and sum given values of params
        self._post_data()

    def draw(self):
        self.base.update()


