"""
***************************************************************************
    ModelerParameterDefinitionDialog.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 math

from qgis.PyQt.QtCore import Qt, QByteArray, QCoreApplication
from qgis.PyQt.QtWidgets import (
    QDialog,
    QVBoxLayout,
    QLabel,
    QLineEdit,
    QComboBox,
    QCheckBox,
    QDialogButtonBox,
    QMessageBox,
    QTabWidget,
    QWidget,
    QTextEdit,
    QHBoxLayout,
)
from qgis.PyQt.QtGui import QColor

from qgis.gui import QgsProcessingLayerOutputDestinationWidget, QgsColorButton
from qgis.core import (
    QgsApplication,
    QgsSettings,
    QgsProcessing,
    QgsProcessingParameterDefinition,
    QgsProcessingDestinationParameter,
    QgsProcessingParameterFeatureSink,
    QgsProcessingParameterFileDestination,
    QgsProcessingParameterFolderDestination,
    QgsProcessingParameterRasterDestination,
    QgsProcessingParameterVectorDestination,
    QgsProcessingModelAlgorithm,
)

from processing.core import parameters
from processing.modeler.exceptions import UndefinedParameterException


class ModelerParameterDefinitionDialog(QDialog):

    @staticmethod
    def use_legacy_dialog(param=None, paramType=None):
        if isinstance(param, QgsProcessingDestinationParameter):
            return True

        # yay, use new API!
        return False

    def __init__(self, alg, paramType=None, param=None):
        self.alg = alg
        self.paramType = paramType
        self.param = param
        QDialog.__init__(self)
        self.setModal(True)
        self.setupUi()
        settings = QgsSettings()
        self.restoreGeometry(
            settings.value(
                "/Processing/modelParametersDefinitionDialogGeometry", QByteArray()
            )
        )

    def closeEvent(self, event):
        settings = QgsSettings()
        settings.setValue(
            "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()
        )
        super().closeEvent(event)

    def switchToCommentTab(self):
        self.tab.setCurrentIndex(1)
        self.commentEdit.setFocus()
        self.commentEdit.selectAll()

    def setupUi(self):
        type_metadata = QgsApplication.processingRegistry().parameterType(
            self.param.type() if self.param else self.paramType
        )
        self.setWindowTitle(
            self.tr("{} Parameter Definition").format(type_metadata.name())
        )

        self.mainLayout = QVBoxLayout()
        self.tab = QTabWidget()
        self.mainLayout.addWidget(self.tab)

        self.setMinimumWidth(300)

        self.verticalLayout = QVBoxLayout()

        self.label = QLabel(self.tr("Parameter name"))
        self.verticalLayout.addWidget(self.label)
        self.nameTextBox = QLineEdit()
        self.verticalLayout.addWidget(self.nameTextBox)

        if isinstance(self.param, QgsProcessingParameterDefinition):
            self.nameTextBox.setText(self.param.description())

        if isinstance(self.param, QgsProcessingDestinationParameter):
            self.verticalLayout.addWidget(QLabel(self.tr("Default value")))
            self.defaultWidget = QgsProcessingLayerOutputDestinationWidget(
                self.param, defaultSelection=True
            )
            self.verticalLayout.addWidget(self.defaultWidget)

        self.verticalLayout.addSpacing(20)
        self.requiredCheck = QCheckBox()
        self.requiredCheck.setText(self.tr("Mandatory"))
        self.requiredCheck.setChecked(True)
        if self.param is not None:
            self.requiredCheck.setChecked(
                not self.param.flags()
                & QgsProcessingParameterDefinition.Flag.FlagOptional
            )
        self.verticalLayout.addWidget(self.requiredCheck)

        self.advancedCheck = QCheckBox()
        self.advancedCheck.setText(self.tr("Advanced"))
        self.advancedCheck.setChecked(False)
        if self.param is not None:
            self.advancedCheck.setChecked(
                self.param.flags() & QgsProcessingParameterDefinition.Flag.FlagAdvanced
            )
        self.verticalLayout.addWidget(self.advancedCheck)

        # If child algorithm output is mandatory, disable checkbox
        if isinstance(self.param, QgsProcessingDestinationParameter):
            child = self.alg.childAlgorithms()[self.param.metadata()["_modelChildId"]]
            model_output = child.modelOutput(
                self.param.metadata()["_modelChildOutputName"]
            )
            param_def = child.algorithm().parameterDefinition(
                model_output.childOutputName()
            )
            if not (
                param_def.flags() & QgsProcessingParameterDefinition.Flag.FlagOptional
            ):
                self.requiredCheck.setEnabled(False)
                self.requiredCheck.setChecked(True)

            self.advancedCheck.setEnabled(False)
            self.advancedCheck.setChecked(False)

        self.verticalLayout.addStretch()

        w = QWidget()
        w.setLayout(self.verticalLayout)
        self.tab.addTab(w, self.tr("Properties"))

        self.commentLayout = QVBoxLayout()
        self.commentEdit = QTextEdit()
        self.commentEdit.setAcceptRichText(False)
        self.commentLayout.addWidget(self.commentEdit, 1)

        hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        hl.addWidget(QLabel(self.tr("Color")))
        self.comment_color_button = QgsColorButton()
        self.comment_color_button.setAllowOpacity(True)
        self.comment_color_button.setWindowTitle(self.tr("Comment Color"))
        self.comment_color_button.setShowNull(True, self.tr("Default"))
        hl.addWidget(self.comment_color_button)
        self.commentLayout.addLayout(hl)

        w2 = QWidget()
        w2.setLayout(self.commentLayout)
        self.tab.addTab(w2, self.tr("Comments"))

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.StandardButton.Cancel | QDialogButtonBox.StandardButton.Ok
        )
        self.buttonBox.setObjectName("buttonBox")
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.mainLayout.addWidget(self.buttonBox)

        self.setLayout(self.mainLayout)

    def setComments(self, text):
        self.commentEdit.setPlainText(text)

    def comments(self):
        return self.commentEdit.toPlainText()

    def setCommentColor(self, color):
        if color.isValid():
            self.comment_color_button.setColor(color)
        else:
            self.comment_color_button.setToNull()

    def commentColor(self):
        return (
            self.comment_color_button.color()
            if not self.comment_color_button.isNull()
            else QColor()
        )

    def accept(self):
        description = self.nameTextBox.text()
        if description.strip() == "":
            QMessageBox.warning(
                self,
                self.tr("Unable to define parameter"),
                self.tr("Invalid parameter name"),
            )
            return

        safeName = QgsProcessingModelAlgorithm.safeName(description)
        name = safeName.lower()

        # Destination parameter
        if isinstance(self.param, QgsProcessingParameterFeatureSink):
            self.param = QgsProcessingParameterFeatureSink(
                name=name,
                description=description,
                type=self.param.dataType(),
                defaultValue=self.defaultWidget.value(),
            )
        elif isinstance(self.param, QgsProcessingParameterFileDestination):
            self.param = QgsProcessingParameterFileDestination(
                name=name,
                description=description,
                fileFilter=self.param.fileFilter(),
                defaultValue=self.defaultWidget.value(),
            )
        elif isinstance(self.param, QgsProcessingParameterFolderDestination):
            self.param = QgsProcessingParameterFolderDestination(
                name=name,
                description=description,
                defaultValue=self.defaultWidget.value(),
            )
        elif isinstance(self.param, QgsProcessingParameterRasterDestination):
            self.param = QgsProcessingParameterRasterDestination(
                name=name,
                description=description,
                defaultValue=self.defaultWidget.value(),
            )
        elif isinstance(self.param, QgsProcessingParameterVectorDestination):
            self.param = QgsProcessingParameterVectorDestination(
                name=name,
                description=description,
                type=self.param.dataType(),
                defaultValue=self.defaultWidget.value(),
            )

        else:
            if self.paramType:
                typeId = self.paramType
            else:
                typeId = self.param.type()

            paramTypeDef = (
                QgsApplication.instance().processingRegistry().parameterType(typeId)
            )
            if not paramTypeDef:
                msg = self.tr(
                    "The parameter `{}` is not registered, are you missing a required plugin?"
                ).format(typeId)
                raise UndefinedParameterException(msg)
            self.param = paramTypeDef.create(name)
            self.param.setDescription(description)
            self.param.setMetadata(paramTypeDef.metadata())

        if not self.requiredCheck.isChecked():
            self.param.setFlags(
                self.param.flags() | QgsProcessingParameterDefinition.Flag.FlagOptional
            )
        else:
            self.param.setFlags(
                self.param.flags() & ~QgsProcessingParameterDefinition.Flag.FlagOptional
            )

        if self.advancedCheck.isChecked():
            self.param.setFlags(
                self.param.flags() | QgsProcessingParameterDefinition.Flag.FlagAdvanced
            )
        else:
            self.param.setFlags(
                self.param.flags() & ~QgsProcessingParameterDefinition.Flag.FlagAdvanced
            )

        settings = QgsSettings()
        settings.setValue(
            "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()
        )

        QDialog.accept(self)

    def reject(self):
        self.param = None

        settings = QgsSettings()
        settings.setValue(
            "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()
        )

        QDialog.reject(self)
