from typing import Dict, Union

from qgis.PyQt import QtCore
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtGui import QFont, QStandardItemModel


class VerticalStringMapTableModel(QStandardItemModel):
    KEY_COL = 0
    VALUE_COL = 1

    def __init__(self, parent=None, key_label: str = "", val_label: str = "") -> None:
        """
        QStandardItemModel for string map display

        Args:
            parent: QWidget
        """
        super().__init__(parent)
        self.setHorizontalHeaderLabels([key_label, val_label])

    def flags(self, index: QtCore.QModelIndex) -> QtCore.Qt.ItemFlags:
        """
        Override QStandardItemModel flags to remove edition for key

        Args:
            index: QModelIndex

        Returns: index flags

        """
        flags = super().flags(index)
        if index.column() == self.KEY_COL:
            flags = flags & QtCore.Qt.ItemIsEditable
        return flags

    def data(
        self, index: QtCore.QModelIndex, role: int = QtCore.Qt.DisplayRole
    ) -> QVariant:
        """
        Override QStandardItemModel data() for font of expected column

        Args:
            index: QModelIndex
            role: Qt role

        Returns: QVariant

        """
        result = super().data(index, role)
        if role == QtCore.Qt.FontRole and index.column() == self.KEY_COL:
            result = QFont()
            result.setBold(True)

        return result

    def set_string_map(self, map: Dict[str, Union[str, int, float]]) -> None:
        """
        Define current string map

        Args:
            map: string map
        """

        while self.rowCount():
            self.removeRow(0)

        for key, val in map.items():
            row = self._get_or_create_key(key)
            self.setData(self.index(row, self.VALUE_COL), val)

    def get_string_map(self) -> Dict[str, Union[str, int, float]]:
        """
        Get current string map

        Returns: current string map

        """
        result = {}
        for i in range(self.rowCount()):
            key = str(self.data(self.index(i, self.KEY_COL)))
            value = self.data(self.index(i, self.VALUE_COL))
            if key and value:
                result[key] = value
        return result

    def _get_or_create_key(self, key: str) -> int:
        """
        Get or create key

        Args:
            key: key

        Returns: row index

        """
        row = self._get_key_row(key)
        if row == -1:
            self.insertRow(self.rowCount())
            row = self.rowCount() - 1
            self.setData(self.index(row, self.KEY_COL), key)
        return row

    def _get_key_row(self, key: str) -> int:
        """
        Get expected key row index (-1 if expected key not available)

        Args:
            key: (str) expected key

        Returns: expected key row index (-1 if expected key not available)

        """
        row = -1
        for i in range(self.rowCount()):
            val = str(self.data(self.index(i, self.KEY_COL)))
            if val == key:
                row = i
        return row
