/****************************************************************************
**
** Copyright (C) 2002-2006 Frank Hemer <frank@hemer.org>,
**                         Tilo Riemer <riemer@crossvc.com>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt 
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt 
** CrossVC is released under the terms of the 
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification, 
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
**    different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
**    products derived from this software without specific prior written
**    permission.
** 4. The source code is the creative property of the authors.
**    Extensions and development under the terms of the Gnu Public License
**    are limited to the Unix platform. Any distribution or compilation of 
**    the source code against libraries licensed other than gpl requires 
**    the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** This program is free software; you can redistribute it and/or modify 
** it under the terms of the GNU General Public License as published by 
** the Free Software Foundation, version 2 of the License.
** This program 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 General Public License version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

/**
 * Settings of each project.
 * SubProjects have a relative path, begining with the toplevel project name
 * slashes ('/') are replaced with (':') due to QSettings limitations
 * on win, subkey are limited to 255 chars, so unfortunatelly a relative path
 * can only safely be stored up to this path length
 */

#include "config.h"

#include "ProjectSettings.h"
// #include "globals.h"//needed for debugging

#include <qmap.h>
#include <qregexp.h>

// helps creating fast hash-tables
static const unsigned int nextPrime( unsigned int i) {
  switch( i) {
     case 0:
     case 2:
     case 3:
     case 5:
     case 7:
     case 11:
     case 13:
     case 17: return 19;
     case 19: return 41;
     case 41: return 79;
     case 79: return 157;
     case 157: return 311;
     case 311: return 619;
     case 619: return 1249;
     case 1249: return 2503;
     case 2503: return 4999;
     case 4999: return 9973;
     default: return i*2+1;
  }
}

const char ProjectSettings::SUBPROJECTS[] = "SubProjects";

ProjectSettings::ProjectSettings() {

  projectMap.setAutoDelete(true);

}

ProjectSettings::~ProjectSettings() {

}

//fails if newProjectKey exists or oldProjectKey not found
bool ProjectSettings::renameProject( QString oldProjectKey, QString newProjectKey) {

  oldProjectKey.replace(QRegExp("\\^"),"^0");
  newProjectKey.replace(QRegExp("\\^"),"^0");
  oldProjectKey.replace(QRegExp("/"),"^e");
  newProjectKey.replace(QRegExp("/"),"^e");
  if ( !(projectMap.find( newProjectKey))) {//null if not found

    if ( projectMap.isEmpty() || (projectMap.size() <= projectMap.count()) ) projectMap.resize( nextPrime(projectMap.size()));

    SettingsMap * map = projectMap.find(oldProjectKey);
    if (!map) return false;

    map = new SettingsMap(*map);
    projectMap.remove(oldProjectKey);
    projectMap.insert( newProjectKey, map);
    return true;

  } else {
    return false;
  }
}

bool ProjectSettings::removeProject( QString project) {

  project.replace(QRegExp("\\^"),"^0");
  project.replace(QRegExp("/"),"^e");
  SettingsMap* settings = projectMap.find(project);
  if (settings) {

    SettingsMap::iterator it = settings->find( SUBPROJECTS);
    if (it != settings->end()) {

      const QStringList &list = QStringList::split("^e",it.data());

      for (QStringList::ConstIterator lit = list.begin(); lit != list.end(); ++lit) {
	QString entry = *lit;
	entry.replace(QRegExp("/"),"^e");
	projectMap.remove(entry);
      }

    }
  }
  return projectMap.remove(project);

}

//removes from subprojectlist and removes project itself
bool ProjectSettings::removeSubProject( QString project, QString subProject) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( SUBPROJECTS);
  if (it != settings->end()) {

    QStringList list = QStringList::split("^e",it.data());
    QString tmp = subProject;
    tmp.replace(QRegExp("\\^"),"^0");
    QStringList::Iterator lit = list.find(tmp);
    if (lit != list.end()) {

      list.remove(lit);
      removeProject(subProject);
      settings->insert(SUBPROJECTS,list.join("^e"));
      return true;
    }
  }
  return false;
}

void ProjectSettings::addSubProject( QString project, QString subProject) {

  SettingsMap* settings = getAndCreateProjectSettings(project);
  SettingsMap::iterator it = settings->find( SUBPROJECTS);
  QString tmp;
  if (it != settings->end()) {
    
    tmp = it.data()+"^e";
  }
  tmp += subProject.replace(QRegExp("\\^"),"^0");
  settings->insert(SUBPROJECTS,tmp);

}

void ProjectSettings::setSubProjects( QString project, QStringList subProjects) {
  set(project,SUBPROJECTS,subProjects);
}

ProjectSettings::SettingsMap* ProjectSettings::getAndCreateProjectSettings( QString projectKey) {

  projectKey.replace(QRegExp("\\^"),"^0");
  projectKey.replace(QRegExp("/"),"^e");
  SettingsMap* map = NULL;
  if ( projectMap.isEmpty() || !(map = projectMap.find( projectKey))) {//null if not found

    map = new SettingsMap();
    if (projectMap.size() <= projectMap.count()) projectMap.resize( nextPrime(projectMap.size()));
    projectMap.insert( projectKey, map);

  }
  return map;

}

ProjectSettings::SettingsMap* ProjectSettings::getProjectSettings( QString projectKey) {

  projectKey.replace(QRegExp("\\^"),"^0");
  projectKey.replace(QRegExp("/"),"^e");
  if (projectKey.isNull()) return NULL;
  SettingsMap* map = projectMap.find( projectKey);//null if not found
  return map;

}

void ProjectSettings::set( const QString project, const QString key, bool val) {

  QString value;
  if (val) {

    value = "true";

  } else {

    value = "false";

  }
  getAndCreateProjectSettings(project)->insert(key,value);
}

void ProjectSettings::set( const QString project, const QString key, int val) {

  getAndCreateProjectSettings(project)->insert(key,QString::number(val));
}

void ProjectSettings::set( const QString project, const QString key, QString val) {

  getAndCreateProjectSettings(project)->insert(key,val);
}
void ProjectSettings::set( const QString project, const QString key, QStringList val) {
  QStringList::Iterator it;
  for (it = val.begin(); it != val.end(); ++it) {
    (*it).replace(QRegExp("\\^"),"^0");
  }
  getAndCreateProjectSettings(project)->insert(key,val.join("^e"));  
}

void ProjectSettings::set( const QString project, const QString key, StringMap map) {
  QString val = "";
  StringMap::Iterator it;
  for ( it = map.begin(); it != map.end(); ++it ) {
    QString tmp = it.key();
    tmp.replace(QRegExp("\\^"),"^0");
    val += tmp+"^e"+it.data().replace(QRegExp("\\^"),"^0")+"^e";
  }
  getAndCreateProjectSettings(project)->insert(key,val);
}

void ProjectSettings::set( const QString project, const QString key, IntMap map) {
  QString val = "";
  IntMap::Iterator it;
  for ( it = map.begin(); it != map.end(); ++it ) {
    QString tmp = it.key();
    tmp.replace(QRegExp("\\^"),"^0");
    val += tmp+"^e"+QString::number(it.data())+"^e";
  }
  getAndCreateProjectSettings(project)->insert(key,val);
}

bool ProjectSettings::getProjects( QStringList& val) {

  if (projectMap.isEmpty()) {
    return false;
  }
  val.clear();
  QDictIterator<SettingsMap> it(projectMap);
  for( ; it.current(); ++it ) {
    QString key = it.currentKey();
    key.replace(QRegExp("\\^e"),"/");
    key.replace(QRegExp("\\^0"),"^");
    val.append(key);
  }
  return true;
}

bool ProjectSettings::getTopLevelProjects( QStringList& val) {

  if (projectMap.isEmpty()) {
    return false;
  }
  val.clear();
  QDictIterator<SettingsMap> it(projectMap);
  for( ; it.current(); ++it ) {
    if (it.currentKey().find("^e") == -1) {
      QString key = it.currentKey();
      key.replace(QRegExp("\\^0"),"^");
      val.append(key);
    }
  }
  return true;
}

bool ProjectSettings::getSubProjects( const QString& project, QStringList& subProjectList) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( SUBPROJECTS);
  if (it == settings->end()) {

    return false;

  } else {

    subProjectList = QStringList::split("^e",it.data());
    QStringList::Iterator it;
    for (it = subProjectList.begin(); it != subProjectList.end(); ++it) {
      (*it).replace(QRegExp("\\^0"),"^");
    }
    return true;

  }

}

bool ProjectSettings::get( const QString project, const QString key, bool& val) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( key);
  if (it == settings->end()) {

    return false;

  } else {

    if ( it.data()=="true") {

      val = true;

    } else {

      val = false;

    }
    return true;
  }
}

bool ProjectSettings::get( const QString project, const QString key, int& val) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( key);
  if (it == settings->end()) {

    return false;

  } else {

    bool ok;
    val = it.data().toInt(&ok);
    if (ok) return true;
    else return false;

  }
}

bool ProjectSettings::get( const QString project, const QString key, QString& val) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( key);
  if (it == settings->end()) {

    return false;

  } else {

    val = it.data();
    return true;

  }
}

bool ProjectSettings::get( const QString project, const QString key, QStringList& val) {

  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator it = settings->find( key);
  if (it == settings->end()) {

    return false;

  } else {

    val = QStringList::split("^e",it.data());
    QStringList::Iterator it;
    for (it = val.begin(); it != val.end(); ++it) {
      (*it).replace(QRegExp("\\^0"),"^");
    }
    return true;

  }
}

bool ProjectSettings::get( const QString project, const QString key, StringMap& val) {
  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator iterator = settings->find( key);
  if (iterator == settings->end()) {

    return false;

  } else {

    QStringList list = QStringList::split("^e",iterator.data());
    QStringList::Iterator it;
    it = list.begin();
    while (it != list.end()) {
      QString key = *(it++);
      key.replace(QRegExp("\\^0"),"^");
      QString value = *(it++);
      value.replace(QRegExp("\\^0"),"^");
      val.insert(key,value);
    }
    return true;

  }
}

bool ProjectSettings::get( const QString project, const QString key, IntMap& val) {
  SettingsMap* settings = getProjectSettings(project);
  if (!settings) return false;
  SettingsMap::iterator iterator = settings->find( key);
  if (iterator == settings->end()) {

    return false;

  } else {

    QStringList list = QStringList::split("^e",iterator.data());
    QStringList::Iterator it;
    it = list.begin();
    while (it != list.end()) {
      QString key = *(it++);
      key.replace(QRegExp("\\^0"),"^");
      QString value = *(it++);
      val.insert(key,value.toInt() );
    }
    return true;

  }
}

void ProjectSettings::writeToSettings( QSettings* settings, const QString projectPath) {

  QDictIterator<SettingsMap> it(projectMap);
  for( ; it.current(); ++it ) {

    QString key = projectPath+"/"+it.currentKey()+"/";
    SettingsMap *tmp = projectMap.find( it.currentKey());

    SettingsMap::iterator it;
    for ( it = tmp->begin(); it != tmp->end(); ++it ) {

      QString subKey = key+it.key();
      settings->writeEntry( subKey, it.data()); 

    }

  }
}

void ProjectSettings::readFromSettings( QSettings* settings, const QString projectPath) {

  unsigned int i = 0;
  QStringList projectList = settings->subkeyList( projectPath);
  for( i = 0; i < projectList.count(); i++) {

    QString subKey = projectPath+"/"+projectList[i];
    QStringList entryList = settings->entryList( subKey);
    unsigned int j = 0;
    for( j = 0; j < entryList.count(); j++) {
      QString entry = settings->readEntry( subKey+"/"+entryList[j], "");

      SettingsMap* map = NULL;
      if ( projectMap.isEmpty() || !(map = projectMap.find( projectList[i]))) {//null if not found
	map = new SettingsMap();
	if (projectMap.size() <= projectMap.count()) projectMap.resize( nextPrime(projectMap.size()));
	projectMap.insert( projectList[i], map);
      }
      map->insert(entryList[j],entry);
    }

  }

}

void ProjectSettings::test() {//debug write ProjectSettings contents to konsole

  qDebug("\n*************************** ProjectSettings contents:");
  QDictIterator<SettingsMap> it(projectMap);
  for( ; it.current(); ++it ) {
    QString pName = it.currentKey();
    pName.replace(QRegExp("\\^e"),"/");
    pName.replace(QRegExp("\\^0"),"^");
    qDebug("\nproject: "+pName);
    SettingsMap *tmp = projectMap.find(it.currentKey());
    SettingsMap::iterator it;
    for ( it = tmp->begin(); it != tmp->end(); ++it ) {
      qDebug(it.key()+" : "+it.data());
    }

  }
  qDebug("&&&&&&&&&&&&&&&&&&&&&&&&&&&done\n");
}

