/***************************************************************************
**
**  This file is part of QGpGuiTools.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-04-01
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "PropertyWidget.h"
#include "ColorButton.h"

namespace QGpGuiTools {

/*!
  \class PropertyWidget PropertyWidget.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
PropertyWidget::PropertyWidget(QWidget * parent)
    : QWidget(parent)
{
  TRACE;
  _wid=0;
}

PropertyWidget::~PropertyWidget()
{
  TRACE;
  QHash<int, PropertyValue *>::Iterator it;
  for(it=_list.begin();it!=_list.end();it++ ) delete it.value();
}

void PropertyWidget::touched(PropertyValue * p, QVariant val)
{
  TRACE;
  // Commit property value and force re-read of all properties except this one (p), because frozen.
  p->freeze(true);
  emit setProperty(p->id(), val);
  emit refreshValues();
  p->freeze(false);
  // Force re-read of all properties from linked widgets
  emit touched();
}

void PropertyWidget::linkTo(PropertyWidget * w)
{
  // First make sure it is not already connected
  disconnect(this, SIGNAL(touched()), w, SIGNAL(refreshValues()));
  connect(this, SIGNAL(touched()), w, SIGNAL(refreshValues()));
}

inline PropertyValue::WidgetType PropertyWidget::determineWidgetType(int pid, QWidget * w, QWidget * label)
{
  TRACE;
  if(!w) return PropertyValue::Unknown;
  // First consider special custom types
  PropertyValue::WidgetType t=determineCustomWidgetType(pid, w, label);
  // If not handled consider standard types
  if(t==PropertyValue::Unknown) {
    if(w->inherits("QGpGuiTools::ColorButton")) t=PropertyValue::ColorButton;
    else if(w->inherits("QAbstractButton")) t=PropertyValue::Button;
    else if(w->inherits("QLineEdit")) t=PropertyValue::LineEdit;
    else if(w->inherits("QComboBox")) t=PropertyValue::ComboBox;
    else if(w->inherits("QSpinBox")) t=PropertyValue::SpinBox;
    else if(w->inherits("QAbstractSlider")) t=PropertyValue::Slider;
    else if(w->inherits("QTextEdit")) t=PropertyValue::TextEdit;
    else if(w->inherits("QGroupBox")) t=PropertyValue::GroupBox;
    else if(w->inherits("QDoubleSpinBox")) t=PropertyValue::DoubleSpinBox;
  }
  return t;
}

inline void PropertyWidget::connectWidget(PropertyValue & p)
{
  TRACE;
  switch (p.widgetType()) {
  case PropertyValue::Unknown:
    break;
  case PropertyValue::Button:
    QObject::connect(p.widget(), SIGNAL(toggled(bool)), &p, SLOT(touched()));
    break;
  case PropertyValue::LineEdit:
    QObject::connect(p.widget(), SIGNAL(textChanged(const QString&)), &p, SLOT(touched()));
    break;
  case PropertyValue::ComboBox:
    QObject::connect(p.widget(), SIGNAL(activated(int)), &p, SLOT(touched()));
    break;
  case PropertyValue::SpinBox:
  case PropertyValue::Slider:
    QObject::connect(p.widget(), SIGNAL(valueChanged(int)), &p, SLOT(touched()));
    break;
  case PropertyValue::DoubleSpinBox:
    QObject::connect(p.widget(), SIGNAL(valueChanged(double)), &p, SLOT(touched()));
    break;
  case PropertyValue::TextEdit:
    QObject::connect(p.widget(), SIGNAL(textChanged()), &p, SLOT(touched()));
    break;
  case PropertyValue::GroupBox:
    if(static_cast<QGroupBox *>(p.widget())->isCheckable()) {
      QObject::connect(p.widget(), SIGNAL(toggled(bool)), &p, SLOT(touched()));
    }
    break;
  case PropertyValue::ColorButton:
    QObject::connect(p.widget(), SIGNAL(colorChanged(QColor)), &p, SLOT(touched()));
    break;
  case PropertyValue::Custom0:
  case PropertyValue::Custom1:
  case PropertyValue::Custom2:
  case PropertyValue::Custom3:
  case PropertyValue::Custom4:
  case PropertyValue::Custom5:
  case PropertyValue::Custom6:
  case PropertyValue::Custom7:
  case PropertyValue::Custom8:
  case PropertyValue::Custom9:
    if(!connectCustomWidget(p)) {
      App::log(tr("widgetType %1 unsupported, define connectCustomWidget()\n")
                    .arg(p.widgetType()));
    }
    break;
  }
}

/*!
  If \a pid does not exist, a new value is created
*/
void PropertyWidget::setValue(int pid, QVariant val)
{
  TRACE;
  QHash<int, PropertyValue *>::iterator it=_list.find(pid);
  if(it!=_list.end()) {
    it.value()->setValue(val);
  } else {
    PropertyValue * p=addProperty(pid, nullptr, nullptr);
    p->setValue(val);
  }
}

PropertyValue * PropertyWidget::addProperty(int pid, QWidget * w, QWidget * label)
{
  TRACE;
  PropertyValue * p=new PropertyValue(pid, w, label, determineWidgetType(pid, w, label) );
  _list.insert(pid, p);
  connectWidget(*p);
  connect(p, SIGNAL(touched(PropertyValue *)), this, SLOT(touched(PropertyValue *)));
  return p;
}

void PropertyWidget::setWidgets()
{
  TRACE;
  QHash<int, PropertyValue *>::Iterator it;
  for(it=_list.begin();it!=_list.end();it++ ) {
    PropertyValue & p=**it;
    if(!p.isFrozen()) { // The property is already frozen by someone else, do not set widget.
      p.freeze(true);
      setWidget(p);
      p.freeze(false);
    }
  }
}

void PropertyWidget::reset()
{
  TRACE;
  QHash<int, PropertyValue *>::Iterator it;
  for(it=_list.begin();it!=_list.end();it++ ) (*it)->reset();
}

void PropertyWidget::setWidget(PropertyValue & p)
{
  TRACE;
  switch (p.widgetType()) {
  case PropertyValue::Unknown:
    break;
  case PropertyValue::Button:
    static_cast<QAbstractButton *>(p.widget())->setChecked(p.value().toBool());
    break;
  case PropertyValue::LineEdit: {
      QLineEdit * w=static_cast<QLineEdit * >(p.widget());
      if(p.value().type()==QVariant::Double) { // Avoid the automatic conversion that ignores current Locale
        w->setText(QLocale().toString(p.value().toDouble()));
        w->setCursorPosition(0);
      } else {
        w->setText(p.value().toString());
      }
    }
    break;
  case PropertyValue::ComboBox:
    static_cast<QComboBox *>(p.widget())->setCurrentIndex(p.value().toInt());
    break;
  case PropertyValue::SpinBox:
    static_cast<QSpinBox *>(p.widget())->setValue(p.value().toInt());
    break;
  case PropertyValue::Slider:
    static_cast<QAbstractSlider *>(p.widget())->setValue(p.value().toInt());
    break;
  case PropertyValue::TextEdit:
    static_cast<QTextEdit *>(p.widget())->setPlainText(p.value().toString());
    break;
  case PropertyValue::GroupBox: {
      QGroupBox * w=static_cast<QGroupBox *>(p.widget());
      if(w->isCheckable()) {
        w->setChecked(p.value().toBool());
      }
    }
    break;
  case PropertyValue::DoubleSpinBox:
    static_cast<QDoubleSpinBox *>(p.widget())->setValue(p.value().toDouble());
    break;
  case PropertyValue::ColorButton:
    static_cast<ColorButton *>(p.widget())->setColor(p.value().value<QColor>());
    break;
  case PropertyValue::Custom0:
  case PropertyValue::Custom1:
  case PropertyValue::Custom2:
  case PropertyValue::Custom3:
  case PropertyValue::Custom4:
  case PropertyValue::Custom5:
  case PropertyValue::Custom6:
  case PropertyValue::Custom7:
  case PropertyValue::Custom8:
  case PropertyValue::Custom9:
    if(!setCustomWidget(p)) {
      App::log(tr("widgetType %1 unsupported, define setCustomWidget()\n")
                   .arg(p.widgetType()));
    }
    break;
  }
}

QVariant PropertyWidget::widgetValue(PropertyValue & p)
{
  QVariant val;
  switch (p.widgetType()) {
  case PropertyValue::Unknown:
    break;
  case PropertyValue::Button:
     val=static_cast<QAbstractButton *>(p.widget())->isChecked();
    break;
  case PropertyValue::LineEdit:
    val=static_cast<QLineEdit *>(p.widget())->text();
    break;
  case PropertyValue::ComboBox:
    val=static_cast<QComboBox *>(p.widget())->currentIndex();
    break;
  case PropertyValue::SpinBox:
    val=static_cast<QSpinBox *>(p.widget())->value();
    break;
  case PropertyValue::Slider:
    val=static_cast<QAbstractSlider *>(p.widget())->value();
    break;
  case PropertyValue::TextEdit:
    val=static_cast<QTextEdit *>(p.widget())->toPlainText();
    break;
  case PropertyValue::GroupBox:
    if(static_cast<QGroupBox *>(p.widget())->isCheckable()) {
      val=static_cast<QGroupBox *>(p.widget())->isChecked();
    }
    break;
  case PropertyValue::DoubleSpinBox:
    val=static_cast<QDoubleSpinBox *>(p.widget())->value();
    break;
  case PropertyValue::ColorButton:
    val=static_cast<ColorButton *>(p.widget())->color();
    break;
  case PropertyValue::Custom0:
  case PropertyValue::Custom1:
  case PropertyValue::Custom2:
  case PropertyValue::Custom3:
  case PropertyValue::Custom4:
  case PropertyValue::Custom5:
  case PropertyValue::Custom6:
  case PropertyValue::Custom7:
  case PropertyValue::Custom8:
  case PropertyValue::Custom9:
    val=customWidgetValue(p);
    if(!val.isValid()) {
      App::log(tr("widgetType %1 unsupported, define customWidgetValue()\n")
                   .arg(p.widgetType()));
    }
    break;
  }
  return val;
}

} // namespace QGpGuiTools
