class ComponentPlotWidget(SpectraPlotWidget):
    def __init__(self, *args, **kwargs):
        super(ComponentPlotWidget, self).__init__(linePos=800,
                                                  txtPosRatio=0.35,
                                                  *args,
                                                  **kwargs)
        self.cross = PlotDataItem([800], [0],
                                  symbolBrush=(255, 255, 255),
                                  symbolPen=(255, 255, 255),
                                  symbol='+',
                                  symbolSize=25)
        self._x, self._y = None, None
        self.ymax, self.zmax = 0, 100

    def getEnergy(self):
        if self._y is not None:
            x_val = self.line.value()
            idx = val2ind(x_val, self._x)
            x_val = self._x[idx]
            y_val = self._y[idx]
            txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\
                                                 X = {x_val: .2f}, Y = {y_val: .4f}</div>'

            self.txt.setHtml(txt_html)
            self.cross.setData([x_val], [y_val])

    def plot(self, x, y, *args, **kwargs):
        # set up infinity line and get its position
        plot_item = self.plotItem.plot(x, y, *args, **kwargs)
        self.addItem(self.line)
        self.addItem(self.cross)
        x_val = self.line.value()
        idx = val2ind(x_val, x)
        x_val = x[idx]
        y_val = y[idx]
        txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\
                                     X = {x_val: .2f}, Y = {y_val: .4f}</div>'

        self.txt.setHtml(txt_html)
        self.txt.setZValue(self.zmax - 1)
        self.cross.setData([x_val], [y_val])
        self.cross.setZValue(self.zmax)
        ymax = max(y)
        if ymax > self.ymax:
            self.ymax = ymax
        self._x, self._y = x, y
        r = self.txtPosRatio
        self.txt.setPos(r * x[-1] + (1 - r) * x[0], self.ymax)
        self.addItem(self.txt)
        return plot_item
class SpectraPlotWidget(PlotWidget):
    sigEnergyChanged = Signal(object)

    def __init__(self, linePos=650, txtPosRatio=0.35, invertX=True, *args, **kwargs):
        """
        A widget to display a 1D spectrum
        :param linePos: the initial position of the InfiniteLine
        :param txtPosRatio: a coefficient that determines the relative position of the textItem
        :param invertX: whether to invert X-axis
        """
        super(SpectraPlotWidget, self).__init__(*args, **kwargs)
        self._data = None
        assert (txtPosRatio >= 0) and (txtPosRatio <= 1), 'Please set txtPosRatio value between 0 and 1.'
        self.txtPosRatio = txtPosRatio
        self.positionmap = dict()
        self.wavenumbers = None
        self._meanSpec = True  # whether current spectrum is a mean spectrum
        self.line = InfiniteLine(movable=True)
        self.line.setPen((255, 255, 0, 200))
        self.line.setValue(linePos)
        self.line.sigPositionChanged.connect(self.sigEnergyChanged)
        self.line.sigPositionChanged.connect(self.getEnergy)
        self.addItem(self.line)
        self.cross = PlotDataItem([linePos], [0], symbolBrush=(255, 0, 0), symbolPen=(255, 0, 0), symbol='+',
                                  symbolSize=20)
        self.cross.setZValue(100)
        self.addItem(self.cross)
        self.txt = TextItem()
        self.getViewBox().invertX(invertX)
        self.spectrumInd = 0
        self.selectedPixels = None
        self._y = None

    def getEnergy(self, lineobject):
        if self._y is not None:
            x_val = lineobject.value()
            idx = val2ind(x_val, self.wavenumbers)
            x_val = self.wavenumbers[idx]
            y_val = self._y[idx]
            if not self._meanSpec:
                txt_html = toHtml(f'Spectrum #{self.spectrumInd}')
            else:
                txt_html = toHtml(f'{self._mean_title}')

            txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}')
            self.txt.setHtml(txt_html)
            self.cross.setData([x_val], [y_val])

    def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs):
        self.header = header
        self.field = field
        # get wavenumbers
        spectraEvent = next(header.events(fields=['spectra']))
        self.wavenumbers = spectraEvent['wavenumbers']
        self.N_w = len(self.wavenumbers)
        self.rc2ind = spectraEvent['rc_index']
        # make lazy array from document
        data = None
        try:
            data = header.meta_array(field)
        except IndexError:
            msg.logMessage(f'Header object contained no frames with field {field}.', msg.ERROR)

        if data is not None:
            # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2])
            self._data = data

    def showSpectra(self, i=0):
        if (self._data is not None) and (i < len(self._data)):
            self.getViewBox().clear()
            self._meanSpec = False
            self.spectrumInd = i
            self.plot(self.wavenumbers, self._data[i])

    def getSelectedPixels(self, selectedPixels):
        self.selectedPixels = selectedPixels
        # print(selectedPixels)

    def clearAll(self):
        # remove legend
        _legend = self.plotItem.legend
        if (_legend is not None) and (_legend.scene() is not None):
            _legend.scene().removeItem(_legend)
        self.getViewBox().clear()

    def showMeanSpectra(self):
        self._meanSpec = True
        self.getViewBox().clear()
        if self.selectedPixels is not None:
            n_spectra = len(self.selectedPixels)
            tmp = np.zeros((n_spectra, self.N_w))
            for j in range(n_spectra):  # j: jth selected pixel
                row_col = tuple(self.selectedPixels[j])
                tmp[j, :] = self._data[self.rc2ind[row_col]]
            self._mean_title = f'ROI mean of {n_spectra} spectra'
        else:
            n_spectra = len(self._data)
            tmp = np.zeros((n_spectra, self.N_w))
            for j in range(n_spectra):
                tmp[j, :] = self._data[j]
            self._mean_title = f'Total mean of {n_spectra} spectra'
        if n_spectra > 0:
            meanSpec = np.mean(tmp, axis=0)
        else:
            meanSpec = np.zeros_like(self.wavenumbers) + 1e-3
        self.plot(self.wavenumbers, meanSpec)

    def plot(self, x, y, *args, **kwargs):
        # set up infinity line and get its position
        self.plotItem.plot(x, y, *args, **kwargs)
        self.addItem(self.line)
        self.addItem(self.cross)
        x_val = self.line.value()
        if x_val == 0:
            y_val = 0
        else:
            idx = val2ind(x_val, self.wavenumbers)
            x_val = self.wavenumbers[idx]
            y_val = y[idx]

        if not self._meanSpec:
            txt_html = toHtml(f'Spectrum #{self.spectrumInd}')
        else:
            txt_html = toHtml(f'{self._mean_title}')

        txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}')
        self.txt.setHtml(txt_html)
        ymax = np.max(y)
        self._y = y
        r = self.txtPosRatio
        self.txt.setPos(r * x[-1] + (1 - r) * x[0], ymax)
        self.cross.setData([x_val], [y_val])
        self.addItem(self.txt)