# Copyright 2017 The Imaging Source Europe GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from PyQt5.QtWidgets import (QHBoxLayout, QSlider, QPushButton,
                             QCheckBox, QComboBox, QWidget,
                             QSpinBox, QDoubleSpinBox, QLabel)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, pyqtSignal
from . import TcamCaptureData, TcamSlider, TcamSpinBox
import logging

log = logging.getLogger(__name__)


class Prop(object):
    """Property container"""
    def __init__(self, name, value, minval, maxval, defval, step, valuetype,
                 flags, category, group):
        self.name = name
        self.value = value
        self.minval = minval
        self.maxval = maxval
        self.defval = defval
        self.step = step
        self.valuetype = valuetype
        self.flags = flags
        self.category = category
        self.group = group


class PropertyWidget(QWidget):

    value_changed = pyqtSignal(object)

    """display widget for tcam property"""
    def __init__(self, data: TcamCaptureData, prop: Prop,
                 app_property: bool=False):
        super().__init__()
        self.app_property = app_property
        self.tcam = data.tcam
        self.signals = data.signals
        self.prop = prop
        self.is_log = False
        self.setup_ui()

    def __repr__(self):
        return repr((self.prop.name, self.prop.valuetype,
                     self.prop.category, self.prop.group))

    def __setup_ui_boolean(self):
        """
        Helper function that contains all setup code for bool UIs
        """
        self.toggle = QCheckBox(self)
        self.toggle.setCheckable(True)
        if self.prop.value:
            self.toggle.toggle()
        self.toggle.toggled.connect(self.button_clicked)
        self.layout.addWidget(self.toggle)

    def __setup_ui_integer(self):
        """

        """

        self.value_box = TcamSpinBox.TcamSpinBox(self)
        try:
            self.value_box.setRange(self.prop.minval, self.prop.maxval)
        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))

        self.value_box.setSingleStep(self.prop.step)
        self.value_box.blockSignals(True)
        self.value_box.setValue(self.prop.value)
        self.value_box.blockSignals(False)
        self.value_box.valueChanged[int].connect(self.set_property_box)
        self.value_box.setKeyboardTracking(False)

        if self.is_log:
            log.debug("Adding log slider for {}".format(self.prop.name))
            self.sld = TcamSlider.TcamLogSlider(self)
            self.sld.valueLogChanged.connect(self.set_property)
        else:
            self.sld = TcamSlider.TcamSlider(self)
            self.sld.valueChanged[int].connect(self.set_property)

        self.sld.setFocusPolicy(Qt.NoFocus)
        try:
            self.sld.blockSignals(True)
            self.sld.setRange(self.prop.minval, self.prop.maxval)
            self.sld.setValue(self.prop.value)
            self.sld.blockSignals(False)

        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))
        self.sld.setSingleStep(self.prop.step)
        self.sld.doubleClicked.connect(self.reset)

        self.layout.addWidget(self.sld)
        self.layout.addWidget(self.value_box)

    def __setup_ui_double(self):
        """
        Helper function that contains all setup code for doube UIs
        """
        self.value_box = TcamSpinBox.TcamDoubleSpinBox(self)
        try:
            self.value_box.setRange(self.prop.minval, self.prop.maxval)
        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))

        self.value_box.setSingleStep(self.prop.step)
        self.value_box.blockSignals(True)
        self.value_box.setValue(self.prop.value)
        self.value_box.blockSignals(False)
        self.value_box.valueChanged[float].connect(self.set_property_box)
        self.value_box.setKeyboardTracking(False)

        if self.is_log:
            self.sld = TcamSlider.TcamLogSlider(self)
            self.sld.valueLogChanged.connect(self.set_property)
        else:
            self.sld = TcamSlider.TcamSlider(self)
            self.sld.valueChanged[int].connect(self.set_property)

        self.sld.setFocusPolicy(Qt.NoFocus)
        try:
            self.sld.setRange(int(self.prop.minval * 100), int(self.prop.maxval * 100))
            self.sld.blockSignals(True)
            self.sld.setValue(int(self.prop.value * 100))
            self.sld.blockSignals(False)

        except OverflowError:
            log.warning("Property {} reported a range "
                        "that could not be handled min: {} max: {}".format(self.prop.name,
                                                                           self.prop.minval,
                                                                           self.prop.maxval))
        self.sld.setSingleStep(int(self.prop.step * 100))
        self.sld.doubleClicked.connect(self.reset)

        self.sld.setGeometry(30, 40, 100, 30)
        self.layout.addWidget(self.sld)
        self.layout.addWidget(self.value_box)

    def setup_ui(self):
        self.layout = QHBoxLayout()

        def should_be_logarithmic(prop):
            """
            Returns True when the range is > 5000
            """
            if prop.valuetype == "integer":
                if (prop.maxval - prop.minval) >= 5000:
                    return True

            elif prop.valuetype == "double":
                if (prop.maxval - prop.minval) >= 5000:
                    return True

            return False

        self.setLayout(self.layout)

        self.is_log = should_be_logarithmic(self.prop)

        if self.prop.valuetype == "integer":

            self.__setup_ui_integer()

        elif self.prop.valuetype == "double":

            self.__setup_ui_double()

        elif self.prop.valuetype == "button":
            self.checkbox = QPushButton(self)
            self.checkbox.clicked.connect(self.set_property)
            self.layout.addWidget(self.checkbox)

        elif self.prop.valuetype == "boolean":
            self.__setup_ui_boolean()

        elif self.prop.valuetype == "string":
            pass

        elif self.prop.valuetype == "enum":
            self.combo = QComboBox(self)
            entry_list = self.tcam.get_tcam_menu_entries(self.prop.name)

            for e in entry_list:
                self.combo.addItem(e)
                self.combo.setCurrentText(self.prop.value)
                self.combo.currentIndexChanged['QString'].connect(self.set_property)
                self.layout.addWidget(self.combo)

    def button_clicked(self):
        log.debug("button clicked")
        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          self.toggle.isChecked(), self.prop.valuetype)
        self.value_changed.emit(self)

    def set_property(self, value, emit_value_changed=True):
        self.prop.value = value
        if self.prop.valuetype == "integer":
            self.update_box_value(self.value_box, value)

        if self.prop.valuetype == "double":
            self.update_box_value(self.value_box, value / 100)

            self.signals.change_property.emit(self.tcam, self.prop.name,
                                              float(value / 100), self.prop.valuetype)
            return

        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          value, self.prop.valuetype)
        if emit_value_changed:
            self.value_changed.emit(self)

    def set_property_box(self, value):
        if self.prop.valuetype == "integer":
            self.update_slider_value(self.sld, value)

        if self.prop.valuetype == "double":
            self.update_slider_value(self.sld, value)

            self.signals.change_property.emit(self.tcam, self.prop.name,
                                              float(value), self.prop.valuetype)
            return

        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          value, self.prop.valuetype)
        self.value_changed.emit(self)

    def update_box_value(self, box, value):
        box.blockSignals(True)
        box.setValue(value)
        box.blockSignals(False)

    def update_box_range(self, box, minval, maxval):
        """"""
        box.blockSignals(True)
        box.setRange(self.prop.minval,
                     self.prop.maxval)
        box.blockSignals(False)

    def update_slider_value(self, slider, value):
        slider.blockSignals(True)
        try:
            if self.prop.valuetype == "double":
                slider.setValue(int(value * 100))
            else:
                slider.setValue(value)

        except OverflowError:
            log.info("The slider for '{}' had a value outside of the integer "
                     "range. That should no happen.".format(self.prop.name))
        finally:
            slider.blockSignals(False)

    def update_slider_range(self, slider, minval, maxval):
        """"""
        self.sld.blockSignals(True)
        try:
            if self.prop.valuetype == "double":
                self.sld.setRange(self.prop.minval * 100,
                                  self.prop.maxval * 100)
            else:
                self.sld.setRange(self.prop.minval,
                                  self.prop.maxval)
        except OverflowError:
            log.error("The slider for '{}' had a value outside of the integer "
                      "range. That should no happen. "
                      "Min: '{}' Max: {}".format(self.prop.name,
                                                 self.prop.minval,
                                                 self.prop.maxval))
        finally:
            self.sld.blockSignals(False)

    def update(self, prop: Prop):

        emit_value_changed = False

        if self.prop.value != prop.value:
            emit_value_changed = True

        self.prop = prop
        if self.prop.valuetype == "integer":
            if type(self.value_box) is TcamSpinBox.TcamSpinBox:
                if self.value_box.active():
                    # box.setValue(value)
                    return
            self.update_slider_value(self.sld, self.prop.value)

            self.update_slider_range(self.sld,
                                     self.prop.minval,
                                     self.prop.maxval)
            self.update_box_range(self.value_box,
                                  self.prop.minval,
                                  self.prop.maxval)
            self.update_box_value(self.value_box, self.prop.value)
        elif self.prop.valuetype == "double":
            self.update_slider_range(self.sld,
                                     self.prop.minval,
                                     self.prop.maxval)
            self.update_slider_value(self.sld, self.prop.value)
            self.update_box_range(self.value_box,
                                  self.prop.minval,
                                  self.prop.maxval)
            self.update_box_value(self.value_box, self.prop.value)

        elif self.prop.valuetype == "button":
            pass

        elif self.prop.valuetype == "boolean":

            if self.prop.value and not self.toggle.isChecked():
                self.toggle.blockSignals(True)
                self.toggle.toggle()
                self.toggle.blockSignals(False)

        elif self.prop.valuetype == "string":
            pass
        elif self.prop.valuetype == "enum":
            self.combo.blockSignals(True)
            self.combo.setCurrentText(prop.value)
            self.combo.blockSignals(False)

        if emit_value_changed:
            self.value_changed.emit(self)

    def reset(self):
        if self.prop.valuetype == "integer":
            self.update_box_value(self.value_box, self.prop.defval)
            self.update_slider_value(self.sld, self.prop.defval)

        elif self.prop.valuetype == "double":
            self.update_box_value(self.value_box, self.prop.defval)
            self.update_slider_value(self.sld, self.prop.defval)

        elif self.prop.valuetype == "button":
            pass

        elif self.prop.valuetype == "boolean":
            if self.prop.defval and not self.toggle.isChecked():
                self.toggle.toggle()

        elif self.prop.valuetype == "string":
            pass
        elif self.prop.valuetype == "enum":
            self.combo.setCurrentText(self.prop.defval)
        self.value_changed.emit(self)
        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          self.prop.defval, self.prop.valuetype)

# Copyright 2017 The Imaging Source Europe GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from PyQt5.QtWidgets import (QHBoxLayout, QSlider, QPushButton,
                             QCheckBox, QComboBox, QWidget,
                             QSpinBox, QDoubleSpinBox, QLabel)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, pyqtSignal
from . import TcamCaptureData, TcamSlider, TcamSpinBox
import logging

log = logging.getLogger(__name__)


class Prop(object):
    """Property container"""
    def __init__(self, name, value, minval, maxval, defval, step, valuetype,
                 flags, category, group):
        self.name = name
        self.value = value
        self.minval = minval
        self.maxval = maxval
        self.defval = defval
        self.step = step
        self.valuetype = valuetype
        self.flags = flags
        self.category = category
        self.group = group


class PropertyWidget(QWidget):

    value_changed = pyqtSignal(object)

    """display widget for tcam property"""
    def __init__(self, data: TcamCaptureData, prop: Prop,
                 app_property: bool=False):
        super().__init__()
        self.app_property = app_property
        self.tcam = data.tcam
        self.signals = data.signals
        self.prop = prop
        self.is_log = False
        self.setup_ui()

    def __repr__(self):
        return repr((self.prop.name, self.prop.valuetype,
                     self.prop.category, self.prop.group))

    def __setup_ui_boolean(self):
        """
        Helper function that contains all setup code for bool UIs
        """
        self.toggle = QCheckBox(self)
        self.toggle.setCheckable(True)
        if self.prop.value:
            self.toggle.toggle()
        self.toggle.toggled.connect(self.button_clicked)
        self.layout.addWidget(self.toggle)

    def __setup_ui_integer(self):
        """

        """

        self.value_box = TcamSpinBox.TcamSpinBox(self)
        try:
            self.value_box.setRange(self.prop.minval, self.prop.maxval)
        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))

        self.value_box.setSingleStep(self.prop.step)
        self.value_box.blockSignals(True)
        self.value_box.setValue(self.prop.value)
        self.value_box.blockSignals(False)
        self.value_box.valueChanged[int].connect(self.set_property_box)
        self.value_box.setKeyboardTracking(False)

        if self.is_log:
            log.debug("Adding log slider for {}".format(self.prop.name))
            self.sld = TcamSlider.TcamLogSlider(self)
            self.sld.valueLogChanged.connect(self.set_property)
        else:
            self.sld = TcamSlider.TcamSlider(self)
            self.sld.valueChanged[int].connect(self.set_property)

        self.sld.setFocusPolicy(Qt.NoFocus)
        try:
            self.sld.blockSignals(True)
            self.sld.setRange(self.prop.minval, self.prop.maxval)
            self.sld.setValue(self.prop.value)
            self.sld.blockSignals(False)

        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))
        self.sld.setSingleStep(self.prop.step)
        self.sld.doubleClicked.connect(self.reset)

        self.layout.addWidget(self.sld)
        self.layout.addWidget(self.value_box)

    def __setup_ui_double(self):
        """
        Helper function that contains all setup code for doube UIs
        """
        self.value_box = TcamSpinBox.TcamDoubleSpinBox(self)
        try:
            self.value_box.setRange(self.prop.minval, self.prop.maxval)
        except OverflowError:
            log.error("Property {} reported a range "
                      "that could not be handled".format(self.prop.name))

        self.value_box.setSingleStep(self.prop.step)
        self.value_box.blockSignals(True)
        self.value_box.setValue(self.prop.value)
        self.value_box.blockSignals(False)
        self.value_box.valueChanged[float].connect(self.set_property_box)
        self.value_box.setKeyboardTracking(False)

        if self.is_log:
            self.sld = TcamSlider.TcamLogSlider(self)
            self.sld.valueLogChanged.connect(self.set_property)
        else:
            self.sld = TcamSlider.TcamSlider(self)
            self.sld.valueChanged[int].connect(self.set_property)

        self.sld.setFocusPolicy(Qt.NoFocus)
        try:
            self.sld.setRange(int(self.prop.minval * 100), int(self.prop.maxval * 100))
            self.sld.blockSignals(True)
            self.sld.setValue(int(self.prop.value * 100))
            self.sld.blockSignals(False)

        except OverflowError:
            log.warning("Property {} reported a range "
                        "that could not be handled min: {} max: {}".format(self.prop.name,
                                                                           self.prop.minval,
                                                                           self.prop.maxval))
        self.sld.setSingleStep(int(self.prop.step * 100))
        self.sld.doubleClicked.connect(self.reset)

        self.sld.setGeometry(30, 40, 100, 30)
        self.layout.addWidget(self.sld)
        self.layout.addWidget(self.value_box)

    def setup_ui(self):
        self.layout = QHBoxLayout()

        def should_be_logarithmic(prop):
            """
            Returns True when the range is > 5000
            """
            if prop.valuetype == "integer":
                if (prop.maxval - prop.minval) >= 5000:
                    return True

            elif prop.valuetype == "double":
                if (prop.maxval - prop.minval) >= 5000:
                    return True

            return False

        self.setLayout(self.layout)

        self.is_log = should_be_logarithmic(self.prop)

        if self.prop.valuetype == "integer":

            self.__setup_ui_integer()

        elif self.prop.valuetype == "double":

            self.__setup_ui_double()

        elif self.prop.valuetype == "button":
            self.checkbox = QPushButton(self)
            self.checkbox.clicked.connect(self.set_property)
            self.layout.addWidget(self.checkbox)

        elif self.prop.valuetype == "boolean":
            self.__setup_ui_boolean()

        elif self.prop.valuetype == "string":
            pass

        elif self.prop.valuetype == "enum":
            self.combo = QComboBox(self)
            entry_list = self.tcam.get_tcam_menu_entries(self.prop.name)

            for e in entry_list:
                self.combo.addItem(e)
                self.combo.setCurrentText(self.prop.value)
                self.combo.currentIndexChanged['QString'].connect(self.set_property)
                self.layout.addWidget(self.combo)

    def button_clicked(self):
        log.debug("button clicked")
        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          self.toggle.isChecked(), self.prop.valuetype)
        self.value_changed.emit(self)

    def set_property(self, value, emit_value_changed=True):
        self.prop.value = value
        if self.prop.valuetype == "integer":
            self.update_box_value(self.value_box, value)

        if self.prop.valuetype == "double":
            self.update_box_value(self.value_box, value / 100)

            self.signals.change_property.emit(self.tcam, self.prop.name,
                                              float(value / 100), self.prop.valuetype)
            return

        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          value, self.prop.valuetype)
        if emit_value_changed:
            self.value_changed.emit(self)

    def set_property_box(self, value):
        if self.prop.valuetype == "integer":
            self.update_slider_value(self.sld, value)

        if self.prop.valuetype == "double":
            self.update_slider_value(self.sld, value)

            self.signals.change_property.emit(self.tcam, self.prop.name,
                                              float(value), self.prop.valuetype)
            return

        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          value, self.prop.valuetype)
        self.value_changed.emit(self)

    def update_box_value(self, box, value):
        box.blockSignals(True)
        box.setValue(value)
        box.blockSignals(False)

    def update_box_range(self, box, minval, maxval):
        """"""
        box.blockSignals(True)
        box.setRange(self.prop.minval,
                     self.prop.maxval)
        box.blockSignals(False)

    def update_slider_value(self, slider, value):
        slider.blockSignals(True)
        try:
            if self.prop.valuetype == "double":
                slider.setValue(int(value * 100))
            else:
                slider.setValue(value)

        except OverflowError:
            log.info("The slider for '{}' had a value outside of the integer "
                     "range. That should no happen.".format(self.prop.name))
        finally:
            slider.blockSignals(False)

    def update_slider_range(self, slider, minval, maxval):
        """"""
        self.sld.blockSignals(True)
        try:
            if self.prop.valuetype == "double":
                self.sld.setRange(self.prop.minval * 100,
                                  self.prop.maxval * 100)
            else:
                self.sld.setRange(self.prop.minval,
                                  self.prop.maxval)
        except OverflowError:
            log.error("The slider for '{}' had a value outside of the integer "
                      "range. That should no happen. "
                      "Min: '{}' Max: {}".format(self.prop.name,
                                                 self.prop.minval,
                                                 self.prop.maxval))
        finally:
            self.sld.blockSignals(False)

    def update(self, prop: Prop):

        emit_value_changed = False

        if self.prop.value != prop.value:
            emit_value_changed = True

        self.prop = prop
        if self.prop.valuetype == "integer":
            if type(self.value_box) is TcamSpinBox.TcamSpinBox:
                if self.value_box.active():
                    # box.setValue(value)
                    return
            self.update_slider_value(self.sld, self.prop.value)

            self.update_slider_range(self.sld,
                                     self.prop.minval,
                                     self.prop.maxval)
            self.update_box_range(self.value_box,
                                  self.prop.minval,
                                  self.prop.maxval)
            self.update_box_value(self.value_box, self.prop.value)
        elif self.prop.valuetype == "double":
            self.update_slider_range(self.sld,
                                     self.prop.minval,
                                     self.prop.maxval)
            self.update_slider_value(self.sld, self.prop.value)
            self.update_box_range(self.value_box,
                                  self.prop.minval,
                                  self.prop.maxval)
            self.update_box_value(self.value_box, self.prop.value)

        elif self.prop.valuetype == "button":
            pass

        elif self.prop.valuetype == "boolean":

            if self.prop.value and not self.toggle.isChecked():
                self.toggle.blockSignals(True)
                self.toggle.toggle()
                self.toggle.blockSignals(False)

        elif self.prop.valuetype == "string":
            pass
        elif self.prop.valuetype == "enum":
            self.combo.blockSignals(True)
            self.combo.setCurrentText(prop.value)
            self.combo.blockSignals(False)

        if emit_value_changed:
            self.value_changed.emit(self)

    def reset(self):
        if self.prop.valuetype == "integer":
            self.update_box_value(self.value_box, self.prop.defval)
            self.update_slider_value(self.sld, self.prop.defval)

        elif self.prop.valuetype == "double":
            self.update_box_value(self.value_box, self.prop.defval)
            self.update_slider_value(self.sld, self.prop.defval)

        elif self.prop.valuetype == "button":
            pass

        elif self.prop.valuetype == "boolean":
            if self.prop.defval and not self.toggle.isChecked():
                self.toggle.toggle()

        elif self.prop.valuetype == "string":
            pass
        elif self.prop.valuetype == "enum":
            self.combo.setCurrentText(self.prop.defval)
        self.value_changed.emit(self)
        self.signals.change_property.emit(self.tcam, self.prop.name,
                                          self.prop.defval, self.prop.valuetype)

# Copyright 2017 The Imaging Source Europe GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Property container display widget for tcam property Helper function that contains all setup code for bool UIs Helper function that contains all setup code for doube UIs Returns True when the range is > 5000 # box.setValue(value)