#ifndef CVVISUAL_INTERVALLSELECTOR_HPP
#define CVVISUAL_INTERVALLSELECTOR_HPP

#include <vector>
#include <algorithm>

#include <QWidget>
#include <QDoubleSpinBox>
#include <QRadioButton>
#include <QCheckBox>
#include <QLabel>
#include <QVBoxLayout>
#include <QButtonGroup>

#include "../util/util.hpp"
#include "../util/observer_ptr.hpp"
#include "signalslot.hpp"

namespace cvv
{
namespace qtutil
{

/**
 * @brief Selects elements from a double range. The elements are converted to a
 * double using the
 * given DoubleExtractor functor.
 */
class IntervallSelector : public QWidget
{
      public:
	/**
	 * @brief Constructor
	 * @param min Minimal value
	 * @param max Maximal value
	 * @param parent Parent widget
	 */
	IntervallSelector(double min, double max, QWidget *parent = nullptr)
	    : QWidget{ parent }, sigSettingsChanged_{}, min_{ nullptr },
	      max_{ nullptr }, complement_{ nullptr }
	{
		auto minb = util::make_unique<QDoubleSpinBox>();
		min_ = *minb;

		auto maxb = util::make_unique<QDoubleSpinBox>();
		max_ = *maxb;
		auto complement =
		    util::make_unique<QCheckBox>("Select the complement");
		complement_ = *complement;

		// set ranges
		minb->setRange(min, max);
		maxb->setValue(min);
		maxb->setRange(min, max);
		maxb->setValue(max);
		// connect
		QObject::connect(min_.getPtr(), SIGNAL(valueChanged(double)),
				 &sigSettingsChanged_, SIGNAL(signal()));
		QObject::connect(max_.getPtr(), SIGNAL(valueChanged(double)),
				 &sigSettingsChanged_, SIGNAL(signal()));
		QObject::connect(complement_.getPtr(), SIGNAL(clicked()),
				 &sigSettingsChanged_, SIGNAL(signal()));
		// build ui
		auto lay = util::make_unique<QVBoxLayout>();
		lay->setContentsMargins(0, 0, 0, 0);
		lay->addWidget(
		util::make_unique<QLabel>(QString{"From lower bound ("}+
					  QString::number(min)+QString{"):"}).release());
		lay->addWidget(minb.release());
		lay->addWidget(
		    util::make_unique<QLabel>(QString{"To upper bound ("}+
					      QString::number(max)+QString{"):"}).release());
		lay->addWidget(maxb.release());
		lay->addWidget(complement.release());
		setLayout(lay.release());
	}

	/**
	 * @brief Returns elements from the selected range.
	 * @param selection The selection.
	 * @param extr Extractor functor (has to be double extr(Type))
	 * @return the selected values
	 */
	template <class Type, class DoubleExtractor>
	std::vector<Type> select(std::vector<Type> selection,
				 DoubleExtractor extr) const
	{
		std::vector<Type> result;

		bool complement = complement_->isChecked();

		std::copy_if(selection.begin(), selection.end(),
			     std::back_insert_iterator<std::vector<Type>>(
				 result),
			     [=](Type t1)
		{
			return complement !=
			       // check weather the element is in the interval
			       (((min_->value()) <= extr(t1)) &&
				(extr(t1) <= max_->value()));
		});
		return result;
	}

	/**
	 * @brief Returns the signal emitted when settings are changed.
	 * @return The signal emitted when settings are changed.
	 */
	const Signal &signalSettingsChanged() const
	{
		return sigSettingsChanged_;
	}

      private:
	/**
	 * @brief Emitted when settings are changed.
	 */
	const Signal sigSettingsChanged_;
	/**
	 * @brief Spinbox to select the lower bound
	 */
	util::ObserverPtr<QDoubleSpinBox> min_;
	/**
	 * @brief Spinbox to select the upper bound
	 */
	util::ObserverPtr<QDoubleSpinBox> max_;
	/**
	 * @brief Weather the complement should be selected
	 */
	util::ObserverPtr<QCheckBox> complement_;
};
}
}
#endif // CVVISUAL_INTERVALLSELECTOR_HPP