from PyQt4 import QtGui, QtCore
from copy import copy
import FlatCAMApp
import re


class RadioSet(QtGui.QWidget):
    def __init__(self, choices, orientation='horizontal', parent=None):
        """
        The choices are specified as a list of dictionaries containing:

        * 'label': Shown in the UI
        * 'value': The value returned is selected

        :param choices: List of choices. See description.
        :type choices: list
        """
        super(RadioSet, self).__init__(parent)
        self.choices = copy(choices)

        if orientation == 'horizontal':
            layout = QtGui.QHBoxLayout()
        else:
            layout = QtGui.QVBoxLayout()

        group = QtGui.QButtonGroup(self)

        for choice in self.choices:
            choice['radio'] = QtGui.QRadioButton(choice['label'])
            group.addButton(choice['radio'])
            layout.addWidget(choice['radio'], stretch=0)
            choice['radio'].toggled.connect(self.on_toggle)

        layout.addStretch()
        self.setLayout(layout)

        self.group_toggle_fn = lambda: None

    def on_toggle(self):
        FlatCAMApp.App.log.debug("Radio toggled")
        radio = self.sender()
        if radio.isChecked():
            self.group_toggle_fn()
        return

    def get_value(self):
        for choice in self.choices:
            if choice['radio'].isChecked():
                return choice['value']
        FlatCAMApp.App.log.error("No button was toggled in RadioSet.")
        return None

    def set_value(self, val):
        for choice in self.choices:
            if choice['value'] == val:
                choice['radio'].setChecked(True)
                return
        FlatCAMApp.App.log.error("Value given is not part of this RadioSet: %s" % str(val))


class LengthEntry(QtGui.QLineEdit):
    def __init__(self, output_units='IN', parent=None):
        super(LengthEntry, self).__init__(parent)

        self.output_units = output_units
        self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")

        # Unit conversion table OUTPUT-INPUT
        self.scales = {
            'IN': {'IN': 1.0,
                   'MM': 1/25.4},
            'MM': {'IN': 25.4,
                   'MM': 1.0}
        }

    def returnPressed(self, *args, **kwargs):
        val = self.get_value()
        if val is not None:
            self.set_text(QtCore.QString(str(val)))
        else:
            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())

    def get_value(self):
        raw = str(self.text()).strip(' ')
        match = self.format_re.search(raw)

        if not match:
            return None
        try:
            if match.group(2) is not None and match.group(2).upper() in self.scales:
                return float(eval(match.group(1)))*float(self.scales[self.output_units][match.group(2).upper()])
            else:
                return float(eval(match.group(1)))
        except:
            FlatCAMApp.App.log.warning("Could not parse value in entry: %s" % str(raw))
            return None

    def set_value(self, val):
        self.setText(QtCore.QString(str(val)))


class FloatEntry(QtGui.QLineEdit):
    def __init__(self, parent=None):
        super(FloatEntry, self).__init__(parent)

    def returnPressed(self, *args, **kwargs):
        val = self.get_value()
        if val is not None:
            self.set_text(QtCore.QString(str(val)))
        else:
            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.text())

    def get_value(self):
        raw = str(self.text()).strip(' ')
        try:
            evaled = eval(raw)
        except:
            FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
            return None

        return float(evaled)

    def set_value(self, val):
        self.setText("%.6f"%val)


class IntEntry(QtGui.QLineEdit):
    def __init__(self, parent=None):
        super(IntEntry, self).__init__(parent)

    def get_value(self):
        return int(self.text())

    def set_value(self, val):
        self.setText(QtCore.QString(str(val)))


class FCEntry(QtGui.QLineEdit):
    def __init__(self, parent=None):
        super(FCEntry, self).__init__(parent)

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

    def set_value(self, val):
        self.setText(QtCore.QString(str(val)))


class EvalEntry(QtGui.QLineEdit):
    def __init__(self, parent=None):
        super(EvalEntry, self).__init__(parent)

    def returnPressed(self, *args, **kwargs):
        val = self.get_value()
        if val is not None:
            self.setText(QtCore.QString(str(val)))
        else:
            FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())

    def get_value(self):
        raw = str(self.text()).strip(' ')
        try:
            return eval(raw)
        except:
            FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
            return None

    def set_value(self, val):
        self.setText(QtCore.QString(str(val)))


class FCCheckBox(QtGui.QCheckBox):
    def __init__(self, label='', parent=None):
        super(FCCheckBox, self).__init__(QtCore.QString(label), parent)

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

    def set_value(self, val):
        self.setChecked(val)


class FCTextArea(QtGui.QPlainTextEdit):
    def __init__(self, parent=None):
        super(FCTextArea, self).__init__(parent)

    def set_value(self, val):
        self.setPlainText(val)

    def get_value(self):
        return str(self.toPlainText())


class VerticalScrollArea(QtGui.QScrollArea):
    """
    This widget extends QtGui.QScrollArea to make a vertical-only
    scroll area that also expands horizontally to accomodate
    its contents.
    """
    def __init__(self, parent=None):
        QtGui.QScrollArea.__init__(self, parent=parent)
        self.setWidgetResizable(True)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)

    def eventFilter(self, source, event):
        """
        The event filter gets automatically installed when setWidget()
        is called.

        :param source:
        :param event:
        :return:
        """
        if event.type() == QtCore.QEvent.Resize and source == self.widget():
            # FlatCAMApp.App.log.debug("VerticalScrollArea: Widget resized:")
            # FlatCAMApp.App.log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
            # FlatCAMApp.App.log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())

            self.setMinimumWidth(self.widget().sizeHint().width() +
                                 self.verticalScrollBar().sizeHint().width())

            # if self.verticalScrollBar().isVisible():
            #     FlatCAMApp.App.log.debug(" Scroll bar visible")
            #     self.setMinimumWidth(self.widget().minimumSizeHint().width() +
            #                          self.verticalScrollBar().width())
            # else:
            #     FlatCAMApp.App.log.debug(" Scroll bar hidden")
            #     self.setMinimumWidth(self.widget().minimumSizeHint().width())
        return QtGui.QWidget.eventFilter(self, source, event)