from .pyqtgraph.graphicsItems.LegendItem import ItemSample
from .pyqtgraph.graphicsItems.ScatterPlotItem import ScatterPlotItem, drawSymbol
from .pyqtgraph.graphicsItems.PlotDataItem import PlotDataItem
from .pyqtgraph import functions as fn
from . import pyqtgraph as pg
from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
import os

isOnWindows = os.name == 'nt'


"""Other modified files (directly): ScatterPlotItem.py, to change point selection. Ctrl + clic: select area. Clic: only one single point"""


class myGraphicsLayoutWidget(pg.GraphicsView):
    def __init__(self, parent=None, **_kwargs):
        pg.GraphicsView.__init__(self, parent)
        self.ci = myGraphicsLayout()
        for n in ['nextRow', 'nextCol', 'nextColumn', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLayout', 'addLabel', 'removeItem', 'itemIndex', 'clear']:
            setattr(self, n, getattr(self.ci, n))
        self.setCentralItem(self.ci)

    def useOpenGL(self, b=True):
        """Overwrited to fix bad antialiasing while using openGL"""
        if b:
            if isOnWindows:
                v = QtOpenGL.QGLWidget()
            else:
                v = QtWidgets.QOpenGLWidget()
                fmt = QtGui.QSurfaceFormat()
                fmt.setSamples(8)
                fmt.setDepthBufferSize(24)
                fmt.setStencilBufferSize(8)  # Fixes antialias zoom error
                v.setFormat(fmt)
        else:
            v = QtGui.QWidget()
        self.setViewport(v)


class myGraphicsLayout(pg.GraphicsLayout):
    def __init__(self):
        super().__init__()
        self.layout.setHorizontalSpacing(-5)
        self.layout.setVerticalSpacing(-5)

    def addItem(self, item, row=None, col=None, rowspan=1, colspan=1):
        """
        Add an item to the layout and place it in the next available cell (or in the cell specified).
        The item must be an instance of a QGraphicsWidget subclass.
        """
        if row is None:
            row = self.currentRow
        if col is None:
            col = self.currentCol

        self.items[item] = []
        for i in range(rowspan):
            for j in range(colspan):
                row2 = row + i
                col2 = col + j
                if row2 not in self.rows:
                    self.rows[row2] = {}
                self.rows[row2][col2] = item
                self.items[item].append((row2, col2))

        self.layout.addItem(item, row, col, rowspan, colspan)
        self.nextRow()

    def set_graph_disposition(self, item, row=1, col=1, rowspan=1, colspan=1):
        """
        Function to modify the position of an item in the list

        :param item: WidgetPlotItem to set
        :param row: Row
        :param col: Column
        :param rowspan:
        :param colspan:
        :return:
        """
        # Remove from existing
        self.layout.removeItem(item)
        for r, c in self.items[item]:
            del self.rows[r][c]

        # Add new configuration
        for i in range(rowspan):
            for j in range(colspan):
                row2 = row + i
                col2 = col + j
                if row2 not in self.rows:
                    self.rows[row2] = {}
                self.rows[row2][col2] = item
                self.items[item].append((row2, col2))
        self.layout.addItem(item, row, col, rowspan, colspan)


class myItemSample(ItemSample):
    def __init__(self, item):
        super().__init__(item)
        self.offset = 13
        self.widthCell = 40

    def set_offset(self, offset):
        self.offset = offset

    def set_width_cell(self, width):
        self.widthCell = width

    # """Overwrites for text closer to the sample"""
    # def boundingRect(self):
    #     return QtCore.QRectF(0, 0, 0, 0)

    def paint(self, p, *args):
        """Overwrites to make matlab-like samples"""
        # p.setRenderHint(p.Antialiasing)  # only if the data is antialiased.
        opts = self.item.opts

        if opts.get('fillLevel', None) is not None and opts.get('fillBrush', None) is not None:
            p.setBrush(fn.mkBrush(opts['fillBrush']))
            p.setPen(fn.mkPen(None))
            p.drawPolygon(QtGui.QPolygonF([QtCore.QPointF(2, 18), QtCore.QPointF(18, 2), QtCore.QPointF(18, 18)]))

        p.save()
        linewidth = self.widthCell
        v_offset = self.offset

        opts = self.item.opts
        if not isinstance(self.item, ScatterPlotItem):
            pen = fn.mkPen(opts['pen'])
            pen.setWidthF(pen.widthF()*0.9)
            p.setPen(pen)
            p.drawLine(0, v_offset, linewidth, v_offset)

        p.restore()

        symbol = opts.get('symbol', None)
        if symbol is not None:
            if isinstance(symbol, list):
                symbol = symbol[0]

            if isinstance(self.item, PlotDataItem):
                opts = self.item.scatter.opts

            pen = fn.mkPen(opts['pen'])
            brush = fn.mkBrush(opts['brush'])
            size = opts['size'] * 1.0  # Scale factor for legend size

            p.translate(linewidth / 2, v_offset)
            drawSymbol(p, symbol, size, pen, brush)


class myLegend(pg.LegendItem):
    """Legend that fixes bugs (flush left + space) from pyqtgraph's legend"""
    def __init__(self, size=None, offset=(30, 30), is_light=False):
        super(myLegend, self).__init__(size, offset)
        self.layout.setVerticalSpacing(-8)

        self.is_light = is_light
        self.width_cell_sample = 25

    def set_space_sample_label(self, theSpace):
        """To set the gap between the sample and the label"""
        self.layout.setHorizontalSpacing(theSpace)

    def set_offset_sample(self, offset):
        """To tune the offset between the sample and the text"""
        for sample, label in self.items:
            sample.set_offset(offset)

    def set_width_cell_sample(self, width):
        """Set width of sample"""
        self.width_cell_sample = width
        self.apply_width_sample()

    def updateSize(self):
        if self.size is not None:
            return
        height = 0
        width = 0
        for sample, label in self.items:
            height += max(sample.boundingRect().height(), label.height())
            width = max(width, sample.boundingRect().width() + label.width())  # + 13
        self.setGeometry(0, 0, width, height)

    def addItem(self, item, name):
        """Overwrites to flush left"""
        # label = LabelItem(name)
        label = myLabelItem(name, justify='left')
        width_cell = self.width_cell_sample
        if isinstance(item, ItemSample):
            sample = item
        else:
            sample = myItemSample(item)
            sample.set_width_cell(width_cell*1)
        row = self.layout.rowCount()

        self.items.append((sample, label))
        self.layout.addItem(sample, row, 0)
        self.layout.setColumnFixedWidth(0, width_cell)
        self.layout.setColumnFixedWidth(1, 10)
        self.layout.setHorizontalSpacing(0.0)  # No spacing between sample and text
        self.layout.addItem(label, row, 1)
        self.layout.setRowFixedHeight(row, 15)

        self.updateSize()

    def apply_width_sample(self):
        width_cell = self.width_cell_sample
        for sample, label in self.items:
            sample.set_width_cell(width_cell*1)
        self.layout.setColumnFixedWidth(0, width_cell)
        self.updateSize()

    def set_font(self, font_size, font_color, fontname=None):
        for item in self.items:
            for single_item in item:
                if isinstance(single_item, pg.LabelItem):
                    if fontname is not None:
                        single_item.setText(single_item.text, **{'color': font_color, 'size': str(font_size) + 'pt', 'font-family': fontname})
                    else:
                        single_item.setText(single_item.text, **{'color': font_color, 'size': str(font_size) + 'pt'})

    def paint(self, p, *args):
        """Overwrited to select background color"""
        if len(self.items):
            if self.is_light:
                p.setPen(pg.mkPen(100, 100, 100, 255))
                p.setBrush(pg.mkBrush(255, 255, 255, 255))
            else:
                p.setPen(pg.mkPen(100, 100, 100, 255))
                p.setBrush(pg.mkBrush(10, 10, 10, 255))
            offset_x = 0
            offset_y = 20
            boundingRect = QtCore.QRectF(0, offset_y/2, self.width()-offset_x, self.height()-offset_y)
            p.drawRect(boundingRect)  # self.boundingRect())

    def set_position(self, position, offset):
        """
        Set the position of the legend, in a corner.

        :param position: String (NW, NE, SW, SE), indicates which corner the legend is close
        :param offset: Tuple (xoff, yoff), x and y offset from the edge
        :return:
        """
        offPosMult = 1

        if position == "NW":
            quartil_parent = (0, 0)
            oxMult, oyMult = 1, 1
        elif position == "NE":
            quartil_parent = (1, 0)
            oxMult, oyMult = -1, 1
        elif position == "SW":
            quartil_parent = (0, 1)
            oxMult, oyMult = 1, -1
            offPosMult = -1
        elif position == "SE":
            quartil_parent = (1, 1)
            oxMult, oyMult = -1, -1
            offPosMult = -1
        else:
            print("Could not place legend : position unknown (please use NW, NE, SW or SE)")
            return
        self.anchor(quartil_parent, quartil_parent, offset=(offset[0] * oxMult, offPosMult * (-10) + offset[1] * oyMult))  # * -10: corresponds to offset_y defined in paint


class myLabelItem(pg.LabelItem):
    def setText(self, text, **args):
        """ Overwrited to add font-family to options """
        self.text = text
        opts = self.opts
        for k in args:
            opts[k] = args[k]

        optlist = []

        color = self.opts['color']
        if color is None:
            color = pg.getConfigOption('foreground')
        color = fn.mkColor(color)
        optlist.append('color: #' + fn.colorStr(color)[:6])
        if 'size' in opts:
            optlist.append('font-size: ' + opts['size'])
        if 'bold' in opts and opts['bold'] in [True, False]:
            optlist.append('font-weight: ' + {True: 'bold', False: 'normal'}[opts['bold']])
        if 'italic' in opts and opts['italic'] in [True, False]:
            optlist.append('font-style: ' + {True: 'italic', False: 'normal'}[opts['italic']])
        if 'font-family' in opts:
            optlist.append('font-family: ' + opts['font-family'])
        full = "<span style='%s'>%s</span>" % ('; '.join(optlist), text)
        # print full
        self.item.setHtml(full)
        self.updateMin()
        self.resizeEvent(None)
        self.updateGeometry()


class myAxis(pg.AxisItem):
    def __init__(self, orientation):
        super().__init__(orientation)
        self.nudges = {'left': (-10, 0), 'right': (5, 0), 'bottom': (0, -5), 'top': (0, 5)}
        # self.style.update({'textFillLimits': [(0, 0.8),    ## never fill more than 80% of the axis
        #         (4, 0.4),    ## If we already have 4 ticks with text, fill no more than 40% of the axis
        #         (6, 0.2)]})

    def get_label_pos(self):
        """Overwrited to place label closer to the axis"""
        br = self.label.boundingRect()
        x, y = 0, 0
        if self.orientation == 'left':
            y, x = int(self.size().height()/2 + br.width()/2), 0
        elif self.orientation == 'right':
            y, x = int(self.size().height()/2 + br.width()/2), int(self.size().width()-br.height())
        elif self.orientation == 'top':
            y, x = 0, int(self.size().width()/2. - br.width()/2.)
        elif self.orientation == 'bottom':
            x, y = int(self.size().width()/2. - br.width()/2.), int(self.size().height()-br.height())
        try:
            return int(x + self.nudges[self.orientation][0]), int(y - self.nudges[self.orientation][1])
        except AttributeError:
            return x, y

    def resizeEvent(self, ev=None):
        """Overwrited to place label closer to the axis"""
        p = QtCore.QPointF(0, 0)
        x_pos, y_pos = self.get_label_pos()
        p.setX(int(x_pos))
        p.setY(int(y_pos))
        self.label.setPos(p)
        self.picture = None

    def set_label_pos(self, orientation, x_offset=0, y_offset=0):
        self.nudges[orientation] = (x_offset, y_offset)

    def set_number_ticks(self, number):
        self.style.update({'textFillLimits': [(number, 0)]})
