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_dialog import (
    CategoricalSymbologyDialog,
    HtmlColorItemDelegate,
)
from openlog.gui.assay_visualization.extended.bar_symbology import BarSymbology
from openlog.gui.assay_visualization.extended.bar_symbology_color_table_model import (
    BarSymbologyColorTableModel,
)
from openlog.gui.assay_visualization.extended.bar_symbology_pattern_table_model import (
    BarSymbologyPatternTableModel,
)
from openlog.toolbelt.vertical_string_map_table_model import VerticalStringMapTableModel


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

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

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

        Args:
            parent: QWidget
            option: QStyleOptionViewItem
            index: QModelIndex

        Returns: QDoubleSpinBox

        """

        # Define default dir from pattern
        pattern = BarSymbology().replace_alias_in_pattern(
            str(index.data(QtCore.Qt.DisplayRole))
        )
        if Path(pattern).is_file():
            directory = str(Path(pattern).parent)
        else:
            directory = ""

        editor = QFileDialog(parent=parent, directory=directory)
        editor.setModal(True)
        editor.setFileMode(QFileDialog.ExistingFile)

        # Select current pattern if available
        if Path(pattern).is_file():
            editor.selectFile(pattern)

        editor.filesSelected.connect(lambda: editor.setResult(QDialog.Accepted))

        return editor

    def setModelData(
        self,
        editor: QWidget,
        model: QtCore.QAbstractItemModel,
        index: QtCore.QModelIndex,
    ) -> None:
        """
        Define model data from selected file

        Args:
            editor: QFileDialog
            model: QAbstractItemModel
            index:QModelIndex
        """
        selected_files = editor.selectedFiles()
        if editor.result() == QDialog.Accepted and len(selected_files) == 1:
            pattern = BarSymbology().add_alias_into_pattern(selected_files[0])
            model.setData(index, pattern)


class BarSymbologyDialog(CategoricalSymbologyDialog):
    def __init__(self, parent=None) -> None:
        """
        Dialog to display bar symbology

        Args:
            parent: QDialog parent
        """
        QDialog.__init__(self, parent)
        self.symbology_class = BarSymbology
        uic.loadUi(
            os.path.join(os.path.dirname(__file__), "bar_symbology_dialog.ui"), self
        )
        self.setWindowTitle(self.tr("Categorical symbology"))

        self._values_updated = False

        self.assay = None

        self._pattern_map_model = BarSymbologyPatternTableModel()
        self._pattern_map_model.dataChanged.connect(self._model_data_changed)
        self.pattern_table_view.setModel(self._pattern_map_model)
        self.pattern_table_view.setItemDelegateForColumn(
            self._pattern_map_model.VALUE_COL, PatternItemDelegate(self)
        )
        self.pattern_table_view.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )

        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
        )

        self._scale_map_model = VerticalStringMapTableModel()
        self._scale_map_model.setHorizontalHeaderLabels(
            [self.tr("Key"), self.tr("Scale")]
        )
        self._scale_map_model.dataChanged.connect(self._model_data_changed)
        self.scale_table_view.setModel(self._scale_map_model)
        self.scale_table_view.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )

        self._define_col_cbx(self.assay)
        self.pattern_key_col_cbx.currentIndexChanged.connect(
            self._pattern_key_column_changed
        )
        self.color_key_col_cbx.currentIndexChanged.connect(
            self._color_key_column_changed
        )
        self.scale_key_col_cbx.currentIndexChanged.connect(
            self._scale_key_column_changed
        )

        self.save_button.clicked.connect(self.save_symbology)
        self.load_button.clicked.connect(self.load_symbology)
        self.merge_button.clicked.connect(self.merge_symbology)

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

        super()._define_col_cbx(assay)

        if assay:
            available_columns = [
                col.name for col in assay.assay_definition.columns.values()
            ]
        else:
            available_columns = []

        self.pattern_key_col_cbx.clear()
        self.pattern_key_col_cbx.addItems(available_columns)

        self.scale_key_col_cbx.clear()
        self.scale_key_col_cbx.addItems(available_columns)

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

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

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

        super().set_symbology(symbology)
        self.pattern_key_col_cbx.blockSignals(True)
        self.scale_key_col_cbx.blockSignals(True)

        self.pattern_key_col_cbx.setCurrentText(symbology.pattern_col)
        self.scale_key_col_cbx.setCurrentText(symbology.scale_col)

        self.pattern_key_col_cbx.blockSignals(False)
        self.scale_key_col_cbx.blockSignals(False)

        self._pattern_map_model.set_string_map(symbology.pattern_map)
        self._scale_map_model.set_string_map(symbology.scale_map)

        self._values_updated = False

    def get_symbology(self) -> BarSymbology:

        """
        Returns displayed bar symbology

        Returns: (BarSymbology)

        """
        result = BarSymbology()
        result.pattern_col = self.pattern_key_col_cbx.currentText()
        result.pattern_map = self._pattern_map_model.get_string_map()

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

        result.scale_col = self.scale_key_col_cbx.currentText()
        result.scale_map = self._scale_map_model.get_string_map()

        return result
