#include "TopologyDimensionBar.h"

TopologyDimensionBar::TopologyDimensionBar(const std::vector<long> &dims, const std::vector<std::string> &dimnames, QWidget *parent) :  QWidget(parent), dims_(dims)
{
   setWhatsThis("This widget is shown for topologies with more than tree dimensions."
                " It allows either to select the dimensions to display or to fold 2 dimensions"
                " into one.");

   modeSelection = new QStackedLayout();

   // fill dnames with dimension names or enumerate them
   QStringList dnames;
   if (dimnames.size() == dims.size()) {
      for (uint i=0; i<dimnames.size(); ++i) {
         dnames.append(QString(dimnames.at(i).c_str()));
      }
   } else {
      for (uint i=0; i<dims.size(); ++i) {
         dnames.append(QString::number(i+1));
      }
   }

   QPushButton *mode1 = new QPushButton("fold");
   connect(mode1, SIGNAL(clicked()), this, SLOT(toggleMode()));
   QPushButton *mode2 = new QPushButton("select");
   connect(mode2, SIGNAL(clicked()), this, SLOT(toggleMode()));

   QWidget *gw = new QWidget(this);
   QGridLayout *grid = new QGridLayout(gw);
   grid->setContentsMargins(3,0,3,0);
   grid->setVerticalSpacing(0);
   modeSelection->addWidget(gw);

   bool hasFoldingMode = (dims.size() <= 6); // folding mode for 4-6 dimensions
   if (hasFoldingMode) {
      grid->addWidget(mode1, 0, 0, -1, 1);
   }

   // ------ dimension selection mode -------
   QSpacerItem *space = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
   grid->addItem(space, 0, dims.size()+2);
   for (uint i=0; i<dims.size(); i++) {
      int value = (i >= 3) ? 0 : -1;
      ValuePopupSlider *vslider = new ValuePopupSlider(value, dims[i]-1);
      connect(vslider, SIGNAL(valueChanged(int)), this, SLOT(selectedDimensionsChanged()));
      sliders.push_back(vslider);
      grid->addWidget(vslider, 0, i+1);
      QLabel *label= new QLabel(dnames.at(i));
      grid->addWidget(label, 1, i+1, Qt::AlignHCenter);
   }

   // ------ folding mode -------
   if (hasFoldingMode) {
      int d[6];
      for (uint i=0; i < 6; i++) d[i]=-1;
      for (uint i=0; i < 3; i++) {
         d[i*2] = i;
      }
      for (uint i=3; i < dims.size(); i++) {
         d[(i-3)*2 + 1] = i;
      }
      select = new OrderWidget(d, dnames);

      connect(select, SIGNAL(foldingDimensionsChanged()), this, SLOT(foldingDimensionsChanged()));

      QHBoxLayout *fold = new QHBoxLayout();
      QWidget *foldw = new QWidget(this);
      fold->addWidget(mode2);
      fold->addWidget(select);
      QSpacerItem *space = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
      fold->addItem(space);
      foldw->setLayout(fold);

      modeSelection->addWidget(foldw);
   }

   // ------ -------
   setLayout(modeSelection);
   this->setMaximumHeight(minimumSizeHint().height());
}

void TopologyDimensionBar::toggleMode() {
   modeSelection->setCurrentIndex( (modeSelection->currentIndex()+1) % 2 );
   if (modeSelection->currentIndex() == 0)
      selectedDimensionsChanged();
   else
      foldingDimensionsChanged();
}

/**
  emit dimensionsChanged, if new combinations of dimensions are selected
  */
void TopologyDimensionBar::foldingDimensionsChanged() {
   std::vector<std::vector<int> > val = select->getValues();
   emit foldingDimensionsChanged(val);
}

/**
  emit dimensionsChanged, if the selected values are valid (3 full dimensions)
  */
void TopologyDimensionBar::selectedDimensionsChanged() {
   std::vector<long> val = getValues();
   //if (val.size() > 0)
   emit selectedDimensionsChanged(val);
}

/**
  returns the user selections for all dimensions
  -1  = display all elements
  >=0 = display one element
  */
std::vector<long> TopologyDimensionBar::getValues() {
   std::vector<long> ret;

   int count=0;
   for (uint i=0; i<sliders.size(); i++) {
      long val = sliders.at(i)->value();
      if (val == -1) count++;
      ret.push_back(val);
   }
   if (count != 3) ret.clear(); // not valid
   return ret;
}

///////////////////////////////////////////////////////////////////////////////

ValuePopupSlider::ValuePopupSlider(int value, int max) : value_(value), max_(max)
{
   win = 0;
   push = new QPushButton();
   push->setMinimumWidth(push->fontMetrics().width("__999__"));
   setValue(value);

   QVBoxLayout *layout = new QVBoxLayout(this);
   layout->setContentsMargins(0,0,0,0);
   setLayout(layout);
   layout->addWidget(push);

   connect(push, SIGNAL(clicked()), this, SLOT(showSlider()));
}

void ValuePopupSlider::setValue(int val) {
   value_ = val;
   if (val >= 0) push->setText(QString::number(val));
   else push->setText("all");
   emit valueChanged(val);
}

int ValuePopupSlider::value() const {
   return value_;
}

void ValuePopupSlider::showSlider() {
   if (win && win->isVisible()) {
      win->close();
      delete win;
      win = 0;
   } else {
      win=new SliderPopup(-1, max_, value_);
      //qDebug() << "max:" << max_;
      connect(win->slider_, SIGNAL(valueChanged(int)), this, SLOT(setValue(int))); // set label
      win->show();
      QPoint pos = push->mapToGlobal(QPoint(0, -win->size().height()));
      win->move(pos); // show slider widget above the button
   }
}

///////////////////////////////////////////////////////////////////////////////

/** label used in Slider popup */
class SliderPopupLabel : public QWidget
{
public:
   explicit SliderPopupLabel(int max) : max_(max) {}
   virtual QSize minimumSizeHint () const {
      return QSize(this->fontMetrics().width("all"), 0);
   }
protected:
   virtual void paintEvent(QPaintEvent*)
   {
      QPainter p(this);
      p.drawText(rect(), Qt::AlignTop, QString::number(max_));
      p.drawText(0, size().height(), "all");
   }
private:
   int max_;
};

SliderPopup::SliderPopup(int min, int max, int value)
{
   setWindowFlags(Qt::Popup);

   slider_ = new QSlider(this);
   slider_->setOrientation(Qt::Vertical);
   QHBoxLayout *layout = new QHBoxLayout(this);
   setLayout(layout);

   slider_->setMinimum(min);
   slider_->setMaximum(max);
   slider_->setValue(value);

   layout->addWidget(slider_);
   layout->addWidget(new SliderPopupLabel(max));
   connect(slider_, SIGNAL(sliderReleased()), this, SLOT(close()));
}
