#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
Module that contains classes to create attribute editors
"""

from __future__ import print_function, division, absolute_import

from Qt.QtCore import *
from Qt.QtWidgets import *
from Qt.QtGui import *

from tpDcc.libs.python import strings as string_utils
from tpDcc.libs.qt.widgets import lineedit, directory
from tpDcc.libs.qt.core import qtutils, base, color


class AttributeEditor(base.BaseWidget, object):
    def __init__(self, name='AttributeEditor', label='Attribute Editor', parent=None):
        self._label = label
        self._objects = list()
        super(AttributeEditor, self).__init__(parent=parent)
        self.setObjectName(name)

    def get_objects(self):
        return self._objects

    objects = property(get_objects)

    def ui(self):
        super(AttributeEditor, self).ui()

        self._main_group = QGroupBox()
        self._main_group.setProperty('class', 'attr_main_group')
        self._main_group.setFlat(False)
        self._main_group.setTitle('')

        self._main_group_layout = self.get_attributes_layout()
        self._main_group.setLayout(self._main_group_layout)

        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setStyleSheet('QScrollArea { background-color: rgb(57,57,57);}')
        scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll_area.setWidget(self._main_group)

        self.main_layout.addWidget(scroll_area)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self._on_create_context_menu)

    def get_attributes_layout(self):
        """
        Returns layout used for the attributes
        :return: QLayout
        """

        attributes_layout = QHBoxLayout()
        attributes_layout.setContentsMargins(5, 5, 5, 5)
        attributes_layout.setAlignment(Qt.AlignTop)

        return attributes_layout

    def set_title(self, new_title):
        self._main_group.setTitle(new_title)

    def clear_layout(self, reset_title=True):
        """
        Clears main layout and resetes editor title
        """

        qtutils.clear_layout(self._main_group_layout)
        if reset_title:
            self._main_group.setTitle('')

    def _build_layout(self):
        pass

    def _on_create_context_menu(self):
        pass


class BaseEditor(QWidget, object):
    attr_type = 'None'
    valueChanged = Signal(object)

    def __init__(self, parent=None, **kwargs):
        super(BaseEditor, self).__init__(parent=parent)

        self._default_value = 0.0
        self._current_value = None

        self.main_layout = QHBoxLayout(self)
        self.main_layout.setContentsMargins(1, 1, 1, 1)
        self.main_layout.setSpacing(3)

    def get_default_value(self):
        return self._default_value

    def set_default_value(self, value):
        if value != self._default_value:
            self._default_value = value

    def get_value(self):
        return None

    def set_value(self, value):
        pass

    default_value = property(get_default_value, set_default_value)
    value = property(get_value)

    def OnValueUpdated(self):
        if self.value != self._current_value:
            self._current_value = self.value
            self.valueChanged.emit(self)

    def initialize_editor(self):
        pass


class FloatEditor(BaseEditor, object):

    attr_type = 'float'

    def __init__(self, parent=None, **kwargs):
        super(FloatEditor, self).__init__(parent=parent, **kwargs)

        self._default_value = 0.0

        self.value_line = lineedit.FloatLineEdit(self)
        self.main_layout.addWidget(self.value_line)

    def get_value(self):
        return self.value_line.value

    value = property(get_value)

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]
                if node_values[0] is None:
                    editor_value = self.default_value

                self.value_line.blockSignals(True)
                self.value_line.setText(str(editor_value))
                self.value_line.blockSignals(False)
                self.value_line.valueChanged.connect(self.OnValueUpdated)

    def set_connected(self, conn):
        if conn != self._connection:
            self._connection = conn
            self.value_line.setText(conn)
            self.value_line.setEnabled(False)
            self.value_line.setProperty('class', 'Connected')


class Float2Editor(BaseEditor, object):

    attr_type = 'float'

    def __init__(self, parent=None, **kwargs):
        super(Float2Editor, self).__init__(parent=parent, **kwargs)

        self._default_value = (0.0, 0.0)

        self.value1_line = lineedit.FloatLineEdit(self)
        self.value2_line = lineedit.FloatLineEdit(self)
        self.main_layout.addWidget(self.value1_line)
        self.main_layout.addWidget(self.value2_line)

    def get_value(self):
        return self.value1_line.value, self.value2_line.value

    value = property(get_value)

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]

                self.value1_line.blockSignals(True)
                self.value2_line.blockSignals(True)

                if type(editor_value) in [QPoint, QPointF]:
                    self.value1_line.setText(str(editor_value.x()))
                    self.value2_line.setText(str(editor_value.y()))
                elif type(editor_value) in [list, tuple]:
                    self.value1_line.setText(str(editor_value[0]))
                    self.value2_line.setText(str(editor_value[1]))
                self.value1_line.blockSignals(False)
                self.value2_line.blockSignals(False)
                self.value1_line.valueChanged.connect(self.OnValueUpdated)
                self.value2_line.valueChanged.connect(self.OnValueUpdated)

    def set_connected(self, conn):
        if conn != self._connection:
            self._conection = conn
            self.value1_line.setText(conn)
            self.value1_line.setEnabled(False)
            self.value2_line.setEnabled(False)
            self.value1_line.setProperty('class', 'Connected')
            self.value2_line.setProperty('class', 'Connected')


class Float3Editor(BaseEditor, object):

    attr_type = 'float'

    def __init__(self, parent=None, **kwargs):
        super(Float3Editor, self).__init__(parent=parent, **kwargs)

        self._default_value = (0.0, 0.0, 0.0)

        self.value1_line = lineedit.FloatLineEdit(self)
        self.value2_line = lineedit.FloatLineEdit(self)
        self.value3_line = lineedit.FloatLineEdit(self)
        self.main_layout.addWidget(self.value1_line)
        self.main_layout.addWidget(self.value2_line)
        self.main_layout.addWidget(self.value3_line)

    def get_value(self):
        return self.value1_line.value(), self.value2_line.value(), self.value3_line.value()

    value = property(get_value)

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]

                self.value1_line.blockSignals(True)
                self.value2_line.blockSignals(True)
                self.value3_line.blockSignals(True)
                self.value1_line.setText(str(editor_value[0]))
                self.value2_line.setText(str(editor_value[1]))
                self.value3_line.setText(str(editor_value[2]))
                self.value1_line.blockSignals(False)
                self.value2line.blockSignals(False)
                self.value3line.blockSignals(False)
                self.value1_line.valueChanged.connect(self.OnValueUpdated)
                self.value2_line.valueChanged.connect(self.OnValueUpdated)
                self.value3_line.valueChanged.connect(self.OnValueUpdated)

    def set_connected(self, conn):
        if conn != self._connection:
            self._conection = conn
            self.value1_line.setText(conn)
            self.value1_line.setEnabled(False)
            self.value2_line.setEnabled(False)
            self.value3_line.setEnabled(False)
            self.value1_line.setProperty('class', 'Connected')
            self.value2_line.setProperty('class', 'Connected')
            self.value3_line.setProperty('class', 'Connected')


class StringEditor(BaseEditor, object):

    attr_type = 'str'

    def __init__(self, parent=None, **kwargs):
        super(StringEditor, self).__init__(parent=parent, **kwargs)

        self._default_value = ""
        self._clean_value = kwargs.get('clean', True)

        self.value_line = QLineEdit(self)
        regexp = QRegExp('^([a-zA-Z0-9_]+)')
        self.main_layout.addWidget(self.value_line)

        self.value_line.textEdited.connect(self._validate_text)
        self.value_line.editingFinished.connect(self.OnValueUpdated)
        self.value_line.returnPressed.connect(self.OnValueUpdated)

    def get_value(self):
        return str(self.value_line.text())

    value = property(get_value)

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]

                self.value_line.blockSignals(True)
                self.value_line.setText(str(editor_value))
                self.value_line.blockSignals(False)

    def set_connected(self, conn):
        if conn != self._connection:
            self._connection = conn
            self.value_line.setText(conn)
            self.value_line.setEnabled(False)
            self.value_line.setProperty('class', 'Connected')

    def _validate_text(self, text):
        """
        Validates the given value and update the current text
        :param text: str, text to validate
        """

        current_text = self.value
        if self._clean_value:
            cursor_pos = self.value_line.cursorPosition()
            cleaned = string_utils.clean_string(text=text)
            self.value_line.blockSignals(True)
            self.value_line.setText(cleaned)
            self.value_line.blockSignals(False)
            self.value_line.setCursorPosition(cursor_pos)


class BoolEditor(BaseEditor, object):

    attr_type = 'bool'

    def __init__(self, parent=None, **kwargs):
        super(BoolEditor, self).__init__(parent=parent, **kwargs)

        self._default_value = False

        self.cbx = QCheckBox(self)
        self.main_layout.addWidget(self.cbx)

        self.cbx.toggled.connect(self.OnValueUpdated)

    def get_value(self):
        return self.cbx.isChecked()

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]

                self.cbx.blockSignals(True)
                self.cbx.setChecked(editor_value)
                self.cbx.blockSignals(False)

    def set_connected(self, conn):
        if conn != self._connection:
            self._connection = conn
            self.value_line.setText(conn)
            self.cbx.setEnabled(False)
            self.value_line.setProperty('class', 'Connected')


class ColorPicker(BaseEditor, object):

    attr_type = 'color'

    def __init__(self, parent=None, **kwargs):
        super(ColorPicker, self).__init__(parent=parent, **kwargs)

        self._default_value = (125, 125, 125)
        self.attr = None

        self.normalized = kwargs.get('normalized', False)
        self.min = kwargs.get('min', 0)
        self.max = kwargs.get('max', 99)
        self.color = kwargs.get('color', QColor(1.0, 1.0, 1.0))
        self.mult = kwargs.get('mult', 0.1)

        self.color_swatch = color.ColorSwatch(parent=self, color=self.color, normalized=self.normalized)
        self.color_swatch.setMaximumSize(QSize(75, 20))
        self.color_swatch.setMinimumSize(QSize(75, 20))
        self.color_swatch.set_color(color=self.color)
        self.main_layout.addWidget(self.color_swatch)

        self.slider = QSlider(self)
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.setValue(self.max)
        self.main_layout.addWidget(self.slider)

        self.set_max(self.max)
        self.set_min(self.min)

        self.slider.valueChanged.connect(self.OnSliderChanged)
        self.slider.sliderReleased.connect(self.OnSliderReleased)
        self.color_swatch.clicked.connect(self.OnColorPicked)

    # region Properties
    def get_rgb(self):
        return self.color_swatch.qcolor.getRgb()[0:3]

    def get_rgbF(self):
        return self.color_swatch.qcolor.getRgbF()[0:3]

    def get_hsv(self):
        return self.color_swatch.qcolor.getHsv()[0:3]

    def get_hsvF(self):
        return self.color_swatch.qcolor.getHsvF()[0:3]

    rgb = property(get_rgb)
    rgbF = property(get_rgbF)
    hsv = property(get_hsv)
    gsvF = property(get_hsvF)

    def OnSliderChanged(self):
        slider_value = float(self.slider.value())
        if not self._current_value:
            tp.logger.debug(
                'Caching color: (%d, %d, %d)' % (
                    self.color_swatch.color[0], self.color_swatch.color[1], self.color_swatch.color[2]))
            self._current_value = self.color_swatch.color
        current_color = QColor(*self._current_value)
        darker = 200 - slider_value
        new_color = current_color.darker(darker)
        self.color_swatch.qcolor = new_color
        self.color_swatch._update()

    def OnSliderReleased(self):
        """
        Updates items color when the slider handle is released
        """

        color = self.color_swatch.color
        self.color_swatch._update()
        self.OnValueUpdated()

    def OnColorPicked(self):
        """
        Event to call color picker
        """

        dialog = QColorDialog(self.color_swatch.qcolor, self)
        if dialog.exec_():
            self.color_swatch.setPalette(QPalette(dialog.currentColor()))
            self.color_swatch.qcolor = dialog.currentColor()
            self.color_swatch._update()
            self.OnValueUpdated()

    def get_value(self):
        return self.color_swatch.color

    def initialize_editor(self):
        editor_value = self.default_value

        node_values = self.values
        if node_values:
            if len(node_values) > 1:
                pass
            elif len(node_values) == 1:
                if node_values[0]:
                    editor_value = node_values[0]

                self.color_swatch.set_color(editor_value)

    def set_connected(self, conn):
        if conn != self._connection:
            self._connection = conn
            self.value_line.setText(conn)
            self.color_swatch.setEnabled(False)

    def sizeHint(self):
        return QSize(350, 27)

    def set_attr(self, value):
        self.attr = value
        return self.attr

    def get_attr(self):
        return self.attr

    def set_min(self, value):
        """
        Sets the slider minimum value
        :param value: int, slider minimum value
        """

        self.min = value
        self.slider.setMinimum(value)

    def set_max(self, value):
        """
        Sets the silder maximum value
        :param value: int, slider maximum value
        """

        self.max = value
        self.slider.setMaximum(value)

    def get_qcolor(self):
        return self.color_swatch.qcolor

    def set_color(self, value):
        return self.color_swatch.set_color(color=value)

    def _update(self):
        return self.color_swatch._update()


class FileEditor(BaseEditor, object):

    attr_type = 'file'

    def __init__(self, parent=None, **kwargs):
        super(FileEditor, self).__init__(parent=parent, **kwargs)

        line_widget = directory.SelectFile()
        self.main_layout.addWidget(line_widget)


# ===============================================================================

WIDGET_MAPPER = dict(
    float=[FloatEditor, 0.0],
    float2=[Float2Editor, [0.0, 0.0]],
    float3=[Float3Editor, [0.0, 0.0, 0.0]],
    bool=[BoolEditor, False],
    str=[StringEditor, ""],
    string=[StringEditor, ""],
    file=[FileEditor, ""],
    dir=[FileEditor, ""],
    # int=[IntEditor, 0],
    # int2=[Int2Editor, [0,0]],
    # int3=[Int3Editor, [0,0,0]],
    # int8=[IntEditor, 0],
    color=[ColorPicker, [172, 172, 172, 255]],
    # short2=[Float2Editor, [0.0, 0.0]],
    # node=[StringEditor, ""],
    # multi=[StringEditor, ""],
    # doc=[DocumentEditor, ""]
)


def map_widget(attr_type, name, parent=None):
    """
    Map the widget to the attribute type
    :param attr_type:
    :param name:
    :param parent:
    :return:
    """

    widget_type = attr_type.replace(' ', '').lower()
    if widget_type not in WIDGET_MAPPER:
        tp.logger.warning('Invalid Editor Class: "{0}" ("{1}")'.format(attr_type, name))
    else:
        cls = WIDGET_MAPPER.get(widget_type)[0]
        return cls(parent=parent, name=name)
    return
