"""
Tinymovr GUI Helpers
Copyright Ioannis Chatzikonstantinou 2020-2023

Various GUI helper functions

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 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
"""

import time
import os
import enum
import pint
from PySide6 import QtGui
from PySide6.QtGui import QGuiApplication, QPalette
from PySide6.QtWidgets import QMessageBox, QFileDialog
from avlos.definitions import RemoteAttribute, RemoteEnum, RemoteBitmask
import tinymovr


app_stylesheet = """

/* --------------------------------------- QPushButton -----------------------------------*/

    QPushButton {
        background-color: #ededef;
        border-radius: 4px; 
        margin: 3px 10px 3px 0;
    }
    QPushButton:pressed {
        background-color: #cdcdcf;
        border-style: inset;
    }
    QPushButton:hover:!pressed
    {
    background-color: #eaeaec;
    } 

/* --------------------------------------- QComboBox -----------------------------------*/

    QComboBox {
        margin: 0 10px 0 5px;
    }

    QComboBox {
        margin: 0 10px 0 0;
    }

    QComboBox::drop-down {
        border: none;
        background-color: #ededef;
        border-radius: 4px; 
    }

/* --------------------------------------- QScrollBar -----------------------------------*/

    QScrollBar:horizontal
    {
        height: 15px;
        margin: 3px 15px 3px 15px;
        border: 1px transparent white;
        border-radius: 4px;
        background-color: white;
    }

    QScrollBar::handle:horizontal
    {
        background-color: #dfdfe1; 
        min-width: 5px;
        border-radius: 4px;
    }

    QScrollBar::add-line:horizontal
    {
        margin: 0px 3px 0px 3px;
        border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
        width: 10px;
        height: 10px;
        subcontrol-position: right;
        subcontrol-origin: margin;
    }

    QScrollBar::sub-line:horizontal
    {
        margin: 0px 3px 0px 3px;
        border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: left;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on
    {
        border-image: url(:/qss_icons/rc/right_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: right;
        subcontrol-origin: margin;
    }


    QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on
    {
        border-image: url(:/qss_icons/rc/left_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: left;
        subcontrol-origin: margin;
    }

    QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
    {
        background: none;
    }


    QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
    {
        background: none;
    }

    QScrollBar:vertical
    {
        background-color: white;
        width: 15px;
        margin: 15px 3px 15px 3px;
        border: 1px transparent white;
        border-radius: 4px;
    }

    QScrollBar::handle:vertical
    {
        background-color: #dfdfe1;
        min-height: 5px;
        border-radius: 4px;
    }

    QScrollBar::sub-line:vertical
    {
        margin: 3px 0px 3px 0px;
        border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: top;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:vertical
    {
        margin: 3px 0px 3px 0px;
        border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: bottom;
        subcontrol-origin: margin;
    }

    QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on
    {
        border-image: url(:/qss_icons/rc/up_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: top;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on
    {
        border-image: url(:/qss_icons/rc/down_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: bottom;
        subcontrol-origin: margin;
    }

    QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
    {
        background: none;
    }

    QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
    {
        background: none;
    }

    QAbstractScrollArea::corner {
        background: #dfdfe1;
        border: none;
    }
"""


app_stylesheet_dark = """

/* --------------------------------------- QPushButton -----------------------------------*/

    QPushButton {
        background-color: #363638;
        border-radius: 4px; 
        margin: 3px 10px 3px 0;
    }
    QPushButton:pressed {
        background-color: #767678;
        border-style: inset;
    }
    QPushButton:hover:!pressed
    {
    background-color: #464648;
    }

/* --------------------------------------- QComboBox -----------------------------------*/

    QComboBox {
        margin: 0 10px 0 5px;
    }
    
    QComboBox::drop-down {
        border: none;
        background-color: #363638;
        border-radius: 4px; 
    }

    QComboBox::down-arrow
    {   
        border: 0px;
        background-repeat: no-repeat;
        background-position: center center;
        border-image: url(:/qss_icons/rc/down_arrow.png);
        height:20px;
        width:20px;
    }

/* ----------------------------------- Headers (dark only) -------------------------------*/

    QHeaderView::section {
        border-right-color: #262628;
        border-right-width: 1px;
        border-style: solid;
        margin: 0 4px;
    }

/* --------------------------------------- QScrollBar -----------------------------------*/

    QScrollBar:horizontal
    {
        height: 15px;
        margin: 3px 15px 3px 15px;
        border: 1px transparent white;
        border-radius: 4px;
        background-color: #363638;
    }

    QScrollBar::handle:horizontal
    {
        background-color: #605F5F; 
        min-width: 5px;
        border-radius: 4px;
    }

    QScrollBar::add-line:horizontal
    {
        margin: 0px 3px 0px 3px;
        border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
        width: 10px;
        height: 10px;
        subcontrol-position: right;
        subcontrol-origin: margin;
    }

    QScrollBar::sub-line:horizontal
    {
        margin: 0px 3px 0px 3px;
        border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: left;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on
    {
        border-image: url(:/qss_icons/rc/right_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: right;
        subcontrol-origin: margin;
    }


    QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on
    {
        border-image: url(:/qss_icons/rc/left_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: left;
        subcontrol-origin: margin;
    }

    QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
    {
        background: none;
    }


    QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
    {
        background: none;
    }

    QScrollBar:vertical
    {
        background-color: #363638;
        width: 15px;
        margin: 15px 3px 15px 3px;
        border: 1px transparent white;
        border-radius: 4px;
    }

    QScrollBar::handle:vertical
    {
        background-color: #605F5F;
        min-height: 5px;
        border-radius: 4px;
    }

    QScrollBar::sub-line:vertical
    {
        margin: 3px 0px 3px 0px;
        border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: top;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:vertical
    {
        margin: 3px 0px 3px 0px;
        border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
        height: 10px;
        width: 10px;
        subcontrol-position: bottom;
        subcontrol-origin: margin;
    }

    QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on
    {
        border-image: url(:/qss_icons/rc/up_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: top;
        subcontrol-origin: margin;
    }

    QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on
    {
        border-image: url(:/qss_icons/rc/down_arrow.png);
        height: 10px;
        width: 10px;
        subcontrol-position: bottom;
        subcontrol-origin: margin;
    }

    QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
    {
        background: none;
    }

    QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
    {
        background: none;
    }

    QAbstractScrollArea::corner {
        background: #363638;
        border: none;
    }
"""


def format_value(value, include_unit=True):
    """
    Format a numeric value according to its
    type and return the formatted string
    """
    if isinstance(value, enum.IntFlag):
        return bitmask_string_representation(value) if value > 0 else "(no flags)"
    if isinstance(value, enum.IntEnum):
        return value.name
    if not include_unit and isinstance(value, pint.Quantity):
        return str(value.magnitude)
    if isinstance(value, float):
        return "{0:.6g}".format(value)
    return str(value)


def bitmask_string_representation(bitmask_value):
    labels_in_bitmask = [
        label.name for label in type(bitmask_value) if label & bitmask_value
    ]
    return ", ".join(labels_in_bitmask)


def load_pixmap(filename, dark_mode_suffix="_dark", high_dpi_suffix="@2x"):
    """
    Load an image from a file and return it as a QPixmap.
    Load appropriate variants based on pixel ratio and
    dark or light mode.
    """
    # Get the pixel ratio
    pixel_ratio = QtGui.QGuiApplication.primaryScreen().devicePixelRatio()

    # Prepare the filename based on the conditions
    parts = filename.split(".")
    if is_dark_mode():
        parts[0] += dark_mode_suffix
    if pixel_ratio > 1:
        parts[0] += high_dpi_suffix
    adjusted_filename = ".".join(parts)

    file_path = os.path.join(
        os.path.dirname(tinymovr.__file__), "resources", "icons", adjusted_filename
    )

    # Load the pixmap and set the device pixel ratio
    pixmap = QtGui.QPixmap(file_path)
    pixmap.setDevicePixelRatio(pixel_ratio)

    return pixmap


def is_dark_mode():
    return QGuiApplication.palette().color(QPalette.Window).lightness() < 128


def load_icon(fname_icon):
    """
    Load an image from a file and return it as a QIcon
    """
    pixmap = load_pixmap(fname_icon)
    icon = QtGui.QIcon()
    icon.addPixmap(pixmap, QtGui.QIcon.Normal)
    icon.addPixmap(pixmap, QtGui.QIcon.Disabled)
    return icon


def magnitude_of(val):
    """
    Extract the magnitude of a pint Quantity
    and return it, or return the value itself
    otherwise
    """
    if isinstance(val, pint.Quantity):
        return val.magnitude
    return val


class TimedGetter:
    """
    An interface class that maintains timing
    information for the getter function
    """

    def __init__(self):
        self.dt = 0

    def get_value(self, getter):
        get_start_time = time.time()
        val = getter()
        get_dt = time.time() - get_start_time
        self.dt = self.dt * 0.99 + get_dt * 0.01
        return val


def display_warning(title, text):
    """
    Display a pop up message with a warning
    """
    msg_box = QMessageBox()
    msg_box.setIcon(QMessageBox.Warning)
    msg_box.setText(text)
    msg_box.setWindowTitle(title)
    msg_box.exec_()


def display_file_open_dialog():
    """
    Display the file open dialog
    """
    documents_dir = os.path.expanduser("~/Documents")
    file_name, _ = QFileDialog.getOpenFileName(
        None, "Select JSON File", "", "JSON Files (*.json)"
    )
    return file_name


def display_file_save_dialog():
    """
    Display the file save dialog
    """
    documents_dir = os.path.expanduser("~/Documents")
    file_name, _ = QFileDialog.getSaveFileName(
        None, "Save JSON File", documents_dir, "JSON Files (*.json)"
    )
    return file_name


def check_selected_items(selected_items):
    if len(selected_items) == 0:
        display_warning(
            "Invalid Selection",
            "No Tinymovr nodes selected.\nSelect a single node",
        )
        return False
    elif len(selected_items) > 1:
        display_warning(
            "Invalid Selection",
            "Multiple Tinymovr nodes selected.\nSelect a single node",
        )
        return False
    return True


def get_dynamic_attrs(attr_dict):
    """
    Get the attributes that are marked as dynamic in the spec.
    """
    dynamic_attrs = []

    for attr in attr_dict.values():
        if isinstance(
            attr, (RemoteAttribute, RemoteEnum, RemoteBitmask)
        ) and attr.meta.get("dynamic"):
            dynamic_attrs.append(attr)
        elif hasattr(attr, "remote_attributes"):
            dynamic_attrs.extend(get_dynamic_attrs(attr.remote_attributes))

    return dynamic_attrs
