import pyqtgraph as pg
from qgis.PyQt import QtCore, QtGui
from qgis.PyQt.QtCore import QRectF


class ErrorBoxItem(pg.GraphicsObject):
    def __init__(self, **opts):
        """
        All keyword arguments are passed to setData().
        """
        pg.GraphicsObject.__init__(self)
        self.picture = QtGui.QPicture()
        self.opts = dict(
            x=None,
            y=None,
            height=None,
            width=None,
            top=None,
            bottom=None,
            left=None,
            right=None,
            beam=None,
            pen=None,
        )
        self.setVisible(False)
        self.setData(**opts)

    def setData(self, **opts):
        """
        Update the data in the item. All arguments are optional.

        Valid keyword options are:
        x, y, height, width, top, bottom, left, right, beam, pen

          * x and y must be numpy arrays specifying the coordinates of data points.
          * height, width, top, bottom, left, right, and beam may be numpy arrays,
            single values, or None to disable. All values should be positive.
          * top, bottom, left, and right specify the lengths of bars extending
            in each direction.
          * If height is specified, it overrides top and bottom.
          * If width is specified, it overrides left and right.
          * beam specifies the width of the beam at the end of each bar.
          * pen may be any single argument accepted by pg.mkPen().

        This method was added in version 0.9.9. For prior versions, use setOpts.
        """
        self.opts.update(opts)
        self.generatePicture()
        self.setVisible(all(self.opts[ax] is not None for ax in ["x", "y"]))
        self.update()
        self.prepareGeometryChange()
        self.informViewBoundsChanged()

    def setOpts(self, **opts):
        # for backward compatibility
        self.setData(**opts)
        self.generatePicture()

    def generatePicture(self):
        # pre-computing a QPicture object allows paint() to run much more quickly,
        # rather than re-drawing the shapes every time.
        self.picture = QtGui.QPicture()
        p = QtGui.QPainter(self.picture)

        pen = self.opts["pen"]
        if pen is None:
            pen = pg.getConfigOption("foreground")
        p.setPen(pg.mkPen(pen))

        x, y = self.opts["x"], self.opts["y"]
        if x is None or y is None:
            return

        beam = self.opts["beam"]

        height, top, bottom = self.opts["height"], self.opts["top"], self.opts["bottom"]
        if height is not None or top is not None or bottom is not None:
            # draw vertical error box
            if height is not None:
                y1 = y - height / 2.0
                y2 = y + height / 2.0
            else:
                if bottom is None:
                    y1 = y
                else:
                    y1 = y - bottom
                if top is None:
                    y2 = y
                else:
                    y2 = y + top

            if beam is not None and beam > 0:
                x1 = x - beam / 2.0
                x2 = x + beam / 2.0

                for i in range(0, len(x1)):
                    x1_val = x1[i]
                    x2_val = x2[i]
                    y1_val = y1[i]
                    y2_val = y2[i]
                    rect = QRectF(x1_val, y1_val, x2_val - x1_val, y2_val - y1_val)
                    p.drawRect(rect)

        width, right, left = self.opts["width"], self.opts["right"], self.opts["left"]
        if width is not None or right is not None or left is not None:
            # draw vertical error box
            if width is not None:
                x1 = x - width / 2.0
                x2 = x + width / 2.0
            else:
                if left is None:
                    x1 = x
                else:
                    x1 = x - left
                if right is None:
                    x2 = x
                else:
                    x2 = x + right

            if beam is not None and beam > 0:
                y1 = y - beam / 2.0
                y2 = y + beam / 2.0

                for i in range(0, len(x1)):
                    x1_val = x1[i]
                    x2_val = x2[i]
                    y1_val = y1[i]
                    y2_val = y2[i]
                    rect = QRectF(x1_val, y1_val, x2_val - x1_val, y2_val - y1_val)
                    p.drawRect(rect)
        p.end()

    def paint(self, p, *args):
        p.drawPicture(0, 0, self.picture)

    def boundingRect(self):
        # boundingRect _must_ indicate the entire area that will be drawn on
        # or else we will get artifacts and possibly crashing.
        # (in this case, QPicture does all the work of computing the bounding rect for us)
        return QtCore.QRectF(self.picture.boundingRect())
