"""
***************************************************************************
    MultipleInputDialog.py
    ---------------------
    Date                 : August 2012
    Copyright            : (C) 2012 by Victor Olaya
    Email                : volayaf at gmail dot com
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""

__author__ = "Victor Olaya"
__date__ = "August 2012"
__copyright__ = "(C) 2012, Victor Olaya"

import os
import warnings
from pathlib import Path

from qgis.core import (
    QgsSettings,
    QgsProcessing,
    QgsVectorFileWriter,
    QgsProviderRegistry,
    QgsProcessingModelChildParameterSource,
)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, QByteArray, QCoreApplication
from qgis.PyQt.QtWidgets import (
    QDialog,
    QAbstractItemView,
    QPushButton,
    QDialogButtonBox,
    QFileDialog,
)
from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem

pluginPath = os.path.split(os.path.dirname(__file__))[0]
with warnings.catch_warnings():
    warnings.filterwarnings("ignore", category=DeprecationWarning)
    WIDGET, BASE = uic.loadUiType(
        os.path.join(pluginPath, "ui", "DlgMultipleSelection.ui")
    )


class MultipleInputDialog(BASE, WIDGET):

    def __init__(self, options, selectedoptions=None, datatype=None):
        super().__init__(None)
        self.setupUi(self)
        self.datatype = datatype
        self.model = None

        self.options = []
        for i, option in enumerate(options):
            if option is None or isinstance(option, str):
                self.options.append((i, option))
            else:
                self.options.append((option[0], option[1]))

        self.selectedoptions = selectedoptions or []

        # Additional buttons
        self.btnSelectAll = QPushButton(self.tr("Select All"))
        self.buttonBox.addButton(
            self.btnSelectAll, QDialogButtonBox.ButtonRole.ActionRole
        )
        self.btnClearSelection = QPushButton(self.tr("Clear Selection"))
        self.buttonBox.addButton(
            self.btnClearSelection, QDialogButtonBox.ButtonRole.ActionRole
        )
        self.btnToggleSelection = QPushButton(self.tr("Toggle Selection"))
        self.buttonBox.addButton(
            self.btnToggleSelection, QDialogButtonBox.ButtonRole.ActionRole
        )
        if self.datatype is not None:
            btnAddFile = QPushButton(
                QCoreApplication.translate("MultipleInputDialog", "Add File(s)…")
            )
            btnAddFile.clicked.connect(self.addFiles)
            self.buttonBox.addButton(btnAddFile, QDialogButtonBox.ButtonRole.ActionRole)

            btnAddDir = QPushButton(
                QCoreApplication.translate("MultipleInputDialog", "Add Directory…")
            )
            btnAddDir.clicked.connect(self.addDirectory)
            self.buttonBox.addButton(btnAddDir, QDialogButtonBox.ButtonRole.ActionRole)

        self.btnSelectAll.clicked.connect(lambda: self.selectAll(True))
        self.btnClearSelection.clicked.connect(lambda: self.selectAll(False))
        self.btnToggleSelection.clicked.connect(self.toggleSelection)

        self.settings = QgsSettings()
        self.restoreGeometry(
            self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray())
        )

        self.lstLayers.setSelectionMode(
            QAbstractItemView.SelectionMode.ExtendedSelection
        )
        self.lstLayers.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)

        self.populateList()
        self.finished.connect(self.saveWindowGeometry)

    def saveWindowGeometry(self):
        self.settings.setValue(
            "/Processing/multipleInputDialogGeometry", self.saveGeometry()
        )

    def populateList(self):
        self.model = QStandardItemModel()
        for value, text in self.options:
            item = QStandardItem(text)
            item.setData(value, Qt.ItemDataRole.UserRole)
            item.setCheckState(
                Qt.CheckState.Checked
                if value in self.selectedoptions
                else Qt.CheckState.Unchecked
            )
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        # add extra options (e.g. manually added layers)
        for t in [o for o in self.selectedoptions if not isinstance(o, int)]:
            if isinstance(t, QgsProcessingModelChildParameterSource):
                item = QStandardItem(t.staticValue())
            else:
                item = QStandardItem(t)
            item.setData(item.text(), Qt.ItemDataRole.UserRole)
            item.setCheckState(Qt.CheckState.Checked)
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        self.lstLayers.setModel(self.model)

    def accept(self):
        self.selectedoptions = []
        model = self.lstLayers.model()
        for i in range(model.rowCount()):
            item = model.item(i)
            if item.checkState() == Qt.CheckState.Checked:
                self.selectedoptions.append(item.data(Qt.ItemDataRole.UserRole))
        QDialog.accept(self)

    def reject(self):
        self.selectedoptions = None
        QDialog.reject(self)

    def getItemsToModify(self):
        items = []
        if len(self.lstLayers.selectedIndexes()) > 1:
            for i in self.lstLayers.selectedIndexes():
                items.append(self.model.itemFromIndex(i))
        else:
            for i in range(self.model.rowCount()):
                items.append(self.model.item(i))
        return items

    def selectAll(self, value):
        for item in self.getItemsToModify():
            item.setCheckState(
                Qt.CheckState.Checked if value else Qt.CheckState.Unchecked
            )

    def toggleSelection(self):
        for item in self.getItemsToModify():
            checked = item.checkState() == Qt.CheckState.Checked
            item.setCheckState(
                Qt.CheckState.Unchecked if checked else Qt.CheckState.Checked
            )

    def getFileFilter(self, datatype):
        """
        Returns a suitable file filter pattern for the specified parameter definition
        :param param:
        :return:
        """
        if datatype == QgsProcessing.SourceType.TypeRaster:
            return QgsProviderRegistry.instance().fileRasterFilters()
        elif datatype == QgsProcessing.SourceType.TypeFile:
            return self.tr("All files (*.*)")
        else:
            exts = QgsVectorFileWriter.supportedFormatExtensions()
            for i in range(len(exts)):
                exts[i] = self.tr("{0} files (*.{1})").format(
                    exts[i].upper(), exts[i].lower()
                )
            return self.tr("All files (*.*)") + ";;" + ";;".join(exts)

    def addFiles(self):
        filter = self.getFileFilter(self.datatype)

        settings = QgsSettings()
        path = str(settings.value("/Processing/LastInputPath"))

        ret, selected_filter = QFileDialog.getOpenFileNames(
            self, self.tr("Select File(s)"), path, filter
        )
        if ret:
            files = list(ret)
            settings.setValue(
                "/Processing/LastInputPath", os.path.dirname(str(files[0]))
            )
            for filename in files:
                item = QStandardItem(filename)
                item.setData(filename, Qt.ItemDataRole.UserRole)
                item.setCheckState(Qt.CheckState.Checked)
                item.setCheckable(True)
                item.setDropEnabled(False)
                self.model.appendRow(item)

    def addDirectory(self):
        settings = QgsSettings()
        path = str(settings.value("/Processing/LastInputPath"))

        ret = QFileDialog.getExistingDirectory(self, self.tr("Select File(s)"), path)
        if ret:
            exts = []

            if self.datatype == QgsProcessing.SourceType.TypeVector:
                exts = QgsVectorFileWriter.supportedFormatExtensions()
            elif self.datatype == QgsProcessing.SourceType.TypeRaster:
                for t in (
                    QgsProviderRegistry.instance().fileRasterFilters().split(";;")[1:]
                ):
                    for e in t[t.index("(") + 1 : -1].split(" "):
                        if e != "*.*" and e.startswith("*."):
                            exts.append(e[2:])

            files = []
            for pp in Path(ret).rglob("*"):
                if not pp.is_file():
                    continue

                if exts and pp.suffix[1:] not in exts:
                    continue

                p = pp.as_posix()

                files.append(p)

            settings.setValue("/Processing/LastInputPath", ret)

            for filename in files:
                item = QStandardItem(filename)
                item.setData(filename, Qt.ItemDataRole.UserRole)
                item.setCheckState(Qt.CheckState.Checked)
                item.setCheckable(True)
                item.setDropEnabled(False)
                self.model.appendRow(item)
