#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2015 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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, or (at your option)
# any later version.
#
# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#

from PyQt4 import Qt, QtCore, QtGui

class Range(object):
    def __init__(self, minv, maxv, step, default, min_length):
        self.min = float(minv)
        self.max = float(maxv)
        self.step = float(step)
        self.default = float(default)
        self.min_length = min_length
        self.find_precision()
        self.find_nsteps()

    def find_precision(self):
        temp = str(float(self.step)-int(self.step))[2:]
        if len(temp) > 13:
          self.precision = 15
        else:
          self.precision = len(temp)+2

    def find_nsteps(self):
        self.nsteps = (self.max + self.step - self.min)/self.step

    def demap_range(self,val):
        if val > self.max:
          val = self.max
        if val < self.min:
          val = self.min
        return ((val-self.min)/self.step)

    def map_range(self,val):
        if val > self.nsteps:
          val = self.max
        if val < 0:
          val = 0
        return (val*self.step+self.min)

class RangeWidget(QtGui.QWidget):
    def __init__(self, ranges, slot, label, style):
        """ Creates the QT Range widget """
        QtGui.QWidget.__init__(self)

        self.range = ranges
        self.slot = slot
        self.style = style

        layout = Qt.QHBoxLayout()
        label = Qt.QLabel(label)
        layout.addWidget(label)

        if style == "dial":
            self.d_widget = self.Dial(self, self.range, self.ds_modified_slot)
        elif style == "slider":
            self.d_widget = self.Slider(self, self.range, self.ds_modified_slot)
        elif style == "counter":
            self.d_widget = self.Counter(self, self.range, self.c_modified_slot)
        elif style == "counter_slider":
            self.d_widget = self.CounterSlider(self, self.range, self.ds_modified_slot, self.c_modified_slot)
        else:
            self.d_widget = self.CounterSlider(self, self.range, self.ds_modified_slot, self.c_modified_slot)

        layout.addWidget(self.d_widget)
        self.setLayout(layout)

    def ds_modified_slot(self,val):
        nval = self.range.map_range(val)
        self.slot(nval)
        if self.style == "counter_slider":
          self.d_widget.set_counter(nval)

    def c_modified_slot(self,val):
        self.slot(val)
        if self.style == "counter_slider":
          temp = self.range.demap_range(val)
          if temp-int(temp) >= 0.5:
            self.d_widget.set_slider(int(temp)+1)
          else:
            self.d_widget.set_slider(int(temp))

    class Dial(QtGui.QDial):
        """ Creates the range using a dial """
        def __init__(self, parent, ranges, slot):
            QtGui.QDial.__init__(self, parent)
            self.setRange(0, ranges.nsteps-1)
            self.setSingleStep(1)
            self.setNotchesVisible(True)
            temp = ranges.demap_range(ranges.default)
            if temp-int(temp) >= 0.5:
              temp = int(temp)+1
            else:
              temp = int(temp)
            self.setValue(temp)
            self.valueChanged.connect(slot)

    class Slider(QtGui.QSlider):
        """ Creates the range using a slider """
        def __init__(self, parent, ranges, slot):
            QtGui.QSlider.__init__(self, QtCore.Qt.Horizontal, parent)
            self.setFocusPolicy(QtCore.Qt.NoFocus)
            self.setRange(0, ranges.nsteps-1)
            temp = ranges.demap_range(ranges.default)
            if temp-int(temp) >= 0.5:
              temp = int(temp)+1
            else:
              temp = int(temp)
            self.setValue(temp)
            self.setPageStep(1)
            self.setSingleStep(1)
            self.setTickPosition(2)
            if ranges.nsteps > ranges.min_length:
              self.setTickInterval(int(ranges.nsteps/ranges.min_length))
            else:
              self.setTickInterval(1)
            self.valueChanged.connect(slot)

        def mousePressEvent(self, event):
            if((event.button() == QtCore.Qt.LeftButton)):
                newVal = self.minimum() + ((self.maximum()-self.minimum()) * event.x()) / self.width()
                self.setValue(newVal)
                event.accept()
            QtGui.QSlider.mousePressEvent(self, event)

    class Counter(QtGui.QDoubleSpinBox):
        """ Creates the range using a counter """
        def __init__(self, parent, ranges, slot):
            QtGui.QDoubleSpinBox.__init__(self, parent)
            self.setRange(ranges.min, ranges.max)
            self.setValue(ranges.default)
            self.setSingleStep(ranges.step)
            self.setKeyboardTracking(False)
            self.setDecimals(ranges.precision)
            self.valueChanged.connect(slot)

    class CounterSlider(QtGui.QWidget):
        """ Creates the range using a counter and slider """
        def __init__(self, parent, ranges, s_slot, c_slot):
            QtGui.QWidget.__init__(self, parent)

            # Need another horizontal layout
            layout = Qt.QHBoxLayout()

            # Create a slider with the top-level widget as the parent
            self.slider = RangeWidget.Slider(parent,ranges,s_slot)
            layout.addWidget(self.slider)

            # Setup the counter
            self.counter = RangeWidget.Counter(parent,ranges,c_slot)
            layout.addWidget(self.counter)

            # Wire the events to each other
            #counter.valueChanged.connect(slider.setValue)
            #slider.valueChanged.connect(counter.setValue)
            self.counter.valueChanged.connect(c_slot)
            self.slider.valueChanged.connect(s_slot)

            self.setLayout(layout)

        def set_slider(self,val):
            self.slider.setValue(val)
        def set_counter(self,val):
            self.counter.setValue(val)