from pymoku.applets import settings, moku_key
from pymoku.applets.device_config import MokuConfigDialog
from pymoku.finder import Finder, MokuInfo

from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import (QWidget, QSizePolicy, QPushButton, QVBoxLayout,
                               QLabel, QMenu, QToolBar, QLineEdit, QScrollArea,
                               QHBoxLayout)

HW_NAMES = {1.0: 'Moku:Lab', 2.0: 'Moku:Lab', 3.0: 'Moku:Pro',
            4.0: 'Moku:Go', 5.0: 'ZCU208', 6.0: 'MokuDelta'}


class MokuItemWidget(QWidget):
    launch_moku = Signal(object)

    def __init__(self, minfo, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_minfo(minfo)
        self.favorite = settings.value(f'{moku_key(self.minfo)}/favorite', False, bool)
        lyt = QVBoxLayout()
        name_lbl = QLabel(minfo.name)
        name_lbl.setAlignment(Qt.AlignCenter)
        hw_lbl = QLabel(HW_NAMES[minfo.hwver])
        hw_lbl.setAlignment(Qt.AlignCenter)
        serial_lbl = QLabel(f'{minfo.serial:d}')
        serial_lbl.setAlignment(Qt.AlignCenter)

        lyt.addStretch()
        lyt.addWidget(name_lbl)
        lyt.addWidget(hw_lbl)
        lyt.addWidget(serial_lbl)

        hlyt = QHBoxLayout()

        self.fav_btn = QPushButton('⭐️' if self.favorite else '☆')
        self.fav_btn.setToolTip('Toggle favorite')
        self.fav_btn.setStyleSheet("QPushButton { border: none; }")
        self.fav_btn.clicked.connect(self.set_favorite)
        self.fav_btn.setCheckable(True)
        self.fav_btn.setChecked(self.favorite)

        hlyt.addStretch()
        hlyt.addWidget(self.fav_btn)

        lyt.addStretch()
        lyt.addLayout(hlyt)

        self.setLayout(lyt)

        self.resize(130, 130)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

    def set_minfo(self, minfo):
        self.minfo = minfo
        # TODO refresh labels

    def toggle_favorite(self):
        self.set_favorite(checked=not self.favorite)

    def set_favorite(self, checked):
        self.favorite = checked

        if checked:
            self.fav_btn.setText('⭐️')
            settings.setValue(f'{moku_key(self.minfo)}/favorite', True)
        else:
            self.fav_btn.setText('☆')
            settings.remove(f'{moku_key(self.minfo)}/favorite')

    def open_config(self):
        diag = MokuConfigDialog(self, self.minfo)
        diag.exec()

    def paintEvent(self, event):
        paint = QPainter()
        paint.begin(self)
        paint.drawRoundedRect(0, 0, self.width() - 1, self.height() - 1, 5, 5)
        paint.end()

    def mouseDoubleClickEvent(self, event):
        self.connect_to_moku()  # TODO is this deferred?

    def connect_to_moku(self):
        self.launch_moku.emit(self.minfo)

    def contextMenuEvent(self, event):
        menu = QMenu(self)
        menu.addAction('Connect', self.connect_to_moku)
        menu.addSeparator()
        menu.addAction('Configure', self.open_config)
        menu.addAction('Favorite', self.toggle_favorite)
        menu.popup(event.globalPos())


class MokuItemPane(QWidget):
    SPACING = 140
    MARGIN = 10

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.items = {}
        self.set_items({})

    def _num_cols(self):
        return max((self.width() - (self.MARGIN * 2)) // self.SPACING, 1)

    def _gen_x_y(self, length):
        for y in range(length // self._num_cols() + 1):
            for x in range(self._num_cols()):
                yield self.MARGIN + x * self.SPACING, self.MARGIN + y * self.SPACING

    def set_items(self, items):
        for k, v in self.items.items():
            if k not in items:
                v.hide()

        y = 0
        for (x, y), (k, item) in zip(self._gen_x_y(len(items)), items.items()):
            # TODO would be nice to animate these
            item.setParent(self)
            item.move(x, y)
            item.show()
        self.setMinimumHeight(y + self.SPACING + self.MARGIN)
        self.items = items

    def resizeEvent(self, event):
        self.set_items(self.items)
        super().resizeEvent(event)


class MokuListWidget(QWidget):
    add_moku = Signal(str, object)
    remove_moku = Signal(str)
    launch_moku = Signal(object)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.items = {}
        self.pane = MokuItemPane()
        self.moku_finder = None

        scroll = QScrollArea()
        scroll.setWidget(self.pane)
        scroll.setWidgetResizable(True)

        lyt = QVBoxLayout()
        lyt.setSpacing(0)
        lyt.setContentsMargins(0, 0, 0, 0)
        lyt.addWidget(self._create_toolbar())
        lyt.addWidget(scroll)

        self.setLayout(lyt)

        self.add_moku.connect(self._add_moku)
        self.remove_moku.connect(self._remove_moku)

    def _create_toolbar(self):
        toolbar = QToolBar('Utils')
        toolbar.setObjectName('utilToolbar')

        toolbar.addSeparator()

        self.ip_addr = QLineEdit(settings.value('recent/ip', defaultValue=''))
        self.ip_addr.setPlaceholderText("IP Address")
        self.ip_addr.returnPressed.connect(self._connect_btn)
        toolbar.addWidget(self.ip_addr)

        toolbar.addAction('Connect', self._connect_btn)

        toolbar.addSeparator()

        filt_text = QLineEdit()
        filt_text.setPlaceholderText('Filter')
        toolbar.addWidget(filt_text)
        self.filt_text = filt_text
        filt_text.textEdited.connect(self._filter)

        settings.beginGroup('device_list/filter')
        self.fav_only = toolbar.addAction('☆', self._filter)
        self.fav_only.setCheckable(True)
        self.fav_only.setChecked(settings.value('favorite', False, bool))
        self.fav_only.setToolTip('Favorite only')

        self.active_only = toolbar.addAction('⦿', self._filter)
        self.active_only.setCheckable(True)
        self.active_only.setChecked(settings.value('active', False, bool))
        self.active_only.setToolTip('Active only')

        settings.endGroup()

        return toolbar

    def _filter(self):
        settings.beginGroup('device_list/filter')
        settings.setValue('favorite', self.fav_only.isChecked())
        settings.setValue('active', self.active_only.isChecked())
        settings.endGroup()
        self.pane.set_items(self.filtered_sorted())

    def filtered_sorted(self):
        filt = {}
        txt = self.filt_text.text()
        fav = self.fav_only.isChecked()
        active = self.active_only.isChecked()
        for k, v in self.items.items():
            if fav and not v.favorite:
                continue

            if active and not v.isEnabled():
                continue

            if (txt in v.minfo.name
                    or txt in str(v.minfo.serial)
                    or txt in v.minfo.ip_addr
                    or txt in HW_NAMES[v.minfo.hwver]):
                filt[k] = v
        return filt

    def _connect_btn(self, *args):
        ip_addr = self.ip_addr.text()
        self.launch_moku.emit(MokuInfo(ip_addr=ip_addr))

    def _add_moku(self, name, minfo):
        if name in self.items:
            self.items[name].set_minfo(minfo)
        else:
            self.items[name] = MokuItemWidget(minfo)
            self.items[name].launch_moku.connect(self.launch_moku.emit)
        self.items[name].setEnabled(True)
        self.pane.set_items(self.filtered_sorted())

    def _remove_moku(self, name):
        if name in self.items:
            self.items[name].setEnabled(False)

    def start_discovery(self):
        if self.moku_finder:
            self.moku_finder.close()

        self.moku_finder = Finder(on_add=self.add_moku.emit,
                                  on_remove=self.remove_moku.emit)
        self.moku_finder.start()

    def close(self):
        if self.moku_finder:
            self.moku_finder.close()


if __name__ == '__main__':
    import sys
    import lorem
    import random
    import string

    from PySide6.QtWidgets import QApplication
    from PySide6.QtCore import QTimer

    random.seed(0)

    def gen_minfo():
        minfo = MokuInfo(name=' '.join(lorem.sentence().split(' ')[0:random.randint(1, 2)]),
                         netver=0,
                         fwver=random.choice([587, 590, 591]),
                         hwver=random.choice(list(HW_NAMES.keys())),
                         serial=random.randint(0, 999999),
                         colour=random.choice(['black', 'blue', 'red', 'silver', 'aqua']),
                         bootmode=random.choice(['normal', 'developer', 'recovery']),
                         ip_addr=f'10.6.119.{random.randint(0, 255):d}')
        return minfo

    app = QApplication(sys.argv)

    w = MokuListWidget()
    w.resize(800, 600)
    for i in range(random.randrange(6, 15)):
        mid = ''.join(random.choices(string.ascii_letters, k=20))
        w._add_moku(mid, gen_minfo())
    w.launch_moku.connect(print)
    w.show()

    def add_remove():
        if random.choices([True, False], weights=[20, len(w.items)])[0]:
            mid = ''.join(random.choices(string.ascii_letters, k=20))
            w._add_moku(mid, gen_minfo())
        else:
            w._remove_moku(random.choice(list(w.items.keys())))

    timer = QTimer()
    timer.timeout.connect(add_remove)
    timer.setInterval(2000)
    # timer.start()

    app.exec()
