/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "IntSliderWidget.h"

#include "pqLineEdit.h"
#include "vtkPVVersion.h"

// Qt includes
#include <QHBoxLayout>
#include <QIntValidator>
#include <QSlider>

namespace tomviz {

IntSliderWidget::IntSliderWidget(bool showLineEdit, QWidget* p) : QWidget(p)
{
  this->BlockUpdate = false;
  this->Value = 0;
  this->Minimum = 0;
  this->Maximum = 1;
  this->StrictRange = false;

  QHBoxLayout* l = new QHBoxLayout(this);
  l->setContentsMargins(0, 0, 0, 0);
  this->Slider = new QSlider(Qt::Horizontal, this);
  this->Slider->setRange(this->Minimum, this->Maximum);
  this->Slider->setFocusPolicy(Qt::StrongFocus);
  l->addWidget(this->Slider, 4);
  this->Slider->setObjectName("Slider");
  if (showLineEdit) {
    this->LineEdit = new pqLineEdit(this);
    l->addWidget(this->LineEdit);
    this->LineEdit->setObjectName("LineEdit");
    this->LineEdit->setValidator(new QIntValidator(this->LineEdit));
    this->LineEdit->setTextAndResetCursor(QString().setNum(this->Value));
  } else {
    this->LineEdit = nullptr;
  }

  QObject::connect(this->Slider, &QSlider::valueChanged, this,
                   &IntSliderWidget::sliderChanged);
  if (showLineEdit) {
    QObject::connect(this->LineEdit, &pqLineEdit::textChanged, this,
                     &IntSliderWidget::textChanged);
    QObject::connect(this->LineEdit, &pqLineEdit::textChangedAndEditingFinished,
                     this, &IntSliderWidget::editingFinished);
  }
}

IntSliderWidget::~IntSliderWidget() {}

void IntSliderWidget::setLineEditWidth(int width)
{
  if (this->LineEdit) {
    QSize s = this->LineEdit->sizeHint();
    QSize newSize(width, s.height());
    this->LineEdit->setFixedSize(newSize);
  }
}

void IntSliderWidget::setPageStep(int step)
{
  this->Slider->setPageStep(step);
}

int IntSliderWidget::value() const
{
  return this->Value;
}

void IntSliderWidget::setValue(int val)
{
  if (this->Value == val) {
    return;
  }

  this->Value = val;

  if (!this->BlockUpdate) {
    // set the slider
    this->updateSlider();

    // set the text
    if (this->LineEdit) {
      this->BlockUpdate = true;
      this->LineEdit->setTextAndResetCursor(QString().setNum(val));
      this->BlockUpdate = false;
    }
  }

  emit this->valueChanged(this->Value);
}

int IntSliderWidget::maximum() const
{
  return this->Maximum;
}

void IntSliderWidget::setMaximum(int val)
{
  this->Maximum = val;
  this->updateValidator();
  this->updateSlider();
}

int IntSliderWidget::minimum() const
{
  return this->Minimum;
}

void IntSliderWidget::setMinimum(int val)
{
  this->Minimum = val;
  this->updateValidator();
  this->updateSlider();
}

void IntSliderWidget::updateValidator()
{
  if (!this->LineEdit) {
    return;
  }
  if (this->StrictRange) {
    this->LineEdit->setValidator(
      new QIntValidator(this->minimum(), this->maximum(), this->LineEdit));
  } else {
    this->LineEdit->setValidator(new QIntValidator(this->LineEdit));
  }
}

bool IntSliderWidget::strictRange() const
{
  if (!this->LineEdit) {
    return true;
  }
  const QIntValidator* dv =
    qobject_cast<const QIntValidator*>(this->LineEdit->validator());
  if (!dv) {
    return false;
  }
  return dv->bottom() == this->minimum() && dv->top() == this->maximum();
}

void IntSliderWidget::setStrictRange(bool s)
{
  this->StrictRange = s;
  this->updateValidator();
}

void IntSliderWidget::sliderChanged(int val)
{
  if (!this->BlockUpdate) {
    this->BlockUpdate = true;
    if (this->LineEdit) {
      this->LineEdit->setTextAndResetCursor(QString().setNum(val));
    }
    this->setValue(val);
    emit this->valueEdited(val);
    this->BlockUpdate = false;
  }
}

void IntSliderWidget::textChanged(const QString& text)
{
  if (!this->BlockUpdate) {
    int val = text.toInt();
    this->BlockUpdate = true;
    int sliderVal = val - this->Minimum;
    this->Slider->setValue(sliderVal);
    this->setValue(val);
    this->BlockUpdate = false;
  }
}

void IntSliderWidget::editingFinished()
{
  emit this->valueEdited(this->Value);
}

void IntSliderWidget::updateSlider()
{
  this->Slider->blockSignals(true);
  int v = this->Value - this->Minimum;
  this->Slider->setRange(this->Minimum, this->Maximum);
  this->Slider->setValue(v);
  this->Slider->blockSignals(false);
}
} // namespace tomviz
