/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  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: 2007-11-25
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "XMLGenericItem.h"

namespace QGpCoreTools {

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

    Full description of class still missing
  */

  const QString XMLGenericItem::xmlGenericTag="XMLGenericItem";

  /*!
    Default constructor with an empty tag.
  */
  XMLGenericItem::XMLGenericItem(XMLGenericItem * parent)
    : TreeContainer(parent), _enabled(true)
  {
  }

  /*!
    Constructor with \a tag.
  */
  XMLGenericItem::XMLGenericItem(const QString& tag, XMLGenericItem * parent)
    : TreeContainer(parent), _enabled(true), _tag(tag)
  {
  }

  /*!
    Must be re-implemented in sub-classes
  */
  bool XMLGenericItem::xml_inherits(const QString& tagName) const
  {
    if(tagName==xmlGenericTag) {
      return true;
    } else {
      return XMLClass::xml_inherits(tagName);
    }
  }

  /*!
    Returns true if the parent of this item is enabled.
    For root items (without parent), true is always returned.
  */
  bool XMLGenericItem::isParentEnabled() const
  {
    TRACE;
    if(parent()) {
      if(parent()->_enabled) {
        return parent()->isParentEnabled();
      } else {
        return false;
      }
    } else {
      return true;
    }
  }

  /*!
    Return item with \a tag. Only direct children are searched. \a tag can be a path to a sub-child
  */
  QList<XMLGenericItem *> XMLGenericItem::children(const QString& tag) const
  {
    QList<XMLGenericItem *> cList;
    int pathSeparator=tag.indexOf("/");
    if(pathSeparator==-1) {
      for(const_iterator it=begin();it!=end();it++) {
        XMLGenericItem * item=static_cast<XMLGenericItem *>(*it);
        if(item->_tag==tag) {
          cList.append(item);
        }
      }
    } else {
      QString tag0=tag.left(pathSeparator);
      for(const_iterator it=begin();it!=end();it++) {
        XMLGenericItem * item=static_cast<XMLGenericItem *>(*it);
        if(item->_tag==tag0) {
          cList += children(tag.mid(pathSeparator+1));
        }
      }
    }
    return cList;
  }

  /*!
    Get the list of attributes
  */
  bool XMLGenericItem::xml_setAttributes(XML_SETATTRIBUTES_ARGS)
  {
    TRACE;
    Q_UNUSED(context)
    _attributes.clear();
    for(XMLRestoreAttributeIterator it=attributes.begin();it!=attributes.end();it++) {
      _attributes.add(it.key().toString(), it.value().toString());
    }
    return true;
  }

  /*!
    Accept all members
  */
  XMLMember XMLGenericItem::xml_member(XML_MEMBER_ARGS)
  {
    TRACE;
    Q_UNUSED(context)
    Q_UNUSED(attributes) // Ignored here because we always return a child for which XMLParser sets automatically attributes.
    if(tag==XMLClass::binDataTag) {
      return XMLMember(_binDataFiles.count());
    } else {
      XMLGenericItem * o=new XMLGenericItem(this);
      o->setTag(tag.toString());
      // Returns a member either child or properties, we don't know at this time
      return XMLMember(o, false);
    }
  }

  void XMLGenericItem::xml_attributes(XML_ATTRIBUTES_ARGS) const
  {
    TRACE;
    Q_UNUSED(context)
    attributes=_attributes;
  }

  bool XMLGenericItem::xml_setProperty(XML_SETPROPERTY_ARGS)
  {
    TRACE;
    Q_UNUSED(tag)
    Q_UNUSED(attributes)
    Q_UNUSED(memberID)
    Q_UNUSED(context)
    if(memberID>=0) {
      _binDataFiles.append(content.toString());
    } else {
      _value=content.toString();
    }
    return true;
  }

  bool XMLGenericItem::xml_setBinaryData(XML_SETBINARYDATA_ARGS)
  {
    TRACE;
    Q_UNUSED(s)
    Q_UNUSED(context)
    return true;
  }

  void XMLGenericItem::xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const
  {
    TRACE;
    Q_UNUSED(context)
    for(const_iterator it=begin(); it!=end(); it++) {
      XMLGenericItem * item=static_cast<XMLGenericItem *>(*it);
      if(item->_enabled && item->childrenCount()==0) { // A children with no children is a property
        writeProperty(s, item->xml_tagName(), item->_attributes, item->value().toString());
      }
    }
    for(QStringList::const_iterator it=_binDataFiles.begin(); it!=_binDataFiles.end(); it++) {
      writeProperty(s, XMLClass::binDataTag, *it);
    }
    if(childrenCount()==0) {
      writeProperty(s, xml_tagName(), _attributes, _value.toString());
    }
  }

  void XMLGenericItem::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
  {
    TRACE;
    Q_UNUSED(context)
    for(const_iterator it=begin(); it!=end(); it++) {
      XMLGenericItem * item=static_cast<XMLGenericItem *>(*it);
      if(item->_enabled && item->childrenCount()>0) { // A children with children has real "real" children in the XMLClass sense
        item->xml_save(s, context, item->_attributes);
      }
    }
  }

  /*!
    Returns the list of items that match the same structure as \a queryItem (tag name, similar attributes and tree).
    \a queryItem can have only a simple structure with only one child per level. Other children are not considered.

    Set empty tags to match anything. Attributes are similar if they have at least those listed by \a queryItem.
    It their values are null, only the keyword name is considered.
  */
  void XMLGenericItem::find(QList<XMLGenericItem *>& itemList, const XMLGenericItem * queryItem) const
  {
    TRACE;
    // This item matches queryItem
    if(!queryItem->xml_tagName().isEmpty() && xml_tagName()==queryItem->xml_tagName()) {
      if(!queryItem->_attributes.isEmpty() || _attributes.isSimilar(queryItem->_attributes)) {
        if(queryItem->childrenCount()>0) {
          find(itemList, queryItem->childAt(0));
        }
      }
    } else {
      for(const_iterator it=begin(); it!=end(); it++) {
        XMLGenericItem * item=static_cast<XMLGenericItem *>(*it);
        if(queryItem->xml_tagName().isEmpty() || item->xml_tagName()==queryItem->xml_tagName()) {
          if(queryItem->_attributes.isEmpty() || item->_attributes.isSimilar(queryItem->_attributes)) {
            if(queryItem->childrenCount()>0) {
              item->find(itemList, queryItem->childAt(0));
            } else {
              itemList.append(item);
            }
          }
        } else if(!queryItem->parent()) { // If still at root level for queryItem structure
          item->find(itemList, queryItem);
        }
      }
    }
  }

} // namespace QGpCoreTools
