/***************************************************************************
 *   Copyright (C) 2001-2002 by Bernd Gehrmann                             *
 *   bernd@kdevelop.org                                                    *
 *   default support: Eray Ozkural (exa)                                   *
 *   additions: John Firebaugh <jfirebaugh@kde.org>                        *
 *              Jakob Simon-Gaarde <jakob@simon-gaarde.dk>                 *
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "domutil.h"

#include <kdebug.h>
#include <tqstringlist.h>
#include <tqfile.h>


void DomUtil::makeEmpty( TQDomElement& e )
{
    while( !e.firstChild().isNull() )
        e.removeChild( e.firstChild() );
}

TQDomElement DomUtil::elementByPath(const TQDomDocument &doc, const TQString &path)
{
    TQStringList l = TQStringList::split('/', path);

    TQDomElement el;
	if(&doc) el = doc.documentElement();
    TQStringList::ConstIterator it;
    for (it = l.begin(); it != l.end(); ++it) {
        el = el.namedItem(*it).toElement();
    }

    return el;
}


TQString DomUtil::readEntry(const TQDomDocument &doc, const TQString &path, const TQString &defaultEntry)
{
    TQDomElement el = elementByPath(doc, path);
    if (el.isNull())
        return defaultEntry;
    else
        return el.firstChild().toText().data();
}

/// @todo consider whether it's okay to accept empty string == default value
/// if not use the below type
///typedef pair<bool,TQString> EltInfo;

TQString DomUtil::readEntryAux(const TQDomDocument &doc, const TQString &path)
{
    TQDomElement el = elementByPath(doc, path);
    if (el.isNull())
        return TQString();
    else
        return el.firstChild().toText().data();
}

int DomUtil::readIntEntry(const TQDomDocument &doc, const TQString &path, int defaultEntry)
{
    TQString entry = readEntryAux(doc, path);
    if (entry.isNull())
      return defaultEntry;
    else
      return entry.toInt();
}


bool DomUtil::readBoolEntry(const TQDomDocument &doc, const TQString &path, bool defaultEntry)
{
    TQString entry = readEntryAux(doc, path);
    if (entry.isNull())
      return defaultEntry;
    else
      return entry == "TRUE" || entry == "true";
}


TQStringList DomUtil::readListEntry(const TQDomDocument &doc, const TQString &path, const TQString &tag)
{
    TQStringList list;

    TQDomElement el = elementByPath(doc, path);
    TQDomElement subEl = el.firstChild().toElement();
    while (!subEl.isNull()) {
        if (subEl.tagName() == tag)
            list << subEl.firstChild().toText().data();
        subEl = subEl.nextSibling().toElement();
    }

    return list;
}


DomUtil::PairList DomUtil::readPairListEntry(const TQDomDocument &doc, const TQString &path, const TQString &tag,
                                             const TQString &firstAttr, const TQString &secondAttr)
{
    PairList list;
    
    TQDomElement el = elementByPath(doc, path);
    TQDomElement subEl = el.firstChild().toElement();
    while (!subEl.isNull()) {
        if (subEl.tagName() == tag) {
            TQString first = subEl.attribute(firstAttr);
            TQString second = subEl.attribute(secondAttr);
            list << Pair(first, second);
        }
        subEl = subEl.nextSibling().toElement();
    }
    
    return list;
}

TQMap<TQString, TQString> DomUtil::readMapEntry(const TQDomDocument &doc, const TQString& path)
{
    TQMap<TQString, TQString> map;

    TQDomElement el = elementByPath(doc, path);
    TQDomElement subEl = el.firstChild().toElement();
    while (!subEl.isNull()) {
        map[subEl.tagName()] = subEl.firstChild().toText().data();
	subEl = subEl.nextSibling().toElement();
    }

    return map;
}

TQDomElement DomUtil::namedChildElement( TQDomElement& el, const TQString& name )
{
    TQDomElement child = el.namedItem( name ).toElement();
    if (child.isNull()) {
        child = el.ownerDocument().createElement( name );
        el.appendChild(child);
    }
    return child;
}


TQDomElement DomUtil::createElementByPath(TQDomDocument &doc, const TQString &path)
{
    TQStringList l = TQStringList::split('/', path);

    TQDomElement el;
      if(&doc) el =  doc.documentElement();
    TQStringList::ConstIterator it;
    for (it = l.begin(); it != l.end(); ++it)
        el = DomUtil::namedChildElement( el, *it );
        
    while (!el.firstChild().isNull())
        el.removeChild(el.firstChild());

    return el;
}


void DomUtil::writeEntry(TQDomDocument &doc, const TQString &path, const TQString &value)
{
    TQDomElement el = createElementByPath(doc, path);
    el.appendChild(doc.createTextNode(value));
}

void DomUtil::writeMapEntry(TQDomDocument &doc, const TQString &path, const TQMap<TQString, TQString> &map)
{
    TQString basePath( path + "/" );
    TQMap<TQString,TQString>::ConstIterator it;
    for (it = map.begin(); it != map.end(); ++it)
    {
        kdDebug( 9010 ) << "writing " << basePath << ";" << it.key() << ";" << it.data() << endl;
	if( ! it.key().isEmpty() )
            writeEntry(doc, basePath + it.key(), it.data() );
    }
}

void DomUtil::writeIntEntry(TQDomDocument &doc, const TQString &path, int value)
{
    writeEntry(doc, path, TQString::number(value));
}


void DomUtil::writeBoolEntry(TQDomDocument &doc, const TQString &path, bool value)
{
    writeEntry(doc, path, value? "true" : "false");
}


void DomUtil::writeListEntry(TQDomDocument &doc, const TQString &path, const TQString &tag,
                             const TQStringList &value)
{
    TQDomElement el = createElementByPath(doc, path);

    TQStringList::ConstIterator it;
    for (it = value.begin(); it != value.end(); ++it) {
        TQDomElement subEl = doc.createElement(tag);
        subEl.appendChild(doc.createTextNode(*it));
        el.appendChild(subEl);
    }
}


void DomUtil::writePairListEntry(TQDomDocument &doc, const TQString &path, const TQString &tag,
                                 const TQString &firstAttr, const TQString &secondAttr,
                                 const PairList &value)
{
    TQDomElement el = createElementByPath(doc, path);

    PairList::ConstIterator it;
    for (it = value.begin(); it != value.end(); ++it) {
        TQDomElement subEl = doc.createElement(tag);
        subEl.setAttribute(firstAttr, (*it).first);
        subEl.setAttribute(secondAttr, (*it).second);
        el.appendChild(subEl);
    }
}

DomPath DomUtil::resolvPathStringExt(const TQString pathstring)
{
    // parse path
    unsigned int i;
    TQStringList pathParts = TQStringList::split('/',pathstring);
    DomPath dompath;
    for (i=0; i<pathParts.count(); i++)
    {
      TQStringList pathElemParts = TQStringList::split('|',pathParts[i],TRUE);
      DomPathElement dompathelem;
      dompathelem.tagName = pathElemParts[0].simplifyWhiteSpace();
      if (pathElemParts.count()>1)
      {
        // handle attributes
        TQStringList attrParts = TQStringList::split(';',pathElemParts[1]);
        for (unsigned int j=0; j<attrParts.count(); j++)
        {
          TQStringList attribSet = TQStringList::split('=',attrParts[j]);
          if (attribSet.count()<2)
            continue;
          DomAttribute domattribute;
          domattribute.name = attribSet[0].simplifyWhiteSpace();
          domattribute.value = attribSet[1].simplifyWhiteSpace();
          dompathelem.attribute.append(domattribute);
        }
      }
      if (pathElemParts.count()>2)
	dompathelem.matchNumber = pathElemParts[2].toInt();
      else
	dompathelem.matchNumber = 0; // or else the first
      dompath.append(dompathelem);
    }
    return dompath;
}


#define rightchild !wrongchild

TQDomElement DomUtil::elementByPathExt(TQDomDocument &doc, const TQString &pathstring)
{
  DomPath dompath = resolvPathStringExt(pathstring);
  TQDomElement elem = doc.documentElement();
  TQDomNodeList children;
  TQDomElement nextElem = elem;
  for (unsigned int j=0; j<dompath.count(); j++)
  {
    children = nextElem.childNodes();
    DomPathElement dompathelement= dompath[j];
    bool wrongchild = false;
    int matchCount = 0;
    for (unsigned int i=0; i<children.count(); i++)
    {
      wrongchild = false;
      TQDomElement child = children.item(i).toElement();
      TQString tag = child.tagName();
      tag = dompathelement.tagName;
      if (child.tagName() == dompathelement.tagName)
      {
        for (unsigned int k=0; k<dompathelement.attribute.count(); k++)
        {
          DomAttribute domattribute = dompathelement.attribute[k];
          TQDomAttr domattr = child.attributeNode(domattribute.name);
          if (domattr.isNull() ||
	      domattr.value() != domattribute.value)
          {
            wrongchild = true;
            break;
          }
        }      
      }
      else
        wrongchild=true;
      if (rightchild)
      {
        if (dompathelement.matchNumber == matchCount++)
        {
          nextElem = child;
          break;
        }
      }
    }
    if (wrongchild)
    {
      TQDomElement nullDummy;
      nullDummy.clear();
      return nullDummy;
    }
  }
  return nextElem;
}


bool DomUtil::openDOMFile(TQDomDocument &doc, TQString filename)
{
  TQFile file( filename );
  if ( !file.open( IO_ReadOnly ) )
    return false;
  if ( !doc.setContent( &file ) ) {
    file.close();
    return false;
  }
  file.close();
  return true;
}

bool DomUtil::saveDOMFile(TQDomDocument &doc, TQString filename)
{
  TQFile file( filename );
  if ( !file.open( IO_ReadWrite | IO_Truncate ) )
    return false;
  TQTextStream t( &file );
  t << doc.toString();
  file.close();
  return true;
}

bool DomUtil::removeTextNodes(TQDomDocument doc,TQString pathExt)
{
  TQDomElement elem = elementByPathExt(doc,pathExt);
  if (elem.isNull())
    return false;
  TQDomNodeList children = elem.childNodes();
  for (unsigned int i=0;i<children.count();i++)
    if (children.item(i).isText())
      elem.removeChild(children.item(i));
  return true;
}


bool DomUtil::appendText(TQDomDocument doc, TQString pathExt, TQString text)
{
  TQDomElement elem = elementByPathExt(doc,pathExt);
  if (elem.isNull())
    return false;
  elem.appendChild(doc.createTextNode(text));
  return true;
}


bool DomUtil::replaceText(TQDomDocument doc, TQString pathExt, TQString text)
{
  if (removeTextNodes(doc,pathExt) &&
      appendText(doc,pathExt,text))
    return true;
  else
    return false;
}
