/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "kautoconfig.h"

#include <kglobal.h>
#include <tqsqlpropertymap.h>
#include <tqobjectlist.h>
#include <kconfig.h>
#include <kapplication.h>
#include <kdeversion.h>

/**
 * Macro function to warn developers when they are making calls
 * that can never return anything of value
 */ 
#ifndef NDEBUG
#include "kdebug.h"
#define functionCallPreOrderCheck(functionName, returnValue) \
  if(!d->retrievedSettings){ \
      kdDebug(180) << "KAutoConfig::"functionName"() was called before " \
      "KAutoConfig::retrieveSettings().  This should NEVER happen because " \
      "it will do nothing.  Please Fix." << endl; \
    return returnValue; \
  }

#define functionCallPostOrderCheck(functionName, returnValue) \
  if(d->retrievedSettings){ \
      kdDebug(180) << "KAutoConfig::"functionName"() was called after " \
      "KAutoConfig::retrieveSettings().  This should NEVER happen because " \
      "it will do nothing.  Please Fix." << endl; \
    return returnValue; \
  }
#else
#define functionCallPostOrderCheck(functionName, returnValue)
#define functionCallPreOrderCheck(functionName, returnValue)
#endif

class KAutoConfig::KAutoConfigPrivate {

public:
  KAutoConfigPrivate() : changed(false)
#ifndef NDEBUG
    , retrievedSettings(false)
#endif
  { init(); }

  // Widgets to parse
  TQPtrList<TQWidget> widgets;
  // Name of the group that KConfig should be set to for each widget.
  TQMap<TQWidget*, TQString> groups;

  // Child widgets of widgets to ignore
  TQPtrList<TQWidget> ignore;

  // Reset to false after saveSettings returns true.
  bool changed;

#ifndef NDEBUG
  // Many functions require this to be true to be of any value.
  bool retrievedSettings;
#endif

  // Known widgets that can be configured
  TQMap<TQWidget*, TQPtrList<TQWidget> > autoWidgets;
  // Default values for the widgets.
  TQMap<TQWidget*, TQVariant> defaultValues;
  // Widgets to not get properties on (TQLabel etc)
  TQAsciiDict<int> ignoreTheseWidgets;

  void init(){
    ignoreTheseWidgets.insert("TQLabel", new int(1));
    ignoreTheseWidgets.insert("TQFrame", new int(2));
    ignoreTheseWidgets.insert("TQGroupBox", new int(3));
    ignoreTheseWidgets.insert("TQButtonGroup", new int(4));
    ignoreTheseWidgets.insert("TQWidget", new int(5));
    ignoreTheseWidgets.setAutoDelete(true);

    static bool defaultKDEPropertyMapInstalled = false;
    if ( !defaultKDEPropertyMapInstalled && kapp ) {
      kapp->installKDEPropertyMap();
      defaultKDEPropertyMapInstalled = true;
    }
  }
};

KAutoConfig::KAutoConfig(KConfig *kconfig, TQObject *parent,
    const char *name) : TQObject(parent, name), config(kconfig) {
  d = new KAutoConfigPrivate();
}

KAutoConfig::KAutoConfig(TQObject *parent, const char *name) :
    TQObject(parent, name), config(KGlobal::config()) {
  d = new KAutoConfigPrivate();
}

KAutoConfig::~KAutoConfig(){
  delete d;
}

void KAutoConfig::addWidget(TQWidget *widget, const TQString &group){
  functionCallPostOrderCheck("addWidget",);
  d->groups.insert(widget, group);
  d->widgets.append(widget);
  TQPtrList<TQWidget> newAutoConfigWidget;
  d->autoWidgets.insert(widget, newAutoConfigWidget );
}

void KAutoConfig::ignoreSubWidget(TQWidget *widget){
  functionCallPostOrderCheck("ignoreSubWidget",);
  d->ignore.append(widget);
}

bool KAutoConfig::retrieveSettings(bool trackChanges){
#ifndef NDEBUG
  if(d->retrievedSettings){
      kdDebug(180) << "This should not happen.  Function "
       "KAutoConfig::retrieveSettings() was called more then once, returning "
       "false.  Please fix." << endl;
    return false;
  }
  d->retrievedSettings = true;
#endif
  
  if(trackChanges){
    // QT
    changedMap.insert(TQString::fromLatin1("TQButton"), TQT_SIGNAL(stateChanged(int)));
    changedMap.insert(TQString::fromLatin1("TQCheckBox"), TQT_SIGNAL(stateChanged(int)));
    changedMap.insert(TQString::fromLatin1("TQPushButton"), TQT_SIGNAL(stateChanged(int)));
    changedMap.insert(TQString::fromLatin1("TQRadioButton"), TQT_SIGNAL(stateChanged(int)));
    changedMap.insert(TQString::fromLatin1("TQComboBox"), TQT_SIGNAL(activated (int)));
    //qsqlproperty map doesn't store the text, but the value!
    //changedMap.insert(TQString::fromLatin1("TQComboBox"), TQT_SIGNAL(textChanged(const TQString &)));
    changedMap.insert(TQString::fromLatin1("QDateEdit"), TQT_SIGNAL(valueChanged(const TQDate &)));
    changedMap.insert(TQString::fromLatin1("QDateTimeEdit"), TQT_SIGNAL(valueChanged(const TQDateTime &)));
    changedMap.insert(TQString::fromLatin1("TQDial"), TQT_SIGNAL(valueChanged (int)));
    changedMap.insert(TQString::fromLatin1("TQLineEdit"), TQT_SIGNAL(textChanged(const TQString &)));
    changedMap.insert(TQString::fromLatin1("TQSlider"), TQT_SIGNAL(valueChanged(int)));
    changedMap.insert(TQString::fromLatin1("TQSpinBox"), TQT_SIGNAL(valueChanged(int)));
    changedMap.insert(TQString::fromLatin1("QTimeEdit"), TQT_SIGNAL(valueChanged(const TQTime &)));
    changedMap.insert(TQString::fromLatin1("TQTextEdit"), TQT_SIGNAL(textChanged()));
    changedMap.insert(TQString::fromLatin1("TQTextBrowser"), TQT_SIGNAL(sourceChanged(const TQString &)));
    changedMap.insert(TQString::fromLatin1("TQMultiLineEdit"), TQT_SIGNAL(textChanged()));
    changedMap.insert(TQString::fromLatin1("TQListBox"), TQT_SIGNAL(selectionChanged()));
    changedMap.insert(TQString::fromLatin1("TQTabWidget"), TQT_SIGNAL(currentChanged(TQWidget *)));

    // KDE
    changedMap.insert( TQString::fromLatin1("KComboBox"), TQT_SIGNAL(activated (int)));
    changedMap.insert( TQString::fromLatin1("KFontCombo"), TQT_SIGNAL(activated (int)));
    changedMap.insert( TQString::fromLatin1("KFontRequester"), TQT_SIGNAL(fontSelected(const TQFont &)));
    changedMap.insert( TQString::fromLatin1("KFontChooser"),  TQT_SIGNAL(fontSelected(const TQFont &)));
    changedMap.insert( TQString::fromLatin1("KHistoryCombo"), TQT_SIGNAL(activated (int)));

    changedMap.insert( TQString::fromLatin1("KColorButton"), TQT_SIGNAL(changed(const TQColor &)));
    changedMap.insert( TQString::fromLatin1("KDatePicker"), TQT_SIGNAL(dateSelected (TQDate)));
    changedMap.insert( TQString::fromLatin1("KEditListBox"), TQT_SIGNAL(changed()));
    changedMap.insert( TQString::fromLatin1("KListBox"), TQT_SIGNAL(selectionChanged()));
    changedMap.insert( TQString::fromLatin1("KLineEdit"), TQT_SIGNAL(textChanged(const TQString &)));
    changedMap.insert( TQString::fromLatin1("KPasswordEdit"), TQT_SIGNAL(textChanged(const TQString &)));
    changedMap.insert( TQString::fromLatin1("KRestrictedLine"), TQT_SIGNAL(textChanged(const TQString &)));
    changedMap.insert( TQString::fromLatin1("KTextBrowser"), TQT_SIGNAL(sourceChanged(const TQString &)));
    changedMap.insert( TQString::fromLatin1("KTextEdit"), TQT_SIGNAL(textChanged()));
    changedMap.insert( TQString::fromLatin1("KURLRequester"),  TQT_SIGNAL(textChanged (const TQString& )));
    changedMap.insert( TQString::fromLatin1("KIntNumInput"), TQT_SIGNAL(valueChanged (int)));
    changedMap.insert( TQString::fromLatin1("KIntSpinBox"), TQT_SIGNAL(valueChanged (int)));
    changedMap.insert( TQString::fromLatin1("KDoubleNumInput"), TQT_SIGNAL(valueChanged (double)));
  }

  // Go through all of the children of the widgets and find all known widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *widget;
  bool usingDefaultValues = false;
  while ( (widget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[widget]);
    usingDefaultValues |= parseChildren(widget, d->autoWidgets[widget], trackChanges);
  }
  return usingDefaultValues;
}

bool KAutoConfig::saveSettings() {
  functionCallPreOrderCheck("saveSettings", false);

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  // Go through all of the widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *widget;
  while ( (widget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[widget]);

    // Go through the known autowidgets of this widget and save
    TQPtrListIterator<TQWidget> it( d->autoWidgets[widget] );
    TQWidget *groupWidget;
    bool widgetChanged = false;
    while ( (groupWidget = it.current()) != 0 ){
      ++it;
      TQVariant defaultValue = d->defaultValues[groupWidget];
      TQVariant currentValue = propertyMap->property(groupWidget);
#if KDE_IS_VERSION( 3, 1, 90 )
      if(!config->hasDefault(TQString::fromLatin1(groupWidget->name())) && currentValue == defaultValue){
        config->revertToDefault(TQString::fromLatin1(groupWidget->name()));
        widgetChanged = true;
      }
      else{
#endif
        TQVariant savedValue = config->readPropertyEntry(groupWidget->name(),
                                                             defaultValue);
        if(savedValue != currentValue){
          config->writeEntry(groupWidget->name(), currentValue);
          widgetChanged = true;
        }
#if KDE_IS_VERSION( 3, 1, 90 )
      }
#endif
    }
    d->changed |= widgetChanged;
    if(widgetChanged)
      emit( settingsChanged(widget) );
  }

  if(d->changed){
    emit( settingsChanged() );
    d->changed = false;
    config->sync();
    return true;
  }
  return false;
}

bool KAutoConfig::hasChanged() const {
  functionCallPreOrderCheck("hasChanged", false);

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  // Go through all of the widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *widget;
  while ( (widget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[widget]);
    // Go through the known autowidgets of this widget and save
    TQPtrListIterator<TQWidget> it( d->autoWidgets[widget] );
    TQWidget *groupWidget;
    while ( (groupWidget = it.current()) != 0 ){
      ++it;
      TQVariant defaultValue = d->defaultValues[groupWidget];
      TQVariant currentValue = propertyMap->property(groupWidget);
      TQVariant savedValue = config->readPropertyEntry(groupWidget->name(),
      defaultValue);

      // Return once just one item is found to have changed.
      if((currentValue == defaultValue && savedValue != currentValue) ||
         (savedValue != currentValue))
        return true;
    }
  }
  return false;
}

bool KAutoConfig::isDefault() const {
  functionCallPreOrderCheck("isDefault", false);

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  // Go through all of the widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *widget;
  while ( (widget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[widget]);
    // Go through the known autowidgets of this widget and save
    TQPtrListIterator<TQWidget> it( d->autoWidgets[widget] );
    TQWidget *groupWidget;
    while ( (groupWidget = it.current()) != 0 ){
      ++it;
      TQVariant defaultValue = d->defaultValues[groupWidget];
      TQVariant currentValue = propertyMap->property(groupWidget);
      if(currentValue != defaultValue){
        //qDebug("groupWidget %s, has changed: default: %s new: %s", groupWidget->name(), defaultValue.toString().latin1(), currentValue.toString().latin1());
        return false;
      }
    }
  }
  return true;
}

void KAutoConfig::resetSettings() const {
  functionCallPreOrderCheck("resetSettings",);

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  // Go through all of the widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *widget;
  while ( (widget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[widget]);

    // Go through the known autowidgets of this widget and save
    TQPtrListIterator<TQWidget> it( d->autoWidgets[widget] );
    TQWidget *groupWidget;
    while ( (groupWidget = it.current()) != 0 ){
      ++it;
      TQVariant defaultValue = d->defaultValues[groupWidget];
      if(defaultValue != propertyMap->property(groupWidget)){
        propertyMap->setProperty(groupWidget, defaultValue);
        d->changed = true;
      }
    }
  }
}

void KAutoConfig::reloadSettings() const {
  functionCallPreOrderCheck("reloadSettings", );

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  // Go through all of the widgets
  TQPtrListIterator<TQWidget> it( d->widgets );
  TQWidget *pageWidget;
  while ( (pageWidget = it.current()) != 0 ) {
    ++it;
    config->setGroup(d->groups[pageWidget]);

    // Go through the known widgets of this page and reload
    TQPtrListIterator<TQWidget> it( d->autoWidgets[pageWidget] );
    TQWidget *widget;
    while ( (widget = it.current()) != 0 ){
      ++it;
      TQVariant defaultSetting = d->defaultValues[widget];
      TQVariant setting = 
        config->readPropertyEntry(widget->name(), defaultSetting);
      propertyMap->setProperty(widget, setting);
    }
  }
  d->changed = false;
}

bool KAutoConfig::parseChildren(const TQWidget *widget,
    TQPtrList<TQWidget>& currentGroup, bool trackChanges){
  bool valueChanged = false;
  const TQPtrList<TQObject> *listOfChildren = widget->children();
  if(!listOfChildren)
    return valueChanged;

  TQSqlPropertyMap *propertyMap = TQSqlPropertyMap::defaultMap();
  TQPtrListIterator<TQObject> it( *listOfChildren );
  TQObject *object;
  while ( (object = it.current()) != 0 )
  {
    ++it;
    if(!object->isWidgetType()){
      continue;
    }
    TQWidget *childWidget = (TQWidget *)object;
    if(d->ignore.containsRef(childWidget)){
      continue;
    }

    bool parseTheChildren = true;
#ifndef NDEBUG
    if(d->ignoreTheseWidgets[childWidget->className()] == 0 &&
		    childWidget->name(0) == NULL){
      // Without a name the widget is just skipped over.
      kdDebug(180) << "KAutoConfig::retrieveSettings, widget with "
        "NULL name.  className: " << childWidget->className() << endl;
    }
#endif
    
    
    if( d->ignoreTheseWidgets[childWidget->className()] == 0 &&  
      childWidget->name(0) != NULL )
    {
      TQVariant defaultSetting = propertyMap->property(childWidget);
      if(defaultSetting.isValid())
      {
        parseTheChildren = false;
        // Disable the widget if it is immutable?
        if(config->entryIsImmutable( TQString::fromLatin1(childWidget->name())))
          childWidget->setEnabled(false);
        else
        {
          // FOR THOSE WHO ARE LOOKING
          // Here is the code were the widget is actually marked to watch.
          //qDebug("KAutoConfig: Adding widget(%s)",childWidget->name()); 
          currentGroup.append(childWidget);
          d->defaultValues.insert(childWidget, defaultSetting);
        }
        // Get/Set settings and connect up the changed signal
        TQVariant setting =
         config->readPropertyEntry(childWidget->name(), defaultSetting);
        if(setting != defaultSetting)
        {
          propertyMap->setProperty(childWidget, setting);
          valueChanged = true;
        }
        if(trackChanges && changedMap.find(TQString::fromLatin1(childWidget->className())) !=
            changedMap.end())
        {
          connect(childWidget, changedMap[TQString::fromLatin1(childWidget->className())],
                  this, TQT_SIGNAL(widgetModified()));
        }
#ifndef NDEBUG
        else if(trackChanges &&
          changedMap.find(TQString::fromLatin1(childWidget->className())) == changedMap.end())
        {
          // Without a signal kautoconfigdialog could incorectly 
	  // enable/disable the buttons
	  kdDebug(180) << "KAutoConfig::retrieveSettings, Unknown changed "
           "signal for widget:" << childWidget->className() << endl;
        }
#endif

      }
#ifndef NDEBUG
      else
      { 
        // If kautoconfig doesn't know how to get/set the widget's value 
	// nothing can be done to it and it is skipped. 
	kdDebug(180) << "KAutoConfig::retrieveSettings, Unknown widget:" 
          << childWidget->className() << endl;
      }
#endif
    }
    if(parseTheChildren)
    {
      // this widget is not known as something we can store.
      // Maybe we can store one of its children.
      valueChanged |= parseChildren(childWidget, currentGroup, trackChanges);
    }
  }
  return valueChanged;
}

#include "kautoconfig.moc"