import numpy as np
from PyQt5 import QtGui
from PyQt5.QtGui import QDoubleValidator

from .DataExtractorLyse import DataExtractorManager
from .WidgetPlot import WidgetPlot

import pyqtgraph as pg
from pyqtgraph import OrderedDict
from PyQt5.QtWidgets import QLabel, QLineEdit
from pyqtgraph import Qt
from pyqtgraph.Qt import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QRegExp

from PIL import ImageColor

color_palette_html = ['#1f77b4',
                      '#ff7f0e',
                      '#2ca02c',
                      '#d62728',
                      '#9467bd',
                      '#8c564b',
                      '#e377c2',
                      '#7f7f7f',
                      '#bcbd22',
                      '#17becf']

color_palette = [ImageColor.getcolor(color, "RGB") for color in color_palette_html]

Gradients = OrderedDict([
    ('bw', {'ticks': [(0.0, (0, 0, 0, 255)), (1, (255, 255, 255, 255))], 'mode': 'rgb'}),
    ('hot', {'ticks': [(0.3333, (185, 0, 0, 255)), (0.6666, (255, 220, 0,
                                                             255)), (1, (255, 255, 255, 255)), (0, (0, 0, 0, 255))],
             'mode': 'rgb'}),
    ('jet0', {'ticks': [(1, (166, 0, 0, 255)),
                       (0.32247191011235954, (0, 255, 255, 255)),
                       (0.11348314606741573, (0, 68, 255, 255)),
                       (0.6797752808988764, (255, 255, 0, 255)),
                       (0.902247191011236, (255, 0, 0, 255)),
                       (0.5022471910112359, (0, 255, 0, 255)),
                       (0.0, (0, 0, 166, 255))],
             'mode': 'rgb'}),
    ('jet', {'ticks': [(1, (128, 0, 0, 255)),
                       (0.878431372549020, (255, 0, 0, 255)),
                       (0.627450980392157, (255, 255, 0, 255)),
                       (0.376470588235294, (0, 255, 255, 255)),
                       (0.125490196078431, (0, 0, 255, 255)),
                       (0.0, (0, 0, 131, 255))],
             'mode': 'rgb'}),
    ('summer', {'ticks': [(1, (255, 255, 0, 255)), (0.0, (0, 170, 127, 255))], 'mode': 'rgb'}),
    ('space', {'ticks': [(0.562, (75, 215, 227, 255)), (0.087, (255, 170, 0, 254)), (0.332, (0, 255, 0, 255)),
                         (0.77, (85, 0, 255, 255)), (0.0, (255, 0, 0, 255)), (1.0, (255, 0, 127, 255))],
               'mode': 'rgb'}),
    ('winter', {'ticks': [(1, (0, 255, 127, 255)), (0.0, (0, 0, 255, 255))], 'mode': 'rgb'})
])


class WidgetFakeColorPlot(WidgetPlot):
    def __init__(self, title, data_path=None, data_group=None, Isocurve=False, Roi=True, HistogramLUT=False, colorbarText=True, **kwargs):

        super().__init__(title, **kwargs)

        self.colorbarText = colorbarText

        self.analyse_panel_update = None

        # set data extractor
        if data_group is None or data_group == 'results':
            self.data_extractor_manager = DataExtractorManager(data_path)
            self.data_extractor_manager.add_data_extractor('data_img', 'array', data_path)
            self.data_extractor_manager.add_data_extractor('x', 'array', (data_path[0], 'x'))
            self.data_extractor_manager.add_data_extractor('y', 'array', (data_path[0], 'y'))
            self.data_extractor_manager.add_data_extractor('warning', 'single', (data_path[0], 'warning'))
        else:
            self.data_extractor_manager = DataExtractorManager(data_path)
            self.data_extractor_manager.add_data_extractor('data_img', 'image', (data_group, data_path[0], data_path[1]))

        self.data_image = None

        self.setMinimumHeight(550)
        self.setMinimumWidth(550)

        if Roi is True:
            # Add y-axis data slider
            self.axsumy = self.plots.addPlot(title="")
            self.axsumy.setFixedWidth(300)
            self.sumy = self.axsumy.plot()
            self.sumy_data = self.axsumy.plot(pen=pg.mkPen(style=QtCore.Qt.DashLine, color=color_palette[1]))

        # Add imaging
        self.aximg = self.plots.addPlot(title="")
        self.img = pg.ImageItem()
        self.aximg.addItem(self.img)

        # fixe the y-axis
        if Roi is True:
            self.axsumy.setYLink(self.aximg)

        # Contrast/color control
        self.colorbar = None
        if HistogramLUT is True:
            self.colorbar = pg.HistogramLUTItem()
            self.colorbar.setImageItem(self.img)
            self.colormap = Gradients['jet']
            self.colorbar.gradient.restoreState(self.colormap)
            self.plots.addItem(self.colorbar)
        else:
            colormap = pg.colormap.getFromMatplotlib('jet')
            self.colorbar = pg.ColorBarItem(cmap=colormap, values=(0, 1))
            self.colorbar.setImageItem(self.img, insert_in=self.aximg)

        self.iso = None
        self.roi = None

        if Isocurve is True:
            # Isocurve draplotsg
            self.iso = pg.IsocurveItem(level=0, pen=color_palette[2])
            self.iso.setParentItem(self.img)
            self.iso.setZValue(5)

            # Draggable line for setting isocurve level
            self.isoLine = pg.InfiniteLine(angle=0, movable=True, pen=color_palette[2])
            self.colorbar.vb.addItem(self.isoLine)
            self.colorbar.vb.setMouseEnabled(y=False)  # makes user interaction a little easier
            self.isoLine.setValue(1000)
            self.isoLine.setZValue(1000)  # bring iso line above contrast controls
            self.isoLine.sigDragged.connect(self.updateIsocurve)

        self.plots.nextRow()

        if Roi is True:
            # Custom ROI for selecting an image region
            self.roi = pg.ROI([500, 500], [300, 300], pen={'color': 'k', 'width': 4})

            self.roi.addScaleHandle([1, 0], [0.5, 0.5])
            self.roi.addScaleHandle([0, 1], [0.5, 0.5])

            self.aximg.addItem(self.roi)
            self.roi.setZValue(10)  # make sure ROI is drawn above image
            self.roi_ax = self.plots.addPlot()
            self.roi_ax.setFixedHeight(300)
            self.roi_ax.setFixedWidth(300)

            self.roi_img = pg.ImageItem()
            self.roi_ax.addItem(self.roi_img)

            roi_colormap = pg.colormap.getFromMatplotlib('jet')
            self.roi_img.setColorMap(roi_colormap)

            self.roi.sigRegionChanged.connect(self.updateROI)
            self.colorbar.sigLevelsChanged.connect(self.updateROI)

            # Add x-axis data slider
            self.axsumx = self.plots.addPlot()
            self.axsumx.setFixedHeight(300)
            self.axsumx.setXLink(self.aximg)
            self.sumx = self.axsumx.plot()
            self.sumx_data = self.axsumx.plot(pen=pg.mkPen(style=QtCore.Qt.DashLine, color=color_palette[1]))

        # Monkey-patch the image to use our custom hover function.
        # This is generally discouraged (you should subclass ImageItem instead),
        # but it works for a very simple use like this.
        self.img.hoverEvent = self.imageHoverEvent

        self.plots.nextRow()
        self.plots.nextColumn()
        self.img.translate(-0.5, -0.5)

        self.scalex = 1
        self.scaley = 1

        self.cx = 0
        self.cy = 0

        #Add text box for colorbar and ROI
        self.colorbar_ROI_box = QSplitter()
        self.colorbar_ROI_box.setOrientation(Qt.Horizontal)

        if colorbarText is True:
            # Add text box for colorbar
            self.colorbar_box = QSplitter()
            self.colorbar_box.setOrientation(Qt.Vertical)

            self.colorbar_title = QLabel('Color Bar')
            self.colorbar_title.setAlignment(Qt.AlignCenter)

            self.colorbar_box_container = QSplitter()
            self.colorbar_box_container.setOrientation(Qt.Horizontal)

            self.colorbar_box_max_label = QLabel('  Max: ')
            self.colorbar_box_max_label.setAlignment(Qt.AlignCenter)
            self.colorbar_box_max_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.colorbar_box_max_line)
            self.colorbar_box_max_line.setValidator(double_validator)
            self.colorbar_box_max_line.setAlignment(Qt.AlignLeft)

            self.colorbar_box_min_label = QLabel('  Min: ')
            self.colorbar_box_min_label.setAlignment(Qt.AlignCenter)
            self.colorbar_box_min_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.colorbar_box_min_line)
            self.colorbar_box_min_line.setValidator(double_validator)
            self.colorbar_box_min_line.setAlignment(Qt.AlignLeft)

            self.colorbar_box_label = QSplitter()
            self.colorbar_box_label.setOrientation(Qt.Vertical)
            self.colorbar_box_line = QSplitter()
            self.colorbar_box_line.setOrientation(Qt.Vertical)
            self.colorbar_box_label.addWidget(self.colorbar_box_max_label)
            self.colorbar_box_label.addWidget(self.colorbar_box_min_label)
            self.colorbar_box_line.addWidget(self.colorbar_box_max_line)
            self.colorbar_box_line.addWidget(self.colorbar_box_min_line)

            self.colorbar_box_container.addWidget(self.colorbar_box_label)
            self.colorbar_box_container.addWidget(self.colorbar_box_line)

            self.colorbar_box.addWidget(self.colorbar_title)
            self.colorbar_box.addWidget(self.colorbar_box_container)

            self.colorbar_box_max_line.editingFinished.connect(self.update_colorbar_text)
            self.colorbar_box_min_line.editingFinished.connect(self.update_colorbar_text)

        if Roi is True:
            #Add text box for ROI
            self.roi_box = QSplitter()
            self.roi_box.setOrientation(Qt.Vertical)

            self.roi_box_title = QLabel('Region of Interest')
            self.roi_box_title.setAlignment(Qt.AlignCenter)

            self.roi_box_container = QSplitter()
            self.roi_box_container.setOrientation(Qt.Horizontal)
            self.roi_center_container = QSplitter()
            self.roi_center_container.setOrientation(Qt.Horizontal)
            self.roi_span_container = QSplitter()
            self.roi_span_container.setOrientation(Qt.Horizontal)

            self.roi_center_label = QSplitter()
            self.roi_center_label.setOrientation(Qt.Vertical)
            self.roi_center_line = QSplitter()
            self.roi_center_line.setOrientation(Qt.Vertical)

            self.roi_span_label = QSplitter()
            self.roi_span_label.setOrientation(Qt.Vertical)
            self.roi_span_line = QSplitter()
            self.roi_span_line.setOrientation(Qt.Vertical)

            self.roi_center_x_label = QLabel('  X: ')
            self.roi_center_x_label.setAlignment(Qt.AlignCenter)
            self.roi_center_x_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.roi_center_x_line)
            self.roi_center_x_line.setValidator(double_validator)
            self.roi_center_x_line.setAlignment(Qt.AlignLeft)

            self.roi_center_y_label = QLabel('  Y: ')
            self.roi_center_y_label.setAlignment(Qt.AlignCenter)
            self.roi_center_y_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.roi_center_y_line)
            self.roi_center_y_line.setValidator(double_validator)
            self.roi_center_y_line.setAlignment(Qt.AlignLeft)

            self.roi_span_x_label = QLabel('  X Span: ')
            self.roi_span_x_label.setAlignment(Qt.AlignCenter)
            self.roi_span_x_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.roi_span_x_line)
            self.roi_span_x_line.setValidator(double_validator)
            self.roi_span_x_line.setAlignment(Qt.AlignLeft)

            self.roi_span_y_label = QLabel('  Y Span: ')
            self.roi_span_y_label.setAlignment(Qt.AlignCenter)
            self.roi_span_y_line = QLineEdit(self)
            double_regex = QRegExp("^[-+0-9.]+$")
            double_validator = QRegExpValidator(double_regex, self.roi_span_y_line)
            self.roi_span_y_line.setValidator(double_validator)
            self.roi_span_y_line.setAlignment(Qt.AlignLeft)

            self.roi_center_label.addWidget(self.roi_center_x_label)
            self.roi_center_label.addWidget(self.roi_center_y_label)
            self.roi_center_line.addWidget(self.roi_center_x_line)
            self.roi_center_line.addWidget(self.roi_center_y_line)
            self.roi_span_label.addWidget(self.roi_span_x_label)
            self.roi_span_label.addWidget(self.roi_span_y_label)
            self.roi_span_line.addWidget(self.roi_span_x_line)
            self.roi_span_line.addWidget(self.roi_span_y_line)

            self.roi_center_container.addWidget(self.roi_center_label)
            self.roi_center_container.addWidget(self.roi_center_line)
            self.roi_span_container.addWidget(self.roi_span_label)
            self.roi_span_container.addWidget(self.roi_span_line)

            self.roi_box_container.addWidget(self.roi_center_container)
            self.roi_box_container.addWidget(self.roi_span_container)
            self.roi_box.addWidget(self.roi_box_title)
            self.roi_box.addWidget(self.roi_box_container)

            self.roi_center_x_line.editingFinished.connect(self.updateROI_text)
            self.roi_center_y_line.editingFinished.connect(self.updateROI_text)
            self.roi_span_x_line.editingFinished.connect(self.updateROI_text)
            self.roi_span_y_line.editingFinished.connect(self.updateROI_text)

            self.colorbar_ROI_box.addWidget(self.colorbar_box)
            self.colorbar_ROI_box.addWidget(self.roi_box)

        self.bottom.addWidget(self.colorbar_ROI_box)

    def update(self, h5_path):

        data = self.data_extractor_manager.get_data(h5_path)
        self.data_image = data['data_img']

        try:
            x = data['x']
            y = data['y']
            warning = data['warning']
        except:
            x = None
            y = None
            warning = None

        if x is None:
            x = np.linspace(0, self.data_image.shape[0], self.data_image.shape[0])
        if y is None:
            y = np.linspace(0, self.data_image.shape[1], self.data_image.shape[1])

        # update plots
        if self.colorbarText is True:
            self.img.setImage(self.data_image.T, autoLevels=False)
        else:
            self.img.setImage(self.data_image.T, autoLevels=True)
            self.colorbar.setLevels((np.min(self.data_image), np.max(self.data_image)))

        if self.iso is not None:
            self.iso.setData(self.data_image.T)

        # set position and scale of image
        newscalex = x[1] - x[0]
        newscaley = y[1] - y[0]

        transx = (x[0] - (self.cx - 0.5 * self.scalex)) / newscalex - 0.5
        transy = (y[0] - (self.cy - 0.5 * self.scaley)) / newscaley - 0.5

        self.img.scale(newscalex / self.scalex, newscaley / self.scaley)
        self.img.translate(transx, transy)

        self.scalex = newscalex
        self.scaley = newscaley

        self.cx = x[0]
        self.cy = y[0]

        # update the region of interest
        if self.roi is not None:
            self.updateROI()

        # update table and warning
        self.update_warning(warning)

        if self.analyse_panel_update is not None:
            self.analyse_panel_update()

    def updateIsocurve(self):
        self.iso.setLevel(self.isoLine.value())

    def update_colorbar_text(self):
        colorbar_max = self.colorbar_box_max_line.text()
        colorbar_min = self.colorbar_box_min_line.text()
        colorbar_max = float(colorbar_max)
        colorbar_min = float(colorbar_min)
        try:
            self.colorbar.setLevels(min=colorbar_min, max=colorbar_max)
        except:
            self.colorbar.setLevels(low=colorbar_min, high=colorbar_max)
        if self.roi is not None:
            self.updateROI()

    def updateROI(self):
        selected = self.roi.getArrayRegion(self.data_image.T, self.img)
        try:
            levels = self.colorbar.getLevels()
        except:
            levels = self.colorbar.levels()
        self.roi_img.setImage(selected, levels=levels)

        colorbar_min, colorbar_max = levels
        self.colorbar_box_max_line.setText(str(colorbar_max))
        self.colorbar_box_min_line.setText(str(colorbar_min))

        x_center, y_center = self.roi.pos()
        x_span, y_span = self.roi.size()
        x_center = x_center + x_span / 2
        y_center = y_center + y_span / 2
        self.roi_center_x_line.setText(str(x_center))
        self.roi_center_y_line.setText(str(y_center))
        self.roi_span_x_line.setText(str(x_span))
        self.roi_span_y_line.setText(str(y_span))

        roi_pos = self.roi.pos()

        pos = int(selected.shape[0]/2)
        x = np.linspace(roi_pos[1], roi_pos[1] + selected.shape[1], selected.shape[1])
        self.sumy_data.setData(x=np.squeeze(selected[pos:pos+1, :]), y=x)

        pos = int(selected.shape[1] / 2)
        x = np.linspace(roi_pos[0], roi_pos[0] + selected.shape[0], selected.shape[0])
        self.sumx_data.setData(x=x, y=np.squeeze(selected[:, pos:pos + 1]))

    def updateROI_text(self):
        x_center = float(self.roi_center_x_line.text())
        y_center = float(self.roi_center_y_line.text())
        x_span = float(self.roi_span_x_line.text())
        y_span = float(self.roi_span_y_line.text())
        x = x_center - x_span / 2
        y = y_center - y_span / 2
        self.roi.setPos((x, y))
        self.roi.setSize((x_span, y_span))

    def imageHoverEvent(self, event):
        """Show the position, pixel, and value under the mouse cursor.
        """
        if event.isExit():
            self.aximg.setTitle("")
            return
        pos = event.pos()
        i, j = pos.y(), pos.x()
        i = int(np.clip(i, 0, self.data_image.shape[0] - 1))
        j = int(np.clip(j, 0, self.data_image.shape[1] - 1))
        val = self.data_image[i, j]
        ppos = self.img.mapToParent(pos)
        x, y = ppos.x(), ppos.y()
        self.aximg.setTitle("pos: (%0.1f, %0.1f) value: %g" % (x, y, val))
