import gc
import string
import random
import os
import sys

try:
    from PySide6.QtCore import (QAbstractListModel, QElapsedTimer, QLibraryInfo, QModelIndex,
                               QSize, QTimer, qVersion, Qt, SIGNAL)
    from PySide6.QtGui  import QIcon, QShortcut
    from PySide6.QtWidgets import (QApplication,QWidget, QStyledItemDelegate, QListView,
                                   QStyleOptionViewItem)
except ImportError:
    from PySide2.QtCore import (QAbstractListModel, QElapsedTimer, QLibraryInfo, QModelIndex,
                               QSize, QTimer, qVersion, Qt, SIGNAL)
    from PySide2.QtGui  import QIcon
    from PySide2.QtWidgets import (QApplication,QWidget, QStyledItemDelegate, QListView,
                                   QShortcut, QStyleOptionViewItem)



elapsed_timer = None


PMAP_COMMAND = f'pmap {os.getpid()} | tail -n 1'


def print_timestamp():
    t = int(elapsed_timer. elapsed() / 1000)
    sys.stdout.write(f'{t}s ')
    sys.stdout.flush()


def print_mem():
    print_timestamp()
    os.system(PMAP_COMMAND)


def gc_collect():
    print_timestamp()
    print('running gc.collect()')
    gc.collect()


class Model(QAbstractListModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
        self.dataset = [
            ''.join(random.choices(chars, k=50))
            for _ in range(1000)
        ]
        self.icon =QIcon.fromTheme('folder')

    def rowCount(self, parent=QModelIndex()):
        if not parent.isValid():
            return len(self.dataset)
        return 0

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid() or index.column() != 0:
            return

        if role == Qt.DisplayRole:
            return self.dataset[index.row()]

        if role == Qt.DecorationRole:
            return self.icon


class Delegate(QStyledItemDelegate):
    def sizeHint(
        self, option: QStyleOptionViewItem, index: QModelIndex
    ) -> QSize:
        """
        I initially didn't implement this in my first example to keep things
        simple but it may have been a mistake as including it makes the leak
        quite bigger and easier to notice.

        Just imagine this delegate actually needs to call .data() to compute
        the size hint.
        """
        text = index.data(Qt.DisplayRole)
        icon:QIcon = index.data(Qt.DecorationRole)
        return QSize(400, 400)

    def paint(self, painter, options, index):
        icon:QIcon = index.data(Qt.DecorationRole)
        painter.drawPixmap(options.rect.topLeft(), icon.pixmap(300, 300))
        text = index.data(Qt.DisplayRole)
        painter.drawText(options.rect.bottomLeft(), text)


class View(QListView):
    def __init__(self, *args, **kwargs ):
        super().__init__(*args, **kwargs)
        self.setResizeMode(QListView.Adjust)
        self.setWrapping(True)
        self.setViewMode(QListView.IconMode)
        self.setUniformItemSizes(False)


if __name__ == '__main__':
    opt_workaround = '-w' in sys.argv

    print('Python {}.{}'.format(sys.version_info[0], sys.version_info[1]))
    print(QLibraryInfo.build(), f"\n\nworkaround={opt_workaround}\n\nCTRL-Q to quit, CTRL-G for gc.collect()\n")
    app = QApplication([])

    elapsed_timer = QElapsedTimer()
    elapsed_timer.start()
    log_timer = None
    if sys.platform == 'linux' and not '-t' in sys.argv:
        log_timer = QTimer()
        log_timer.timeout.connect(print_mem)
        log_timer.start(500)

    view = View()
    view.setWindowTitle(qVersion())
    delegate = Delegate(parent=view)
    view.setItemDelegate(delegate)

    model = Model(parent=view)
    view.setModel(model)

    sc = QShortcut(Qt.CTRL | Qt.Key_Q, view)
    sc.activated.connect(view.close)
    sc = QShortcut(Qt.CTRL | Qt.Key_G, view)
    sc.activated.connect(gc_collect)

    delta = 10
    availableGeometry = view.screen().availableGeometry()
    MAX_WIDTH = availableGeometry.width()
    MAX_HEIGHT = availableGeometry.height()
    def show_mem_leak():
        global delta, MAX_WIDTH, MAX_HEIGHT
        if view.size().width() >= MAX_WIDTH - 10:
            delta = -10
        elif view.size().width() <  100:
            delta = 10

        old_width = view.size().width()
        new_width = old_width + delta
        view.resize(new_width, new_width)
        view.repaint()

    timer = QTimer(parent=view)
    timer.setSingleShot(False)
    timer.setInterval(5)
    timer.setTimerType(Qt.PreciseTimer)
    timer.timeout.connect(show_mem_leak)
    timer.start()

    app.connect(SIGNAL("aboutToQuit()"), view.destroy)
    view.show()
    app.exec_()
