#!/usr/bin/env python


__license__   = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'

from operator import attrgetter

from qt.core import QDialog, QDialogButtonBox, QIcon, QLabel, QSize, Qt, QTabWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout, pyqtSignal

from calibre.gui2 import file_icon_provider
from calibre.utils.icu import lower as icu_lower


def browser_item(f, parent):
    name = f.name
    if not f.is_folder:
        name += f' [{f.last_mod_string}]'
    ans = QTreeWidgetItem(parent, [name])
    ans.setData(0, Qt.ItemDataRole.UserRole, f.full_path)
    if f.is_folder:
        ext = 'dir'
    else:
        ext = f.name.rpartition('.')[-1]
    ans.setData(0, Qt.ItemDataRole.DecorationRole, file_icon_provider().icon_from_ext(ext))

    return ans


class Storage(QTreeWidget):

    def __init__(self, storage, show_files=False, item_func=browser_item):
        QTreeWidget.__init__(self)
        self.item_func = item_func
        self.show_files = show_files
        self.create_children(storage, self)
        self.name = storage.name
        self.object_id = storage.persistent_id
        self.setMinimumHeight(350)
        self.setHeaderHidden(True)
        self.storage = storage

    def create_children(self, f, parent):
        for child in sorted(f.folders, key=attrgetter('name')):
            i = self.item_func(child, parent)
            self.create_children(child, i)
        if self.show_files:
            for child in sorted(f.files, key=attrgetter('name')):
                i = self.item_func(child, parent)

    @property
    def current_item(self):
        item = self.currentItem()
        if item is not None:
            return (self.object_id, item.data(0, Qt.ItemDataRole.UserRole))
        return None


class Folders(QTabWidget):

    selected = pyqtSignal()

    def __init__(self, filesystem_cache, show_files=True):
        QTabWidget.__init__(self)
        self.fs = filesystem_cache
        for storage in self.fs.entries:
            w = Storage(storage, show_files)
            self.addTab(w, w.name)
            w.doubleClicked.connect(self.selected)

        self.setCurrentIndex(0)

    @property
    def current_item(self):
        w = self.currentWidget()
        if w is not None:
            return w.current_item


class Browser(QDialog):

    def __init__(self, filesystem_cache, show_files=True, parent=None):
        QDialog.__init__(self, parent)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        self.folders = cw = Folders(filesystem_cache, show_files=show_files)
        l.addWidget(cw)
        bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok|QDialogButtonBox.StandardButton.Cancel)
        l.addWidget(bb)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.setMinimumSize(QSize(500, 500))
        self.folders.selected.connect(self.accept)
        self.setWindowTitle(_('Choose folder on device'))
        self.setWindowIcon(QIcon.ic('devices/tablet.png'))

    @property
    def current_item(self):
        return self.folders.current_item


class IgnoredFolders(QDialog):

    def __init__(self, dev, ignored_folders=None, parent=None):
        QDialog.__init__(self, parent)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        self.la = la = QLabel('<p>'+ _('<b>Scanned folders:</b>') + ' ' +
            _('You can select which folders calibre will '
              'scan when searching this device for books.'))
        la.setWordWrap(True)
        l.addWidget(la)
        self.tabs = QTabWidget(self)
        l.addWidget(self.tabs)
        self.widgets = []

        for storage in dev.filesystem_cache.entries:
            self.dev = dev
            w = Storage(storage, item_func=self.create_item)
            del self.dev
            self.tabs.addTab(w, storage.name)
            self.widgets.append(w)
            w.itemChanged.connect(self.item_changed)

        self.la2 = la = QLabel(_(
            'If you a select a previously unselected folder, any sub-folders'
            ' will not be visible until you restart calibre.'))
        l.addWidget(la)
        la.setWordWrap(True)

        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok |
                                   QDialogButtonBox.StandardButton.Cancel)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.sab = self.bb.addButton(_('Select &all'), QDialogButtonBox.ButtonRole.ActionRole)
        self.sab.clicked.connect(self.select_all)
        self.snb = self.bb.addButton(_('Select &none'), QDialogButtonBox.ButtonRole.ActionRole)
        self.snb.clicked.connect(self.select_none)
        l.addWidget(self.bb)
        self.setWindowTitle(_('Choose folders to scan'))
        self.setWindowIcon(QIcon.ic('devices/tablet.png'))

        self.resize(600, 500)

    def item_changed(self, item, column):
        w = item.treeWidget()
        root = w.invisibleRootItem()
        w.itemChanged.disconnect(self.item_changed)
        try:
            if item.checkState(0) == Qt.CheckState.Checked:
                # Ensure that the parents of this item are checked
                p = item.parent()
                while p is not None and p is not root:
                    p.setCheckState(0, Qt.CheckState.Checked)
                    p = p.parent()
            # Set the state of all descendants to the same state as this item
            for child in self.iterchildren(item):
                child.setCheckState(0, item.checkState(0))
        finally:
            w.itemChanged.connect(self.item_changed)

    def iterchildren(self, node):
        ' Iterate over all descendants of node '
        for i in range(node.childCount()):
            child = node.child(i)
            yield child
            yield from self.iterchildren(child)

    def create_item(self, f, parent):
        name = f.name
        ans = QTreeWidgetItem(parent, [name])
        ans.setData(0, Qt.ItemDataRole.UserRole, '/'.join(f.full_path[1:]))
        ans.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
        ans.setCheckState(0,
            Qt.CheckState.Unchecked if self.dev.is_folder_ignored(f.storage_id, f.full_path[1:]) else Qt.CheckState.Checked)
        ans.setData(0, Qt.ItemDataRole.DecorationRole, file_icon_provider().icon_from_ext('dir'))
        return ans

    def select_all(self):
        w = self.tabs.currentWidget()
        for i in range(w.invisibleRootItem().childCount()):
            c = w.invisibleRootItem().child(i)
            c.setCheckState(0, Qt.CheckState.Checked)

    def select_none(self):
        w = self.tabs.currentWidget()
        for i in range(w.invisibleRootItem().childCount()):
            c = w.invisibleRootItem().child(i)
            c.setCheckState(0, Qt.CheckState.Unchecked)

    @property
    def ignored_folders(self):
        ans = {}
        for w in self.widgets:
            folders = set()
            for node in self.iterchildren(w.invisibleRootItem()):
                if node.checkState(0) == Qt.CheckState.Checked:
                    continue
                path = str(node.data(0, Qt.ItemDataRole.UserRole) or '')
                parent = path.rpartition('/')[0]
                if '/' not in path or icu_lower(parent) not in folders:
                    folders.add(icu_lower(path))
            ans[str(w.storage.storage_id)] = list(folders)
        return ans


def setup_device():
    from calibre.devices.mtp.driver import MTP_DEVICE
    from calibre.devices.scanner import DeviceScanner
    s = DeviceScanner()
    s.scan()
    dev = MTP_DEVICE(None)
    dev.startup()
    cd = dev.detect_managed_devices(s.devices)
    if cd is None:
        raise ValueError('No MTP device found')
    dev.open(cd, 'test')
    return dev


def browse():
    from calibre.gui2 import Application
    app = Application([])
    app
    dev = setup_device()
    d = Browser(dev.filesystem_cache)
    d.exec()
    dev.shutdown()
    return d.current_item


def ignored_folders():
    from calibre.gui2 import Application
    app = Application([])
    app
    dev = setup_device()
    d = IgnoredFolders(dev)
    d.exec()
    dev.shutdown()
    return d.ignored_folders


if __name__ == '__main__':
    print(browse())
    # print('Ignored:', ignored_folders())
