from PyQt4.QtGui import QCheckBox, QWidget, QLabel
from PyQt4.QtCore import Qt, pyqtSignal, QObject

from segyviewlib import ArraySpinBox


class IndexController(QObject):
    index_changed = pyqtSignal(int)
    min_max_changed = pyqtSignal(tuple)

    def __init__(self, parent=None, context=None, slice_direction_index_source=None):
        QObject.__init__(self, parent)

        self._context = context
        self._slice_direction = slice_direction_index_source

        self._current_index_label = QLabel("")
        self._current_index_label.setDisabled(True)

        # the select-index-widgets
        self.index_s_box = self._set_up_index_widget()

        # the min max settings widgets
        self._min_active, self._min_spinbox = self._set_up_min_max_widgets()
        self._max_active, self._max_spinbox = self._set_up_min_max_widgets()

        self._initialize(self._context.slice_data_source().indexes_for_direction(self._slice_direction).tolist())

    @property
    def index_widget(self):
        return self.index_s_box

    @property
    def min_checkbox(self):
        return self._min_active

    @property
    def min_spinbox(self):
        return self._min_spinbox

    @property
    def max_checkbox(self):
        return self._max_active

    @property
    def max_spinbox(self):
        return self._max_spinbox

    @property
    def current_index_label(self):
        return self._current_index_label

    def _set_up_index_widget(self):
        index_s_box = ArraySpinBox([0])
        index_s_box.valueChanged.connect(self.index_changed.emit)
        return index_s_box

    def _set_up_min_max_widgets(self):
        check_box = QCheckBox()
        spin_box = ArraySpinBox([0])
        spin_box.setDisabled(True)

        check_box.toggled.connect(spin_box.setEnabled)
        check_box.toggled.connect(self._min_max_value_changed)
        spin_box.valueChanged.connect(self._min_max_value_changed)

        return check_box, spin_box

    def _initialize(self, indexes):
        self._indexes = indexes
        # set up initial min max values
        self.current_index = 0
        self.min_index = 0
        self.max_index = len(self._indexes) - 1

        self.index_s_box.update_view(self._indexes, self.current_index)

        self._min_spinbox.update_view(self._indexes, self.min_index)
        self._max_spinbox.update_view(self._indexes, self.max_index)

        self._min_spinbox.setMaximum(self.max_index - 1)
        self._max_spinbox.setMinimum(self.min_index + 1)

        self._max_active.setCheckState(Qt.Unchecked)
        self._min_active.setCheckState(Qt.Unchecked)

        self._update_label()
        self._min_max_value_changed()

    @staticmethod
    def _to_lbl_txt(val):
        return str(round(val, 4)) if isinstance(val, float) else str(val)

    def _update_label(self):
        self._current_index_label.setText(
            "pos: {0} - [{1}:{2}]".format(self._to_lbl_txt(self._indexes[self.current_index]),
                                          self._to_lbl_txt(self._indexes[self.min_index]),
                                          self._to_lbl_txt(self._indexes[self.max_index])))

    def update_index(self, index):
        self.index_s_box.update_view(self._indexes, index)
        self.current_index = index
        self._update_label()

    def update_view(self, indexes, index):
        if self._indexes != indexes:
            # re initialize when index is changed
            self._initialize(indexes)
        else:
            self.update_index(index)

    def _min_max_value_changed(self):
        if self._min_active.isChecked():
            self.min_index = self._min_spinbox.value()
            min_max = self.min_index + 1 if self.min_index < self.max_index - 1 else self.max_index
            self._max_spinbox.setMinimum(min_max)

            # move current index to inside the boundary
            if self.min_index > self.current_index:
                self.index_changed.emit(self.min_index)
        else:
            self.min_index = 0

        if self._max_active.isChecked():
            self.max_index = self._max_spinbox.value()
            max_min = self.max_index - 1 if self.max_index > 0 else 0
            self._min_spinbox.setMaximum(max_min)

            # move current index to inside the boundary
            if self.max_index < self.current_index:
                self.index_changed.emit(self.max_index)
        else:
            self.max_index = len(self._indexes) - 1

        self.min_max_changed.emit((self.min_index, self.max_index))