/***************************************************************************
**
**  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-03-31
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "PropertyEditor.h"
#include "PropertyProxy.h"
#include "PropertyWidget.h"
#include "PropertyArea.h"
#include "ExpandTabWidget.h"
#include "Settings.h"
#include "PropertyCategorySettings.h"

namespace QGpGuiTools {

PropertyEditor * PropertyEditor::_instance=0;

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
PropertyEditor::PropertyEditor(QWidget * parent)
    : QWidget(parent)
{
  ASSERT(!_instance);
  resize(400, 600);
  _instance=this;
  Application::addGlobalObject(this);
  _proxy=nullptr;
  setWhatsThis(tr("<p>When you select more than one object at a time, "
                  "there may be some properties marked in red, meaning that properties differ between "
                  "widgets. <b>Touching red properties will uniformize them across all objects.</b></p>"));
  QVBoxLayout * vboxLayout=new QVBoxLayout(this);
  _tab=new QTabWidget(this);
  _tab->setTabPosition(QTabWidget::West);
  connect(_tab, SIGNAL(currentChanged(int)), this, SLOT(currentCategoryChanged(int)));
  vboxLayout->addWidget(_tab);
  // Restore category settings to registry
  QSettings& reg=CoreApplication::instance()->settings();
  reg.beginGroup("PropertyEditor");
  QStringList categoryKeys=reg.childGroups();
  for(QStringList::iterator it=categoryKeys.begin(); it!=categoryKeys.end();it++) {
    reg.beginGroup(*it);
    QStringList tabKeys=reg.value( "keys" ).toStringList();
    QList<QVariant> tabStates=reg.value( "states" ).toList();
    QStringList tabScrollPositions=reg.value( "scrollPositions" ).toStringList();
    time_t t=reg.value( "clickTime" ).toInt();
    if(tabKeys.count()>0 &&
        tabKeys.count()==tabStates.count() &&
        tabKeys.count()==tabScrollPositions.count()) {
      PropertyCategorySettings * s=new PropertyCategorySettings;
      s->set(tabKeys, tabStates, tabScrollPositions);
      s->setClickTime(t);
      _categorySettings.insert(  *it, s);
    }
    reg.endGroup();
  }
  Settings::getRect(this, "PropertyEditor" );
}

/*!
  Description of destructor still missing
*/
PropertyEditor::~PropertyEditor()
{
  QMap<QString, PropertyCategorySettings *>::iterator it;
  // Save category settings to registry
  QSettings& reg=CoreApplication::instance()->settings();
  reg.beginGroup("PropertyEditor");
  for(it=_categorySettings.begin(); it!=_categorySettings.end();it++) {
    reg.beginGroup(it.key());
    reg.setValue( "keys", it.value()->keys());
    reg.setValue( "states", it.value()->states());
    reg.setValue( "scrollPositions", it.value()->scrollPositions());
    reg.setValue( "clickTime", (int)it.value()->clickTime());
    reg.endGroup();
  }
  // Delete settings
  for(it=_categorySettings.begin(); it!=_categorySettings.end();it++) {
    delete it.value();
  }
  _instance=0;
}

void PropertyEditor::resizeEvent(QResizeEvent * )
{
  Settings::setSize(this, "propertyEditor" );
}

void PropertyEditor::moveEvent(QMoveEvent * )
{
  Settings::setRect(this, "propertyEditor" );
}

void PropertyEditor::setProxy(PropertyProxy * proxy)
{
  if(_proxy) {
    if(_proxy==proxy) {
      return;
    }
    // Save current state of categories
    int n=_tab->count();
    for(int i=0; i<n; i++) {
      saveState(i);
    }
    takeWidgets();
  }
  _proxy=proxy;
  if(_proxy) {
    setWindowTitle(tr("Property editor::%1").arg(_proxy->title()));
    _proxy->setEditor();
  }
}

/*!
  Remove all categories, delete ExpandTabWidget but do not delete individual widgets
*/
void PropertyEditor::takeWidgets()
{
  _tab->blockSignals(true);   // currentCategoryChanged called only after user click
  int n=_tab->count();
  for(int i=0; i<n;i++) {
    PropertyArea * pa=static_cast<PropertyArea *>(_tab->widget(0));
    static_cast<ExpandTabWidget *>(pa->widget())->takeWidgets();
    _tab->removeTab(0);
    delete pa;
  }
  _categories.clear();
  _propertyAreas.clear();
  _tab->blockSignals(false);
}

/*!
  Records time of last user selection of category
*/
void PropertyEditor::currentCategoryChanged(int index)
{
  QMap<QString, PropertyCategorySettings *>::iterator it;
  QString categoryName=_tab->tabText(index);
  it=_categorySettings.find(categoryName);
  PropertyCategorySettings * s;
  if(it!=_categorySettings.end()) {
    s=it.value();
    s->setClickTime();
  } else {
    s=new PropertyCategorySettings;
    s->setClickTime();
    _categorySettings.insert(categoryName, s);
  }
}

void PropertyEditor::addCategory(QIcon, const QString& caption, ExpandTabWidget * w)
{
  PropertyArea * pa=new PropertyArea(this);
  w->installEventFilter(this);
  pa->setWidget(w);
  _tab->blockSignals(true);   // currentCategoryChanged called only after user click
  _tab->addTab(pa, caption);
  _tab->blockSignals(false);
  _categories.insert(caption, pa);
  _propertyAreas.insert(w, pa);
  // Current category restore
  QMap<QString, PropertyCategorySettings *>::iterator it;
  time_t currentCategoryTime=0;
  if(_tab->count()>0) {
    QString categoryName=_tab->tabText(_tab->currentIndex());
    it=_categorySettings.find(categoryName);
    if(it!=_categorySettings.end()) {
      currentCategoryTime=it.value()->clickTime();
    }
  }
  it=_categorySettings.find(caption);
  if(it!=_categorySettings.end()) {
    if(currentCategoryTime<it.value()->clickTime()) {
      _tab->blockSignals(true);   // currentCategoryChanged called only after user click
      _tab->setCurrentIndex(_tab->count()-1);
      _tab->blockSignals(false);
    }
  }
}

void PropertyEditor::removeCategory(const QString& category)
{
  QMap<QString, QWidget *>::iterator it=_categories.find(category);
  if(it!=_categories.end()) {
    int i=_tab->indexOf(it.value());
    _tab->blockSignals(true);   // currentCategoryChanged called only after user click
    _tab->removeTab(i);
    _tab->blockSignals(false);
    PropertyArea * pa=static_cast<PropertyArea *>(it.value());
    _propertyAreas.remove(static_cast<ExpandTabWidget *>(pa->widget()));
    delete pa;
    _categories.erase(it);
    if(_tab->count()==0) hide();
  }
}

void PropertyEditor::addTab(const QString& category, const QString& title, PropertyWidget * w)
{
  QMap<QString, QWidget *>::iterator it=_categories.find(category);
  if(it!=_categories.end()) {
    PropertyArea * pa=static_cast<PropertyArea *>(it.value());
    static_cast<ExpandTabWidget *>(pa->widget())->addWidget(title, w);
  }
}

ExpandTabWidget * PropertyEditor::widget(const QString& category)
{
  QMap<QString, QWidget *>::iterator it=_categories.find(category);
  if(it!=_categories.end()) {
    PropertyArea * pa=static_cast<PropertyArea *>(it.value());
    return static_cast<ExpandTabWidget *>(pa->widget());
  } else {
    return 0;
  }
}

void PropertyEditor::setCurrentTab(const QString &category)
{
  QMap<QString, QWidget *>::iterator it=_categories.find(category);
  if(it!=_categories.end()) {
    _tab->setCurrentWidget(it.value());
  }
}

void PropertyEditor::saveStates()
{
  int n=_tab->count();
  for(int i=0; i<n;i++) {
    saveState(i);
  }
}

void PropertyEditor::restoreStates()
{
  int n=_tab->count();
  for(int i=0; i<n;i++) {
    restoreState(i);
  }
}

void PropertyEditor::restoreState(int index)
{
  QMap<QString, PropertyCategorySettings *>::iterator it;
  it=_categorySettings.find(_tab->tabText(index));
  if(it!=_categorySettings.end()) {
    // Tab state restore
    PropertyArea * pa=static_cast<PropertyArea *>(_tab->widget(index));
    ExpandTabWidget * w=static_cast<ExpandTabWidget *>(pa->widget());
    uint k=w->key();
    w->restoreState(it.value()->state(k));
    pa->resizeContent();  // Make sure scroll bar maximum is correctly computed
    pa->verticalScrollBar()->setValue(it.value()->scrollPosition(k));
  }
}

void PropertyEditor::saveState(int index)
{
  QMap<QString, PropertyCategorySettings *>::iterator it;
  QString categoryName=_tab->tabText(index);
  it=_categorySettings.find(categoryName);
  PropertyArea * pa=static_cast<PropertyArea *>(_tab->widget(index));
  ExpandTabWidget * w=static_cast<ExpandTabWidget *>(pa->widget());
  uint k=w->key();
  if(it!=_categorySettings.end()) {
    it.value()->setState(k, w->saveState());
    it.value()->setScrollPosition(k, pa->verticalScrollBar()->value());
  } else {
    PropertyCategorySettings * s=new PropertyCategorySettings;
    s->setState(k, w->saveState());
    s->setScrollPosition(k, pa->verticalScrollBar()->value());
    _categorySettings.insert(categoryName, s);
  }
}

bool PropertyEditor::eventFilter (QObject * watched, QEvent * event)
{
  ExpandTabWidget * w=qobject_cast<ExpandTabWidget *>(watched);
  if(w) {
    if(event->type()==QEvent::LayoutRequest) {
      PropertyArea * pa=_propertyAreas[w];
      pa->resizeContent();
    }
  }
  return false;
}

} // namespace QGpGuiTools
