#!/usr/bin/env python
# License: GPLv3 Copyright: 2013, Kovid Goyal <kovid at kovidgoyal.net>

import glob
import os
import shutil
from functools import partial

from qt.core import (
    QAbstractItemView,
    QApplication,
    QDialog,
    QDialogButtonBox,
    QIcon,
    QLabel,
    QListView,
    QListWidget,
    QListWidgetItem,
    QSize,
    Qt,
    QTimer,
    QVBoxLayout,
)

from calibre.constants import config_dir
from calibre.gui2 import choose_files, error_dialog
from calibre.utils.icu import sort_key
from calibre.utils.localization import _
from calibre.utils.resources import get_image_path as I


def texture_dir():
    ans = os.path.join(config_dir, 'textures')
    if not os.path.exists(ans):
        os.makedirs(ans)
    return ans


def texture_path(fname):
    if not fname:
        return
    if fname.startswith(':'):
        return I(f'textures/{fname[1:]}')
    return os.path.join(texture_dir(), fname)


class TextureChooser(QDialog):
    def __init__(self, parent=None, initial=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Choose a texture'))

        self.l = l = QVBoxLayout()
        self.setLayout(l)

        self.tdir = texture_dir()

        self.images = il = QListWidget(self)
        il.itemDoubleClicked.connect(self.accept, type=Qt.ConnectionType.QueuedConnection)
        il.setIconSize(QSize(256, 256))
        il.setViewMode(QListView.ViewMode.IconMode)
        il.setFlow(QListView.Flow.LeftToRight)
        il.setSpacing(20)
        il.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
        il.itemSelectionChanged.connect(self.update_remove_state)
        l.addWidget(il)

        self.ad = ad = QLabel(
            _('The builtin textures come from <a href="{}">subtlepatterns.com</a>.').format('https://www.toptal.com/designers/subtlepatterns/')
        )
        ad.setOpenExternalLinks(True)
        ad.setWordWrap(True)
        l.addWidget(ad)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        b = self.add_button = bb.addButton(_('Add texture'), QDialogButtonBox.ButtonRole.ActionRole)
        assert b is not None
        b.setIcon(QIcon.ic('plus.png'))
        b.clicked.connect(self.add_texture)
        b = self.remove_button = bb.addButton(_('Remove texture'), QDialogButtonBox.ButtonRole.ActionRole)
        assert b is not None
        b.setIcon(QIcon.ic('minus.png'))
        b.clicked.connect(self.remove_texture)
        l.addWidget(bb)

        images = [
            {
                'fname': ':' + os.path.basename(x),
                'path': x,
                'name': ' '.join(s.capitalize() for s in os.path.splitext(os.path.basename(x))[0].split('_')),
            }
            for x in glob.glob(I('textures/*.png'))
        ] + [
            {
                'fname': os.path.basename(x),
                'path': x,
                'name': os.path.splitext(os.path.basename(x))[0],
            }
            for x in glob.glob(os.path.join(self.tdir, '*'))
            if x.rpartition('.')[-1].lower() in {'jpeg', 'png', 'jpg'}
        ]

        images.sort(key=lambda x: sort_key(x['name']))

        for i in images:
            self.create_item(i)
        self.update_remove_state()

        if initial:
            existing = {str(i.data(Qt.ItemDataRole.UserRole) or ''): i for i in (self.images.item(c) for c in range(self.images.count())) if i is not None}
            item = existing.get(initial, None)
            if item is not None:
                item.setSelected(True)
                QTimer.singleShot(100, partial(il.scrollToItem, item))

        self.resize(QSize(950, 650))

    def create_item(self, data):
        x = data
        i = QListWidgetItem(QIcon(x['path']), x['name'], self.images)
        i.setData(Qt.ItemDataRole.UserRole, x['fname'])
        i.setData(Qt.ItemDataRole.UserRole + 1, x['path'])
        return i

    def update_remove_state(self):
        removable = bool(self.selected_fname and not self.selected_fname.startswith(':'))
        assert self.remove_button is not None
        self.remove_button.setEnabled(removable)

    @property
    def texture(self):
        return self.selected_fname

    def add_texture(self):
        path = choose_files(
            self,
            'choose-texture-image',
            _('Choose image'),
            filters=[(_('Images'), ['jpeg', 'jpg', 'png'])],
            all_files=False,
            select_only_single_file=True,
        )
        if not path:
            return
        path = path[0]
        fname = os.path.basename(path)
        name = fname.rpartition('.')[0]
        existing = {str(i.data(Qt.ItemDataRole.UserRole) or ''): i for i in (self.images.item(c) for c in range(self.images.count())) if i is not None}
        dest = os.path.join(self.tdir, fname)
        with open(path, 'rb') as s, open(dest, 'wb') as f:
            shutil.copyfileobj(s, f)
        if fname in existing:
            self.images.takeItem(self.images.row(existing[fname]))
        data = {'fname': fname, 'path': dest, 'name': name}
        i = self.create_item(data)
        i.setSelected(True)
        self.images.scrollToItem(i)

    @property
    def selected_item(self):
        for x in self.images.selectedItems():
            return x

    @property
    def selected_fname(self):
        try:
            return str(self.selected_item.data(Qt.ItemDataRole.UserRole) or '')
        except AttributeError, TypeError:
            pass

    def remove_texture(self):
        if not self.selected_fname:
            return
        if self.selected_fname.startswith(':'):
            return error_dialog(self, _('Cannot remove'), _('Cannot remove builtin textures'), show=True)
        os.remove(str(self.selected_item.data(Qt.ItemDataRole.UserRole + 1) or ''))
        self.images.takeItem(self.images.row(self.selected_item))


if __name__ == '__main__':
    app = QApplication([])  # noqa: F841
    d = TextureChooser()
    d.exec()
    print(d.texture)
