import json
import os
from pathlib import Path
from typing import Union

from qgis.PyQt import QtCore, uic
from qgis.PyQt.QtGui import QColor
from qgis.PyQt.QtWidgets import (
    QColorDialog,
    QDialog,
    QFileDialog,
    QHeaderView,
    QMessageBox,
    QStyledItemDelegate,
    QWidget,
)

from openlog.datamodel.assay.generic_assay import GenericAssay
from openlog.gui.assay_visualization.categorical_symbology import CategoricalSymbology
from openlog.gui.assay_visualization.extended.bar_symbology_color_table_model import (
    BarSymbologyColorTableModel,
)


class HtmlColorItemDelegate(QStyledItemDelegate):
    def __init__(self, parent) -> None:
        """
        QStyledItemDelegate for HTML color string definition

        Args:
            parent:
        """
        super().__init__(parent)

    def setEditorData(self, editor: QWidget, index: QtCore.QModelIndex) -> None:
        editor.setCurrentColor(QColor(index.data(QtCore.Qt.DisplayRole)))

    def createEditor(self, parent, option, index) -> QWidget:
        """
        Create a QColorDialog for color definition

        Args:
            parent: QWidget
            option: QStyleOptionViewItem
            index: QModelIndex

        Returns: QDoubleSpinBox

        """
        editor = QColorDialog(parent)
        editor.setOption(QColorDialog.DontUseNativeDialog, True)
        editor.setModal(True)
        return editor

    def setModelData(
        self,
        editor: QWidget,
        model: QtCore.QAbstractItemModel,
        index: QtCore.QModelIndex,
    ) -> None:
        if editor.result() == QDialog.Accepted:
            model.setData(index, editor.selectedColor().name())


STRING_FILTER = "JSON file(*.json)"
STRING_SELECT = "Select file"


class CategoricalSymbologyDialog(QDialog):
    """
    Base class dialog to display categorical symbology.
    Default is one tab for color configuration.

    Args:
        parent: QDialog parent
    """

    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self.symbology_class = CategoricalSymbology
        uic.loadUi(
            os.path.join(os.path.dirname(__file__), "point_symbology_dialog.ui"), self
        )
        self.setWindowTitle("Categorical symbology")

        self._values_updated = False

        self.assay = None
        self._color_map_model = BarSymbologyColorTableModel()
        self._color_map_model.dataChanged.connect(self._model_data_changed)
        self.color_table_view.setModel(self._color_map_model)
        self.color_table_view.setItemDelegateForColumn(
            self._color_map_model.VALUE_COL, HtmlColorItemDelegate(self)
        )
        self.color_table_view.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )

    def set_assay(self, assay: GenericAssay) -> None:
        self.assay = assay
        self._define_col_cbx(assay)

    def _define_col_cbx(self, assay: GenericAssay) -> None:
        if assay:
            available_columns = [
                col.name for col in assay.assay_definition.columns.values()
            ]
        else:
            available_columns = []

        self.color_key_col_cbx.clear()
        self.color_key_col_cbx.addItems(available_columns)

    def _get_symbology_from_assay(self) -> Union[CategoricalSymbology, None]:
        if self.assay:
            symbology = self.get_symbology()
            symbology.init_from_assay(self.assay)
            return symbology
        else:
            return None

    def _color_key_column_changed(self) -> None:
        assay_symbology = self._get_symbology_from_assay()
        if assay_symbology:
            current_symbology = self.get_symbology()
            current_symbology.color_map = assay_symbology.color_map
            self.set_symbology(current_symbology)

    def _model_data_changed(self) -> None:
        self._values_updated = True

    def set_symbology(self, symbology: CategoricalSymbology) -> None:

        self.color_key_col_cbx.blockSignals(True)

        self.color_key_col_cbx.setCurrentText(symbology.color_col)

        self.color_key_col_cbx.blockSignals(False)

        self._color_map_model.set_string_map(symbology.color_map)

        self._values_updated = False

    def get_symbology(self) -> CategoricalSymbology:

        """
        Returns displayed bar symbology

        Returns: (BarSymbology)

        """
        result = CategoricalSymbology()

        result.color_col = self.color_key_col_cbx.currentText()
        result.color_map = self._color_map_model.get_string_map()

        return result

    def merge_symbology(self, d) -> None:
        """
        Merge symbology from a JSON file selected by user with current configuration
        Args:
            - d: dict

        """

        current_symbology = self.get_symbology()
        current_symbology.merge(self.symbology_class.from_json(d), replace=True)
        self.set_symbology(current_symbology)
        self._values_updated = False
