import numpy as np
from .util import prepare_for_display, window_manager
from ..._shared.utils import warn

from qtpy.QtWidgets import (QApplication, QLabel, QMainWindow, QWidget,
                            QGridLayout)
from qtpy.QtGui import QImage, QPixmap
from qtpy import QtCore

# We try to acquire the gui lock first or else the gui import might
# trample another GUI's PyOS_InputHook.
window_manager.acquire('qt')

app = None


class ImageLabel(QLabel):
    def __init__(self, parent, arr):
        QLabel.__init__(self)

        # we need to hold a reference to
        # arr because QImage doesn't copy the data
        # and the buffer must be alive as long
        # as the image is alive.
        self.arr = arr

        # we also need to pass in the row-stride to
        # the constructor, because we can't guarantee
        # that every row of the numpy data is
        # 4-byte aligned. Which Qt would require
        # if we didn't pass the stride.
        self.img = QImage(arr.data, arr.shape[1], arr.shape[0],
                          arr.strides[0], QImage.Format_RGB888)
        self.pm = QPixmap.fromImage(self.img)
        self.setPixmap(self.pm)
        self.setAlignment(QtCore.Qt.AlignTop)
        self.setMinimumSize(100, 100)

    def resizeEvent(self, evt):
        width = self.width()
        pm = QPixmap.fromImage(self.img)
        self.pm = pm.scaledToWidth(width)
        self.setPixmap(self.pm)


class ImageWindow(QMainWindow):
    def __init__(self, arr, mgr):
        QMainWindow.__init__(self)
        self.setWindowTitle('skimage')
        self.mgr = mgr
        self.main_widget = QWidget()
        self.layout = QGridLayout(self.main_widget)
        self.setCentralWidget(self.main_widget)

        self.label = ImageLabel(self, arr)
        self.layout.addWidget(self.label, 0, 0)
        self.layout.addLayout
        self.mgr.add_window(self)
        self.main_widget.show()

    def closeEvent(self, event):
        # Allow window to be destroyed by removing any
        # references to it
        self.mgr.remove_window(self)


def imread(filename):
    """
    Read an image using QT's QImage.load
    """
    qtimg = QImage()
    if not qtimg.load(filename):
        # QImage.load() returns false on failure, so raise an exception
        raise IOError('Unable to load file %s' % filename)
    if qtimg.depth() == 1:
        raise IOError('1-bit images currently not supported')
    # TODO: Warn about other odd formats we don't currently handle properly,
    # such as the odd 16-bit packed formats QT supports
    arrayptr = qtimg.bits()
    # QT may pad the image, so we need to use bytesPerLine, not width for
    # the conversion to a numpy array
    bytes_per_pixel = qtimg.depth() // 8
    pixels_per_line = qtimg.bytesPerLine() // bytes_per_pixel
    img_size = pixels_per_line * qtimg.height() * bytes_per_pixel
    arrayptr.setsize(img_size)
    img = np.array(arrayptr)
    # Reshape and trim down to correct dimensions
    if bytes_per_pixel > 1:
        img = img.reshape((qtimg.height(), pixels_per_line, bytes_per_pixel))
        img = img[:, :qtimg.width(), :]
    else:
        img = img.reshape((qtimg.height(), pixels_per_line))
        img = img[:, :qtimg.width()]
    # Strip qt's false alpha channel if needed
    # and reorder color axes as required
    if bytes_per_pixel == 4 and not qtimg.hasAlphaChannel():
        img = img[:, :, 2::-1]
    elif bytes_per_pixel == 4:
        img[:, :, 0:3] = img[:, :, 2::-1]
    return img

def imshow(arr, fancy=False):
    global app
    if not app:
        app = QApplication([])

    arr = prepare_for_display(arr)

    if not fancy:
        iw = ImageWindow(arr, window_manager)
    else:
        from .skivi import SkiviImageWindow
        iw = SkiviImageWindow(arr, window_manager)

    iw.show()


def _app_show():
    global app
    if app and window_manager.has_windows():
        app.exec_()
    else:
        print('No images to show.  See `imshow`.')


def imsave(filename, img, format_str=None):
    # we can add support for other than 3D uint8 here...
    img = prepare_for_display(img)
    qimg = QImage(img.data, img.shape[1], img.shape[0],
                  img.strides[0], QImage.Format_RGB888)
    if _is_filelike(filename):
        byte_array = QtCore.QByteArray()
        qbuffer = QtCore.QBuffer(byte_array)
        qbuffer.open(QtCore.QIODevice.ReadWrite)
        saved = qimg.save(qbuffer, format_str.upper())
        qbuffer.seek(0)
        filename.write(qbuffer.readAll().data())
        qbuffer.close()
    else:
        saved = qimg.save(filename)
    if not saved:
        from textwrap import dedent
        msg = dedent(
            '''The image was not saved. Allowable file formats
            for the QT imsave plugin are:
            BMP, JPG, JPEG, PNG, PPM, TIFF, XBM, XPM''')
        raise RuntimeError(msg)


def _is_filelike(possible_filelike):
    return callable(getattr(possible_filelike, 'write', None))
