/**************************************************************************
 *  This file is part of QXmlEdit                                         *
 *  Copyright (C) 2011 by Luca Bellonda and individual contributors       *
 *    as indicated in the AUTHORS file                                    *
 *  lbellonda _at_ gmail.com                                              *
 *                                                                        *
 * 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; if not, write to the                  *
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       *
 * Boston, MA  02110-1301  USA                                            *
 **************************************************************************/

#include "xmlEdit.h"
#include "element.h"
#include "findtextparams.h"
#include "regola.h"
#include "config.h"
#include "utils.h"

/***********************************************************************************************************
  Element class uses the following userData of the WidgetItem:
  UserData: a pointer to self
  ShowDataRole = Qt::UserRole + 1, a boolean  indicating show structure

***********************************************************************************************************/


QBrush Element::commentBrush(QColor(0xC0, 0xC0, 0xC0));
QBrush Element::defaultBrush(QColor(0xC0, 0x00, 0x00));
QBrush Element::procInstrBrush(QColor(0x00, 0xC0, 0xC0));
QBrush Element::attrBrush(QColor(0xC0, 0x00, 0x00));

bool Element::isLoadedIcons = false ;
QIcon Element::procInstrIcon;
QIcon Element::commentIcon;
QIcon Element::elementIcon;
QIcon Element::bookmarkIcon;
QIcon Element::procInstrBkmIcon;
QIcon Element::commentBkmIcon;
QIcon Element::elementBkmIcon;
QIcon Element::textIcon;
QIcon Element::textBkmIcon;
QIcon Element::childrenHiddenIcon;
QIcon Element::childrenHiddenBkmIcon;
QIcon Element::filteredAttributesIcon;

const int Element::ShowDataRole = Qt::UserRole + 1;

QRegExp Element::terminatorSearch("[\\n\\r]");

QFont Element::fixedWidthFont(DEFAULT_FIXEDWIDTHFONT);
QFont *Element::attrFont = NULL ;

int Element::defaultAttrFonts = 0 ;
int Element::fixedSizeAttrFonts = 0 ;

bool Element::firstTimeFixedWidthFont = true ;
QString Element::textCompactViewPrefix ;


ElementInfo::ElementInfo()
{
    reset();
}

ElementInfo::~ElementInfo()
{
}

void ElementInfo::reset()
{
    numElements = 0 ;
    totalSize = 0 ;
}

TextChunk::TextChunk(const bool isCDATASection, const QString &txt)
{
    isCDATA = isCDATASection ;
    text  = txt ;
}

TextChunk *TextChunk::clone()
{
    TextChunk *cloned = new TextChunk(isCDATA, text);
    return cloned;
}

long  Element::instances = 0;

Element::Element(const QString newTag, const QString &itext, Regola *regola, Element *parent)
{
    houseWork(regola, parent);
    _tag = newTag ;
    type = ET_ELEMENT;
    text = itext;
}

Element::Element(Regola *regola)
{
    houseWork(regola, NULL);
    type = ET_ELEMENT;
}

Element::Element(Regola *regola, const ElType newType, Element *parent)
{
    houseWork(regola, parent);
    type = newType ;
}

Element::~Element()
{
    QVectorIterator<Element*> it(childItems);
    while(it.hasNext()) {
        delete it.next();
    }
    clearTextNodes();
    clearAttributes();
    parentRule = NULL;
    parentElement = NULL ;
}

void Element::generateId()
{
    instanceId = instances++;
}

void Element::houseWork(Regola *regola, Element *parent)
{
    generateId();
    visibilityState = EVN_NORMAL ;
    isShowTextBase64 = false ;
    ui = NULL;
    parentRule = regola ;
    parentElement = parent;
    wasOpen = false;
    _isCData = false ;
    //_isEntityReference = false;
    _viewMode = E_VM_NONE;
    _edited = false;
    _saved = false;
}


Element *Element::parent()
{
    return parentElement ;
}

void Element::loadIcons()
{
    if(isLoadedIcons)
        return ;
    isLoadedIcons = true ;
    procInstrIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/pi_icon.png")), QIcon::Normal, QIcon::Off);
    commentIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/comm_icon.png")), QIcon::Normal, QIcon::Off);
    elementIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/el_icon.png")), QIcon::Normal, QIcon::Off);
    procInstrBkmIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/pi_bkm_icon.png")), QIcon::Normal, QIcon::Off);
    commentBkmIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/comm_bk_icon.png")), QIcon::Normal, QIcon::Off);
    elementBkmIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/el_bkm.png")), QIcon::Normal, QIcon::Off);
    bookmarkIcon.addPixmap(QPixmap(QString::fromUtf8(":/special/bookmark.png")), QIcon::Normal, QIcon::Off);
    textIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/text.png")), QIcon::Normal, QIcon::Off);
    textBkmIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/el_bkm.png")), QIcon::Normal, QIcon::Off);
    childrenHiddenIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/hidden_children")), QIcon::Normal, QIcon::Off);
    childrenHiddenBkmIcon.addPixmap(QPixmap(QString::fromUtf8(":/tree/hidden_children_bm")), QIcon::Normal, QIcon::Off);
    filteredAttributesIcon.addPixmap(QPixmap(QString::fromUtf8(":/editText/filter")), QIcon::Normal, QIcon::Off);
    textCompactViewPrefix =  "     ";
}

bool Element::isShownBase64() const
{
    return isShowTextBase64 ;
}

void Element::setShownBase64(const bool shownAs)
{
    isShowTextBase64 = shownAs ;
}

Element::EViewModes Element::viewMode() const
{
    return _viewMode ;
}
void Element::setViewMode(const EViewModes newMode)
{
    _viewMode = newMode ;
}


QString Element::limitTextWithEllipsis(const QString &inputText)
{
    bool appendEllipsis = false;
    QString result ;
    if(inputText.length() > MAX_LIMIT_TEXTLEN) {
        result = inputText.left(MAX_LIMIT_TEXTLEN);
        appendEllipsis = true ;
    } else {
        result = inputText ;
    }
    int indexOfLF = result.indexOf(terminatorSearch, 0);
    if(indexOfLF >= 0) {
        result = result.left(indexOfLF);
        appendEllipsis = true ;
    }
    if(appendEllipsis) {
        result.append(tr("..."));
    }
    return result;
}

void Element::registerState()
{
    if(NULL != ui) {
        wasOpen = ui->isExpanded();
    } else {
        wasOpen = false ;
    }
    // passa ai figli
    foreach(Element * value, childItems) {
        value->registerState();
    }
}

void Element::displayRecursive(PaintInfo *paintInfo)
{
    if(NULL == ui) {
        return ;
    }
    display(ui, paintInfo);
    // passa ai figli
    foreach(Element * value, childItems) {
        value->displayRecursive(paintInfo);
    }
}

const QString Element::styleElementTag(QTreeWidgetItem *me, PaintInfo *paintInfo)
{
    if(!VStyle::globalInitFlag) {
        VStyle::globalActivate(createElementFonts(me->font(0)), QApplication::palette().text());
    }
    QString qualifiedInfo;
    bool isFont = false ;
    bool isBrush = false ;
    VStyle* style = paintInfo->currentStyle();
    if(NULL != style) {
        foreach(Attribute * attribute, attributes) {
            IdEntry *id = style->getIdEntry(attribute->name);
            if(NULL != id) {
                if(id->isAlpha()) {
                    qualifiedInfo.append(" '");
                    qualifiedInfo.append(attribute->value);
                    qualifiedInfo.append("'");
                } else {
                    qualifiedInfo.append(" ");
                    qualifiedInfo.append(attribute->value);
                }
            }
        }
        StyleEntry *se = style->getStyleOfKeyword(tag()) ;
        if(NULL != se) {
            if(se->isColor()) {
                me->setForeground(0, se->brush());
                isBrush = true ;
            }
            QFont *styleFont = se->font();
            if(NULL != styleFont) {
                me->setFont(0, *styleFont);
                isFont = true ;
            }
        }
    }
    //usa il default se non assegnato
    if(!isBrush) {
        me->setForeground(0, VStyle::defaultBrush());
    }
    if(!isFont) {
        me->setFont(0, VStyle::defaultFont());
    }
    return qualifiedInfo;
}

const QString Element::styleElementTagString(PaintInfo *paintInfo)
{
    QString qualifiedInfo;
    VStyle* style = paintInfo->currentStyle();
    if(NULL != style) {
        foreach(Attribute * attribute, attributes) {
            IdEntry *id = style->getIdEntry(attribute->name);
            if(NULL != id) {
                if(id->isAlpha()) {
                    qualifiedInfo.append(" '");
                    qualifiedInfo.append(attribute->value);
                    qualifiedInfo.append("'");
                } else {
                    qualifiedInfo.append(" ");
                    qualifiedInfo.append(attribute->value);
                }
            }
        }
    }
    return qualifiedInfo;
}

QFont Element::styleElementTagFont(PaintInfo *paintInfo)
{
    VStyle* style = paintInfo->currentStyle();
    if(NULL != style) {
        StyleEntry *se = style->getStyleOfKeyword(tag()) ;
        if(NULL != se) {
            QFont *styleFont = se->font();
            if(NULL != styleFont) {
                return *styleFont;
            }
        }
    }
    return VStyle::defaultFont();
}

QBrush Element::styleElementTagColor(PaintInfo *paintInfo)
{
    VStyle* style = paintInfo->currentStyle();
    if(NULL != style) {
        StyleEntry *se = style->getStyleOfKeyword(tag()) ;
        if(NULL != se) {
            if(se->isColor()) {
                return se->brush();
            }
        }
    }
    //usa il default se non assegnato
    return VStyle::defaultBrush();
}

QIcon Element::styleElementTagIcon()
{
    switch(type) {
    case ET_ELEMENT:
        return elementIcon;
    case ET_PROCESSING_INSTRUCTION:
        return procInstrIcon;
    case ET_COMMENT:
        return commentIcon;
    case ET_TEXT:
        return textIcon;
    default:
        return QIcon();
    }
}

QVariant Element::columnViewTooltipData()
{
    switch(getType()) {
    case Element::ET_ELEMENT: {
        QString tooltip;
        foreach(Attribute * attribute, getAttributesList()) {
            QString attrValue = QString("%1=\"%2\"\n").arg(attribute->name).arg(attribute->value);
            tooltip += attrValue ;
        }
        return QVariant(tooltip);
    }
    default:
        break;
    } // switch type
    return QVariant();
}

QVariant Element::columnViewDisplayData(PaintInfo *paintInfo, const int rowOrdinal)
{
    switch(getType()) {
    case Element::ET_COMMENT:
        return QVariant(QString("%1 Comment: %2").arg(rowOrdinal).arg(getComment()));
        break;
    case Element::ET_PROCESSING_INSTRUCTION:
        return QVariant(QString("%1 P.I.: %2 %3").arg(rowOrdinal).arg(getPITarget()).arg(getPIData()));
        break;
    case Element::ET_TEXT: {
        QString txt = text;
        if(txt.length() > 10) {
            txt = txt.left(10);
            txt += "...";
        }
        return QVariant(QString("%1 Text: %2").arg(rowOrdinal).arg(txt));
        break;
    }
    case Element::ET_ELEMENT: {
        int totalChildrenNumber = selfInfo.numElements + childrenInfo.numElements;
        QString childInfo;
        if(totalChildrenNumber > 0) {
            childInfo = QString("(%1)").arg(totalChildrenNumber);
        }
        QString elmInfo = styleElementTagString(paintInfo);
        return QVariant(QString("%1 %2 %3 %4").arg(rowOrdinal).arg(tag()).arg(elmInfo).arg(childInfo));
    }
    default:
        return QVariant(QString("%1 ???").arg(rowOrdinal));
        break;
    } // switch type
    return QVariant("");
}

void Element::display(QTreeWidgetItem *me, PaintInfo *paintInfo)
{
    if(ui != me) {
        ui = me ;
        setChildrenLeavesHiddenState(me, false);
    }
    me->setData(0, Qt::UserRole, qVariantFromValue((void*)this));

    switch(visibilityState) {
    case EVN_ELLIPSIS:
        me->setIcon(paintInfo->columnForTag, QIcon());
        me->setText(paintInfo->columnForTag, tr("...")) ;
        me->setText(paintInfo->columnForAttributes, tr("...")) ;
        me->setText(paintInfo->columnForText, tr("...")) ;
        me->setText(paintInfo->columnForDecodedText, tr("...")) ;
        return ;
    case EVN_HIDDEN:
        me->setHidden(true);
        return ;
    default:
        break;
    }

    if(paintInfo->exploreMode()) {
        QString firstColText = QString("<%1> #%2, data: %L3")
                               .arg(tag())
                               .arg(this->childItems.size())
                               .arg(selfInfo.totalSize + childrenInfo.totalSize);
        me->setText(0, firstColText);
        return;
    }

    bool isShowAttributesLength = paintInfo->showAttributesLength();
    bool isUseFixedFonts = paintInfo->useFixedLengthFont();
    bool isOneAttributePerLine = paintInfo->oneAttrPerLine();
    bool isShowChildIndex = paintInfo->indexPerChild();
    bool isBookmarked = parentRule->isBookmarked(this);
    bool isCompactView = paintInfo->compactView();
    QString firstColText;
    if(isShowChildIndex && (NULL != parentElement)) {
        QTreeWidgetItem *parentItem = parentElement->getUI();
        if(NULL != parentItem) {
            int childIndex = parentItem->indexOfChild(ui);
            firstColText = QString("%1 -").arg(childIndex + 1);
        }
    }
    QString attrTxt = "" ;
    switch(type) {
    case ET_ELEMENT:
        firstColText += tag() ;
        if(!areChildrenLeavesHidden(me)) {
            me->setIcon(0, isBookmarked ? elementBkmIcon : elementIcon);
        } else {
            me->setIcon(0, isBookmarked ? childrenHiddenBkmIcon : childrenHiddenIcon);
        }
        {
            QString qualifiedInfo = styleElementTag(me, paintInfo);
            if(qualifiedInfo.length() > 0) {
                firstColText.append(" - ");
                firstColText.append(qualifiedInfo);
            }
            me->setText(paintInfo->columnForTag, firstColText);
            if(paintInfo->showElementSize()) {
                QString children = QString("%1 (%2)")
                                   .arg(selfInfo.numElements)
                                   .arg(selfInfo.numElements + childrenInfo.numElements);
                me->setText(paintInfo->columnForChildren, children);
                QString size = QString("%L1")
                               .arg(selfInfo.totalSize + childrenInfo.totalSize);
                me->setText(paintInfo->columnForSize, size);
            }
            QString textToShow ;
            QString textToShowBase64 ;
            bool collectBase64Text = isShowTextBase64 || paintInfo->showUnBase64();
            QVectorIterator<TextChunk*> tt(textNodes);
            while(tt.hasNext()) {
                TextChunk   *tx = tt.next();
                if(tx->isCDATA) {
                    textToShow.append(" [[CDATA:");
                    if(isCompactView) {
                        textToShow.append(tx->text.trimmed());
                    } else {
                        textToShow.append(tx->text);
                    }
                    textToShow.append(" ]]");
                } else {
                    if(isCompactView) {
                        if(collectBase64Text) {
                            if(!textToShowBase64.isEmpty()) {
                                textToShowBase64.append("\n ---\n");
                            }
                            textToShowBase64.append(Utils::fromBase64(tx->text));
                        }
                        textToShow.append(tx->text.trimmed());
                    } else {
                        if(collectBase64Text) {
                            if(!textToShowBase64.isEmpty()) {
                                textToShowBase64.append("\n ---\n");
                            }
                            textToShowBase64.append(Utils::fromBase64(tx->text));
                        }
                        textToShow.append(tx->text);
                    }
                }
            }
            QString originalText = textToShow ;
            if(isCompactView) {
                textToShow = limitTextWithEllipsis(textToShow);
            }
            if(paintInfo->showElementTextLength()) {
                textToShow = QString("(%1)- %2").arg(originalText.length()).arg(textToShow);
            }

            if(isCompactView && attributes.isEmpty()) {
                me->setText(paintInfo->columnForText, "");
                attrTxt = textCompactViewPrefix;
                attrTxt.append(textToShow);
            } else {
                me->setText(paintInfo->columnForText, textToShow);
            }
            me->setText(paintInfo->columnForDecodedText, textToShowBase64);
        }
        break;
    case ET_PROCESSING_INSTRUCTION: {
        firstColText += tr("*Processing Instruction* <? ?>") ;
        me->setText(0, firstColText);
        me->setForeground(0, procInstrBrush);
        me->setIcon(0, isBookmarked ? procInstrBkmIcon : procInstrIcon);
        QString pI = getPITarget();
        pI.append("(");
        pI.append(getPIData());
        pI.append(")");
        me->setText(paintInfo->columnForAttributes, pI);
        me->setText(paintInfo->columnForText, tr(""));
    }
    break;
    case ET_COMMENT: {
        firstColText += tr("*comment*") ;
        me->setText(0, firstColText);
        me->setForeground(0, commentBrush);
        me->setIcon(0, isBookmarked ? commentBkmIcon : commentIcon);
        QString textToShow = text;
        if(isCompactView) {
            textToShow = limitTextWithEllipsis(text);
        }
        me->setText(paintInfo->columnForText, textToShow);
    }
    break;

    case ET_TEXT: {
        firstColText += tr("*text*") ;
        me->setText(0, firstColText);
        me->setForeground(0, commentBrush);
        me->setIcon(0, isBookmarked ? textBkmIcon : textIcon);
        QString textToShow = text;
        if(isCompactView) {
            textToShow = limitTextWithEllipsis(text);
        }
        me->setText(paintInfo->columnForText, textToShow);
    }
    break;

    default:
        firstColText += tr("unknown node") ;
        me->setText(0, firstColText);
        me->setForeground(0, defaultBrush);
        me->setIcon(0, isBookmarked ? bookmarkIcon : QIcon());
        break;
    }

    if(!attributes.isEmpty()) {
        bool isFirst = true;
        QString attrList ;
        AttributeFilter *filter = paintInfo->attributeFilter();
        bool skipped = false;

        if(paintInfo->attributesHTML()) {
            attrList = "<html><body bgcolor=\"transparent\">";
            int attrIndex = 1 ;
            int size = attributes.size();
            for(int attr = 0; attr < size ; attr++) {
                Attribute* attribute = attributes.at(attr);
                QString key = attribute->name;
                QString value = attribute->value;
                if(NULL != filter) {
                    bool skip = filter->checkIfSkipped(key);
                    if(skip) {
                        skipped = true ;
                        continue;
                    }
                }

                if(isFirst) {
                    isFirst = false;
                } else {
                    if(isOneAttributePerLine) {
                        if(isCompactView) {
                            if(attrIndex > ATTR_PER_COLUMN) {
                                attrIndex = 1 ;
                                attrList.append("<br/>\n");
                            } else {
                                attrList.append(", ");
                            }
                        } else { // compactView
                            attrList.append("<br/>\n");
                        }
                    } else { //OneAttribute
                        attrList.append(", ");
                    }
                }
                attrList.append("<FONT COLOR=\"green\">");
                attrList.append(key);
                if(isShowAttributesLength) {
                    int attrLen = value.length();
                    attrList.append("(");
                    if(isUseFixedFonts) {
                        attrList.append(QString("%1").arg(attrLen, WIDTH_LEN_CHARS));
                    } else {
                        attrList.append(QString("%1").arg(attrLen));
                    }
                    attrList.append(")=\"");
                } else {
                    attrList.append("=\"");
                }
                attrList.append("</FONT><FONT COLOR=\"blue\">");
                attrList.append(value);
                attrList.append("</FONT>");
                attrList.append("\"");
                attrIndex ++;
            }
            attrList.append("</body><html>");
        } else {
            int attrIndex = 1 ;
            int size = attributes.size();
            for(int attr = 0; attr < size ; attr++) {
                Attribute* attribute = attributes.at(attr);
                QString key = attribute->name;
                QString value = attribute->value;
                if(NULL != filter) {
                    bool skip = filter->checkIfSkipped(key);
                    if(skip) {
                        skipped = true ;
                        continue;
                    }
                }

                if(isFirst) {
                    isFirst = false;
                } else {
                    if(isOneAttributePerLine) {
                        if(isCompactView) {
                            if(attrIndex > ATTR_PER_COLUMN) {
                                attrIndex = 1 ;
                                attrList.append("\n");
                            } else {
                                attrList.append(", ");
                            }
                        } else { // compactView
                            attrList.append("\n");
                        }
                    } else { //OneAttribute
                        attrList.append(", ");
                    }
                }
                attrList.append(key);
                if(isShowAttributesLength) {
                    int attrLen = value.length();
                    attrList.append("(");
                    if(isUseFixedFonts) {
                        attrList.append(QString("%1").arg(attrLen, WIDTH_LEN_CHARS));
                    } else {
                        attrList.append(QString("%1").arg(attrLen));
                    }
                    attrList.append(")=\"");
                } else {
                    attrList.append("=\"");
                }
                attrList.append(value);
                attrList.append("\"");
                attrIndex ++;
            }
        }
        me->setText(paintInfo->columnForAttributes, attrList);
        if(skipped) {
            me->setIcon(paintInfo->columnForAttributes, filteredAttributesIcon);
        } else {
            me->setIcon(paintInfo->columnForAttributes, QIcon());
        }
        me->setForeground(paintInfo->columnForAttributes, attrBrush);
        if(isUseFixedFonts) {
            if(firstTimeFixedWidthFont) {
                firstTimeFixedWidthFont = false ;
                createFixedWidthFonts();
            }
            me->setFont(paintInfo->columnForAttributes, fixedWidthFont);
        } else {
            if(NULL == attrFont) {
                QFontInfo info(ui->font(0));
                QString family = info.family();
                attrFont = new QFont(family);
                defaultAttrFonts = attrFont->pointSize();
            }
            me->setFont(paintInfo->columnForAttributes, *attrFont);
        }

    } else if(ET_ELEMENT == type) {
        me->setText(paintInfo->columnForAttributes, attrTxt);
        if(attrTxt.length() > 0) {
            if(NULL == attrFont) {
                QFontInfo info(ui->font(0));
                QString family = info.family();
                attrFont = new QFont(family);
                defaultAttrFonts = attrFont->pointSize();
            }
            me->setFont(paintInfo->columnForAttributes, *attrFont);
        }
    }
    if(wasOpen) {
        ui->setExpanded(true);
    }
}

void Element::createFixedWidthFonts()
{
    firstTimeFixedWidthFont = false ;
    fixedWidthFont.setFamily(DEFAULT_FIXEDWIDTHFONT);
    //se abilitato
    if(Config::getBool(Config::KEY_ELEMENT_FONTFIXEXDWIDTHENABLED, false)) {
        QString family = Config::getString(QString(Config::KEY_ELEMENT_FONTFIXEXDWIDTHFAMILY), DEFAULT_FIXEDWIDTHFONT);
        if(family.isEmpty()) {
            family = DEFAULT_FIXEDWIDTHFONT ;
        }
        fixedWidthFont.setFamily(family);
        int fixedWidthFontPoints = Config::getInt(Config::KEY_ELEMENT_FONTFIXEXDWIDTHSIZE, 0);
        if(fixedWidthFontPoints > 0) {
            fixedWidthFont.setPointSize(fixedWidthFontPoints);
        }
    }
    fixedSizeAttrFonts = fixedWidthFont.pointSize();
}

QFont Element::createElementFonts(QFont defaultFont)
{
    if(Config::getBool(Config::KEY_ELEMENT_ELEMENTFONTENABLED, false)) {
        QString elementFontName = Config::getString(Config::KEY_ELEMENT_ELEMENTFONTFAMILY, "");
        int elementFontSize = Config::getInt(Config::KEY_ELEMENT_ELEMENTFONTSIZE, 0);
        bool elementFontItalic = Config::getBool(Config::KEY_ELEMENT_ELEMENTFONTITALIC, false);
        bool elementFontBold = Config::getBool(Config::KEY_ELEMENT_ELEMENTFONTBOLD, false);
        if(elementFontName.isEmpty()) {
            elementFontName = defaultFont.family();
        }
        if(elementFontSize <= 0) {
            elementFontSize = defaultFont.pointSize();
        }
        QFont newDefaultFont(elementFontName, elementFontSize, elementFontBold ? QFont::Bold : -1, elementFontItalic);
        return newDefaultFont;
    } else {
        return defaultFont ;
    }
}


QString Element::getAsText()
{
    QString textToShow;
    QVectorIterator<TextChunk*> tt(textNodes);
    while(tt.hasNext()) {
        TextChunk   *tx = tt.next();
        if(tx->isCDATA) {
            textToShow.append(" [[CDATA:");
            textToShow.append(tx->text);
            textToShow.append(" ]]");
        } else {
            textToShow.append(tx->text);
        }
    }
    return textToShow ;
}

QString Element::getAsSimpleText(const bool isBase64)
{
    QString textToShow;
    if(type == ET_TEXT) {
        textToShow = text ;
    } else {
        QVectorIterator<TextChunk*> tt(textNodes);
        while(tt.hasNext()) {
            TextChunk   *tx = tt.next();
            textToShow.append(tx->text);
        }
    }
    if(isBase64) {
        textToShow = Utils::fromBase64(textToShow);
    }
    return textToShow ;
}

QString Element::getAsSimpleTextXml(const bool isBase64)
{
    QString textToShow;
    if(type == ET_TEXT) {
        textToShow = text ;
    } else {
        QVectorIterator<TextChunk*> tt(textNodes);
        while(tt.hasNext()) {
            TextChunk   *tx = tt.next();
            textToShow.append(tx->text);
        }
    }
    if(isBase64) {
        textToShow = Utils::fromBase64Xml(textToShow);
    }
    return textToShow ;
}

bool Element::isCDATA()
{
    if(type == ET_TEXT) {
        return _isCData ;
    }
    QVectorIterator<TextChunk*> tt(textNodes);
    while(tt.hasNext()) {
        TextChunk   *tx = tt.next();
        return tx->isCDATA ;
    }
    return false ;
}

void Element::setAsSingleTextNode(const QString &newText, const bool isBase64, const bool isCData)
{
    clearTextNodes();
    QString text = newText ;
    if(isBase64) {
        text = Utils::toBase64(newText);
    }
    if(type == ET_TEXT) {
        setTextOfTextNode(text, isCData);
    }  else {
        TextChunk *textChunk = new TextChunk(isCData, text);
        addTextNode(textChunk);
    }
    markEdited();
    //TODO fa il display?
}

void Element::markEdited()
{
    _edited = true;
    _saved  = false;
}

void Element::markSavedRecursive()
{
    if(_edited) {
        _saved  = true;
    }
    foreach(Element * value, childItems) {
        value->markSavedRecursive();
    }
}

void Element::markEditedRecursive()
{
    _edited = true;
    _saved  = false;
    foreach(Element * value, childItems) {
        value->markEditedRecursive();
    }
}

bool Element::edited()
{
    return _edited ;
}

bool Element::saved()
{
    return _saved ;
}


void Element::caricaFigli(QTreeWidget *pTree, QTreeWidgetItem *parent, PaintInfo *paintInfo, const bool isGUI, const int pos)
{
    D(printf("..statement %s, figli %d\n", text.toAscii().data(), childItems.size()));
    // Carica se stesso
    QTreeWidgetItem *me = NULL ;
    bool isTop = false;
    if(isGUI) {
        if(NULL == parent) {
            me = new QTreeWidgetItem(0);
            isTop = true ;
        } else {
            if(pos >= 0) {
                me = new QTreeWidgetItem();
                parent->insertChild(pos, me);
            } else {
                me = new QTreeWidgetItem(parent);
            }
        }
        display(me, paintInfo);
    }
    // passa ai figli
    foreach(Element * value, childItems) {
        value->caricaFigli(NULL, me, paintInfo, isGUI);
    }
    if(isTop) {
        if(pos >= 0) {
            pTree->insertTopLevelItem(pos, me);
        } else {
            pTree->addTopLevelItem(me);
        }
    }
}

bool Element::generateDom(QDomDocument &document, QDomNode &parent)
{
    switch(type) {
    default:
    case ET_ELEMENT: {
        // appends itself
        QDomElement node = document.createElement(tag());

        QVectorIterator<TextChunk*> tt(textNodes);
        while(tt.hasNext()) {
            TextChunk   *tx = tt.next();
            if(tx->isCDATA) {
                QDomCDATASection nodeCData = document.createCDATASection(tx->text);
                node.appendChild(nodeCData);
            } else {
                QDomText nodeText = document.createTextNode(tx->text);
                node.appendChild(nodeText);
            }
        }

        //itera sulla lista e prendi i valori dalla chiabe
        QVectorIterator<Attribute*>  attrs(attributes);
        while(attrs.hasNext()) {
            Attribute* attribute = attrs.next();
            node.setAttribute(attribute->name, attribute->value);
        }

        parent.appendChild(node);
        //append children
        foreach(Element * value, childItems) {
            if(!value->generateDom(document, node))
                return false;
        }
        return true;
    }
    break;

    case ET_PROCESSING_INSTRUCTION: {
        QDomProcessingInstruction node = document.createProcessingInstruction(getPITarget(), getPIData());
        parent.appendChild(node);
        return true;
    }
    break;

    case ET_COMMENT: {
        QDomComment node = document.createComment(getComment());
        parent.appendChild(node);
        return true;
    }
    break;

    case ET_TEXT: {
        if(_isCData) {
            QDomCDATASection nodeCData = document.createCDATASection(text);
            parent.appendChild(nodeCData);
        } else {
            QDomText nodeText = document.createTextNode(text);
            parent.appendChild(nodeText);
        }
        return true;
    }
    break;

    }
    return false;
}

bool Element::isEmpty() const
{
    return childItems.isEmpty();
}

void Element::clearAttributes()
{
    QVectorIterator<Attribute*> it(attributes);
    while(it.hasNext()) {
        delete it.next();
    }
    attributes.clear();
}

void Element::addAttribute(const QString &name, const QString &value)
{
    if(!name.isEmpty()) {
        Attribute *attribute = new Attribute(name, value);
        //if( NULL == attribute ) { TODO
        //    throw new QXmlException(ERROR_1, tr("cannot add an attribute") );
        //}
        attributes.append(attribute);
    }
}

Element *Element::fromItemData(QTreeWidgetItem *item)
{
    if(NULL == item) {
        return NULL ;
    }
    QVariant data = item->data(0, Qt::UserRole);
    Element *pElement = (Element *)data.value<void*>();
    return pElement ;
}

Element *Element::fromModelIndex(const QModelIndex & index)
{
    if(!index.isValid()) {
        return NULL ;
    }
    QVariant data = index.data(Qt::UserRole);
    Element *pElement = (Element *)data.value<void*>();
    return pElement ;
}

void Element::addChild(Element *newElement)
{
    newElement->parentElement = this ;
    childItems.append(newElement);
    addChildInfo(newElement);
}

void Element::addChildAt(Element *newElement, const int position)
{
    newElement->parentElement = this ;
    childItems.insert(position, newElement);
    addChildInfo(newElement);
}


int Element::addChildAfter(Element *newElement, Element *brotherElement)
{
    newElement->parentElement = this ;
    if(NULL == brotherElement) {
        childItems.append(newElement);
        return childItems.size() - 1;
    }
    QVectorIterator<Element*> it(childItems);
    int index = 0;
    while(it.hasNext()) {
        if(it.next() == brotherElement) {
            childItems.insert(index + 1, newElement);
            return index + 1 ;
        }
        index ++;
    }
    // not found, print a warning
    // TODO: D_W("addChildAfter: not found");
    childItems.append(newElement);
    addChildInfo(newElement);
    parentRule->setModified(true) ;
    return childItems.size() - 1;
}

bool Element::moveUp(QVector<Element*> &items, Element *element)
{
    int indexOf = items.indexOf(element);
    if(indexOf <= 0)
        return false;
    Element *pToSwap = items.at(indexOf - 1);
    items.replace(indexOf - 1, element);
    items.replace(indexOf, pToSwap);
    // user interface
    QTreeWidget *tree = element->ui->treeWidget();
    QTreeWidgetItem *item = NULL;
    Element *parent = element->parentElement;
    if(NULL == parent) {
        item = tree->takeTopLevelItem(indexOf);
        QTreeWidgetItem *item1p = tree->takeTopLevelItem(indexOf - 1);
        tree->insertTopLevelItem(indexOf - 1, item);
        tree->insertTopLevelItem(indexOf, item1p);
    } else {
        QTreeWidgetItem *parentUI = parent->ui;
        item = parentUI->takeChild(indexOf);
        QTreeWidgetItem *item1p = parentUI->takeChild(indexOf - 1);
        parentUI->insertChild(indexOf - 1, item);
        parentUI->insertChild(indexOf, item1p);
    }
    tree->setCurrentItem(item);
    element->markEditedRecursive();
    element->parentRule->setModified(true) ;
    return true;
}

bool Element::moveUp(Element *element)
{
    return moveUp(childItems, element);
}

bool Element::moveDown(QVector<Element*> &items, Element *element)
{
    int indexOf = items.indexOf(element);
    if((indexOf < 0) || (indexOf >= (items.size() - 1)))
        return false;
    Element *pToSwap = items.at(indexOf + 1);
    items.replace(indexOf + 1, element);
    items.replace(indexOf, pToSwap);
    // user interface
    /*
        QTreeWidgetItem *item1p = ui->takeChild(indexOf+1);
        QTreeWidgetItem *item1 = ui->takeChild(indexOf);
        ui->insertChild(indexOf, item1p);
        ui->insertChild(indexOf+1, item1);
        return true;
    */
    QTreeWidget *tree = element->ui->treeWidget();
    QTreeWidgetItem *item = NULL;
    QTreeWidgetItem *item1p ;
    Element *parent = element->parentElement;
    if(NULL == parent) {
        item1p = tree->takeTopLevelItem(indexOf + 1);
        item = tree->takeTopLevelItem(indexOf);
        tree->insertTopLevelItem(indexOf, item1p);
        tree->insertTopLevelItem(indexOf + 1, item);
    } else {
        QTreeWidgetItem *parentUI = parent->ui;

        item1p = parentUI->takeChild(indexOf + 1);
        item = parentUI->takeChild(indexOf);
        parentUI->insertChild(indexOf, item1p);
        parentUI->insertChild(indexOf + 1, item);
    }
    tree->setCurrentItem(item);
    element->markEditedRecursive();
    element->parentRule->setModified(true) ;
    return true;
}

bool Element::moveDown(Element *element)
{
    return moveDown(childItems, element);
}

void Element::zeroUI()
{
    zeroUISelf(true);
    foreach(Element * item, childItems) {
        item->zeroUI();
    }
}

void Element::zeroUISelf(const bool emitMe)
{
    parentRule->takeOutElement(this);
    ui = NULL ;
    if(emitMe) {
        parentRule->notifyUIDeleted(this);
    } else {
        parentRule->takeoutFormBookmarks(this);
    }
}

Regola *Element::getParentRule()
{
    return parentRule ;
}

void Element::deleteUI()
{
    if(NULL != ui) {
        QTreeWidgetItem *parentWItem = ui->parent();
        int index;
        if(NULL != parentWItem) {
            index = parentWItem->indexOfChild(ui);
            delete parentWItem->takeChild(index);
        } else {
            index = ui->treeWidget()->indexOfTopLevelItem(ui);
            delete ui->treeWidget()->takeTopLevelItem(index);
        }
        // elimina tutti i figli
        zeroUI();
    }
}

void Element::autoDelete(const bool deleteMe)
{
    // sgancia l'elemento dal parent
    if(NULL != parentElement) {
        int indexOf = parentElement->childItems.indexOf(this);
        parentElement->childItems.remove(indexOf);
    } else {
        // iif it is a true element
        parentRule->notifyDeletionTopElement(this);
    }
    parentRule->takeOutElement(this) ;
    // this operation causes a notify message and must be done after processing.
    deleteUI();
    parentRule->setModified(true) ;
    parentRule = NULL;
    if(deleteMe) {
        delete this;
    }
}

void Element::autoDeleteRecursiveInner()
{
    foreach(Element * child, childItems) {
        child->autoDeleteRecursiveInner();
    }
    // let the parent deal with this
    zeroUISelf(false);
    ui = NULL ;
    parentRule = NULL;
}

void Element::autoDeleteRecursive()
{
    foreach(Element * child, childItems) {
        child->autoDeleteRecursiveInner();
        delete child;
    }
    childItems.clear();
    // sgancia l'elemento dal parent
    if(NULL != parentElement) {
        int indexOf = parentElement->childItems.indexOf(this);
        parentElement->childItems.remove(indexOf);
    } else {
        // iif it is a true element
        parentRule->notifyDeletionTopElement(this);
    }
    parentRule->takeOutElement(this) ;
    // this operation causes a notify message and must be done after processing.
    if(NULL != ui) {
        deleteUI();
    } else {
        zeroUISelf(false);
    }
    parentRule->setModified(true) ;
    parentRule = NULL;
    delete this;
}

Element *Element::copyTo(Element &newElement)
{
    return copyTo(newElement, true);
}

Element *Element::copyTo(Element &newElement, const bool isRecursive)
{
    copyHeaderAndDirectNodes(newElement);
    // TODO newElement.nameSpace = nameSpace;

    QVectorIterator<Element*> it(childItems);
    while(it.hasNext()) {
        Element *newEl = new Element(newElement.parentRule);
        newElement.addChild(newEl);
        it.next()->copyTo(*newEl, isRecursive);
    }
    return &newElement;
}


void Element::copyHeader(Element &newElement)
{
    // TODO newElement.nameSpace = nameSpace;
    if(NULL != newElement.parentRule) {
        newElement._tag = newElement.parentRule->addNameToPool(_tag);
    } else {
        newElement._tag = _tag ;
    }
    newElement.text = text;
    newElement.type = type ;
    newElement._viewMode = _viewMode ;
    newElement._isCData = _isCData ;
    //newElement._isEntityReference = _isEntityReference ;
}

void Element::copyHeaderAndDirectNodes(Element &newElement)
{
    copyHeader(newElement);
    newElement.clearTextNodes();
    newElement.clearAttributes();

    QVectorIterator<Attribute*>attr(attributes);
    while(attr.hasNext()) {
        Attribute *src = attr.next();
        Attribute *dst = new Attribute(src->name, src->value);
        newElement.attributes.append(dst);
    }

    QVectorIterator<TextChunk*> tt(textNodes);
    while(tt.hasNext()) {
        TextChunk  *tx = tt.next();
        TextChunk  *newText = tx->clone();
        newElement.textNodes.append(newText);
    }
}


Element* Element::copyToClipboard()
{
    Element *newElement = new Element(NULL);
    if(NULL != newElement) {
        copyTo(*newElement);
    }
    return newElement;
}

Element *Element::cutToClipboard()
{
    // detach from parent
    if(NULL != parentElement) {
        parentElement->removeChildInfo(this);
    }
    parentRule->deleteElement(this, false);
    return this;
}

bool Element::isFirstChild()
{
    if(NULL == parentElement) {
        return (parentRule->getItems().indexOf(this) <= 0) ? true : false;
    }
    return (parentElement->childItems.indexOf(this) <= 0) ? true : false;
}

bool Element::isLastChild()
{
    int indexOf = -1 ;
    int size = 0;
    if(NULL == parentElement) {
        QVector<Element*> &items = parentRule->getItems();
        size = items.size();
        indexOf = items.indexOf(this);
    } else {
        indexOf = parentElement->childItems.indexOf(this);
        size = parentElement->childItems.size();
    }
    if((indexOf < 0) || (indexOf >= (size - 1)))
        return true;
    return false;
}

void Element::expand(QTreeWidget *tree)
{
    if(NULL != ui)
        tree->expandItem(ui);
    QVectorIterator<Element*> it(childItems);
    while(it.hasNext()) {
        it.next()->expand(tree);
    }
}

Element* Element::getChildAt(const int childIndex)
{
    if((childIndex >= 0) && (childIndex < childItems.size())) {
        return childItems.at(childIndex);
    }
    return NULL ;
}

int Element::childIndex(Element *child)
{
    int index = 0 ;
    foreach(Element * aChild, childItems) {
        if(aChild == child) {
            return index ;
        }
        index ++ ;
    }
    return -1;
}


void Element::setText(const QString &data)
{
    text = data ;
}

void Element::setTextOfTextNode(const QString &data, const bool isCData)
{
    text = data ;
    _isCData = isCData ;
}

const QString &Element::getComment()
{
    return text ;
}

void Element::setComment(const QString & comment)
{
    text = comment ;
}

void Element::addTextNode(TextChunk *text)
{
    textNodes.append(text);
}

void Element::clearTextNodes()
{
    QVectorIterator<TextChunk*> tt(textNodes);
    while(tt.hasNext()) {
        delete tt.next();
    }
    textNodes.clear();
}

const QString &Element::getPITarget()
{
    return text;
}

const QString Element::getPIData()
{
    return tag();
}

void Element::setPITarget(const QString & target)
{
    text = target ;
}

void Element::setPIData(const QString & data)
{
    _tag = parentRule->addNameToPool(data);
}

// nuovo algoritmo: ambito di ricerca

bool Element::searchInScope(FindTextParams &findArgs)
{
    if(!findArgs.isSearchWithScope()) {
        return true;
    }
    QString scope = findArgs.mainScope();
    if(scope.isEmpty() || (tag() == scope)) {
        QStringList scopes = findArgs.getScopes(); //get a reference!
        int scopeCount = scopes.count();
        Element *el = this ;
        for(int i = scopeCount - 1 ; i >= 0 ; i--) {
            el = el->parentElement ;
            if(NULL == el) {
                return false;
            }
            QString thisScope = scopes.at(i) ;
            if(!thisScope.isEmpty() && (el->tag() != thisScope)) {
                return false;
            }
        }
        return true;
    }
    return false ;
}

bool Element::findText(FindTextParams &findArgs)
{
    bool isFound = false;
    bool isFoundInChild = false;
    bool isHiliteAll = findArgs.isHiliteAll();
    bool isCloseUnrelated = findArgs.isCloseUnrelated();
    FindTextParams::EFindTarget findTarget = findArgs.getFindTarget();
    if(findArgs.isCountingOnly()) {
        isCloseUnrelated = false;
    }

    switch(type) {
    default:
    case ET_ELEMENT: {

        if(searchInScope(findArgs)) {
            if((FindTextParams::FIND_ALL == findTarget) || (FindTextParams::FIND_TAG == findTarget)) {
                if(findArgs.isTextMatched(tag()))
                    isFound = true ;
            }
            if(!isFound) {
                if((FindTextParams::FIND_ALL == findTarget) || (FindTextParams::FIND_TEXT == findTarget)) {
                    foreach(TextChunk * chunk, textNodes) {
                        if(findArgs.isTextMatched(chunk->text)) {
                            isFound = true ;
                        }
                    }
                }
            }
            if(!isFound) {
                QVectorIterator<Attribute*>  attrs(attributes);
                while(attrs.hasNext()) {
                    Attribute* a = attrs.next();
                    if((FindTextParams::FIND_ALL == findTarget) || (FindTextParams::FIND_ATTRIBUTE_NAME == findTarget)) {
                        if(findArgs.isTextMatched(a->name)) {
                            isFound = true ;
                        }
                    }
                    if(!isFound) {
                        if((FindTextParams::FIND_ALL == findTarget) || (FindTextParams::FIND_ATTRIBUTE_VALUE == findTarget)) {
                            if(findArgs.isSearchInAttribute()) {
                                if((a->name == findArgs.attributeName())
                                        && (FindTextParams::FIND_ATTRIBUTE_VALUE == findTarget)) {
                                    isFound = true ;
                                }
                            } else if(findArgs.isTextMatched(a->value)) {
                                isFound = true ;
                            }
                        }
                    }
                }
            }
        }
    }
    break;
    case ET_TEXT:
        if((FindTextParams::FIND_ALL == findTarget) || (FindTextParams::FIND_TEXT == findTarget)) {
            if(findArgs.isTextMatched(text)) {
                isFound = true ;
            }
        }
        break;
    case ET_PROCESSING_INSTRUCTION: {
        if(FindTextParams::FIND_ALL == findTarget) {
            if(findArgs.isTextMatched(getPITarget()) || findArgs.isTextMatched(getPIData())) {
                isFound = true ;
            }
        }
    }
    break;
    case ET_COMMENT: {
        if(FindTextParams::FIND_ALL == findTarget) {
            if(findArgs.isTextMatched(getComment())) {
                isFound = true ;
            }
        }
    }
    break;
    }
    if(isFound) {
        findArgs.newOccurence(selfInfo.totalSize + childrenInfo.totalSize);
        if(!findArgs.isCountingOnly()) {
            hilite();
            if(findArgs.isSelToBookmarks()) {
                parentRule->addBookmark(this);
            }
        }
    }
    //append children
    if(isHiliteAll) {
        foreach(Element * value, childItems) {
            if(value->findText(findArgs)) {
                isFoundInChild = true ;
            }
        }
    }

    bool isFoundSomeWhere = isFoundInChild || isFound ;
    if(!findArgs.isCountingOnly()) {
        if(isCloseUnrelated && (NULL != ui)) {
            if(isFoundInChild) {
                if(!ui->isExpanded()) {
                    ui->setExpanded(true);
                }
            } else {
                if(ui->isExpanded()) {
                    ui->setExpanded(false);
                }
            }
        }
    }
    return isFoundSomeWhere  ;
}

void Element::unhilite()
{
    QTreeWidgetItem *theUi = getUI();
    if(NULL != theUi) {
        theUi->setBackgroundColor(0, QColor(0xFF, 0xFF, 0xFF, 0));
    }
}

void Element::hilite()
{
    QTreeWidgetItem *theUi = getUI();
    if(NULL != theUi) {
        theUi->setBackgroundColor(0, QColor(0xFF, 0xC0, 0x40));
    }
    parentRule->addHilite(this);
}

bool Element::isMixedContent()
{
    foreach(Element * value, childItems) {
        if(value->type == ET_TEXT) {
            return true ;
        }
    }
    return false;
}

QVector<Element*> & Element::getParentChildren()
{
    if(NULL == parentElement) {
        return parentRule->getItems();
    } else {
        return parentElement->childItems;
    }
}

QTreeWidgetItem *Element::findNextBrother(QVector<Element*> &items)
{
    int indexOfSelf = items.indexOf(this);
    int size = items.size();
    if((indexOfSelf >= 0) && (indexOfSelf < (size - 1))) {
        return items.at(indexOfSelf + 1)->ui ;
    }
    return NULL;
}

QTreeWidgetItem *Element::goToNextBrother()
{
    return findNextBrother(getParentChildren());
}

QTreeWidgetItem *Element::findPreviousBrother(QVector<Element*> &items)
{
    int indexOfSelf = items.indexOf(this);
    if(indexOfSelf > 0) {
        return items.at(indexOfSelf - 1)->ui ;
    }
    return NULL;
}

QTreeWidgetItem *Element::goToPreviousBrother()
{
    return findPreviousBrother(getParentChildren());
}

void Element::isFirstOrLastChild(bool &isFirst, bool &isLast)
{
    findFirstAndLast(getParentChildren(), isFirst, isLast);
}

void Element::findFirstAndLast(QVector<Element*> &items, bool &isFirst, bool &isLast)
{
    int indexOfSelf = items.indexOf(this);
    if(indexOfSelf > 0) {
        isFirst = false;
    } else {
        isFirst = true ;
    }
    int size = items.size();
    if(indexOfSelf < (size - 1)) {
        isLast = false;
    } else {
        isLast = true ;
    }
}

void Element::refreshUI()
{
    parentRule->redisplayElement(this);
}

void Element::displayWithPaintInfo(PaintInfo *paintInfo)
{
    if(NULL != ui) {
        display(ui, paintInfo);
    }
}

QString Element::formatRowNumber(const int index)
{
    QString str = QString::number(index, 16);
    return str.rightJustified(6, '0', true);
}

QString Element::topLevelItemIndex(QTreeWidgetItem *topLevelItem)
{
    int position = ui->treeWidget()->indexOfTopLevelItem(topLevelItem);
    return formatRowNumber(position);
}

/**
  * swaps a child with a new element ans return its position
  */
int Element::setItemLike(Element *newElement, Element* oldElement)
{
    int pos = childItems.indexOf(oldElement);
    if(pos >= 0) {
        childItems.insert(pos, newElement);
    }
    return pos;
}

QString Element::getRow()
{
    QString result = "";
    if(NULL != ui) {
        QTreeWidgetItem *parent = ui->parent();
        if(NULL == parent) {
            return topLevelItemIndex(ui);
        }
        QTreeWidgetItem *child = ui;
        while(NULL != parent) {
            result = formatRowNumber(parent->indexOfChild(child)) + result;
            child = parent;
            parent = parent->parent();
        }
        result = topLevelItemIndex(child) + result;
    }
    return result;
}

void Element::hideBrothers()
{
    int indexOfThis = -1 ;
    int size = 0;
    QVector<Element*> *elems = NULL ;
    if(NULL == parentElement) {
        QVector<Element*> *items = parentRule->getChildItems();
        size = items->size();
        indexOfThis = items->indexOf(this);
        elems = items;
    } else {
        indexOfThis = parentElement->childItems.indexOf(this);
        size = parentElement->childItems.size();
        elems = this->parentElement->getChildItems();
    }
    if((indexOfThis < 0) || (indexOfThis >= size))
        return ;
    int previousIndex = indexOfThis - 1 ;
    int nextIndex = indexOfThis + 1 ;

    QVectorIterator<Element*> it(*elems);
    PaintInfo *paintInfo = parentRule->getPaintInfo();
    int index = 0 ;
    while(it.hasNext()) {
        Element *elm = it.next();
        if(indexOfThis == index) {
            elm->setVisibilityStateShow();
        } else if((index == previousIndex) || (index == nextIndex)) {
            elm->setVisibilityStateEllipsis();
            if(elm->ui->isExpanded()) {
                elm->ui->setExpanded(false);
            }
            QVectorIterator<Element*> it(elm->childItems);
            while(it.hasNext()) {
                Element *el = it.next();
                if(NULL != el->ui) {
                    el->ui->setHidden(true);
                }
            }
        } else {
            elm->setVisibilityStateHidden();
        }
        elm->displayWithPaintInfo(paintInfo);
        index ++ ;
    }
}

void Element::showBrothers()
{
    int indexOfThis = -1 ;
    int size = 0;
    QVector<Element*> *elems = NULL ;
    if(NULL == parentElement) {
        QVector<Element*> *items = parentRule->getChildItems();
        size = items->size();
        indexOfThis = items->indexOf(this);
        elems = items;
    } else {
        indexOfThis = parentElement->childItems.indexOf(this);
        size = parentElement->childItems.size();
        elems = this->parentElement->getChildItems();
    }
    if((indexOfThis < 0) || (indexOfThis >= size))
        return ;

    QVectorIterator<Element*> it(*elems);
    PaintInfo *paintInfo = parentRule->getPaintInfo();
    int index = 0 ;
    while(it.hasNext()) {
        Element *elm = it.next();
        if(elm->isVisibilityStateEllipsis()) {
            QVectorIterator<Element*> it(elm->childItems);
            while(it.hasNext()) {
                Element *el = it.next();
                if(NULL != el->ui) {
                    el->ui->setHidden(false);
                }
            }
        }
        elm->resetVisibilityState();
        if(elm->ui->isHidden()) {
            elm->ui->setHidden(false);
        }
        elm->displayWithPaintInfo(paintInfo);
        index ++ ;
    }
}

bool Element::isNormalViewState()
{
    return visibilityState == EVN_NORMAL ;
}

void Element::resetVisibilityState()
{
    visibilityState = EVN_NORMAL ;
}

void Element::setVisibilityStateEllipsis()
{
    visibilityState = EVN_ELLIPSIS ;
}

void Element::setVisibilityStateHidden()
{
    visibilityState = EVN_HIDDEN;
}

void Element::setVisibilityStateShow()
{
    visibilityState = EVN_SHOW ;
}

bool Element::isVisibilityStateEllipsis()
{
    return visibilityState == EVN_ELLIPSIS ;
}

QList<Attribute*>Element::getAttributesList()
{
    QList<Attribute*>attrList;
    attrList = attributes.toList();
    return attrList ;
}

Attribute* Element::getAttribute(const QString &attributeName)
{
    foreach(Attribute * attribute, attributes) {
        if(attribute->name == attributeName) {
            return attribute;
        }
    }
    return NULL ;
}


void Element::setZoomFactor(const int zoom)
{
    if(NULL != attrFont) {
        attrFont->setPointSize(VStyle::getZoomFontSize(defaultAttrFonts, zoom));
    }
    int newSize = VStyle::getZoomFontSize(fixedSizeAttrFonts, zoom) ;
    if(newSize > 0) {
        fixedWidthFont.setPointSize(VStyle::getZoomFontSize(fixedSizeAttrFonts, zoom));
    }
}


int Element::textSize()
{
    int size = 0;
    QVectorIterator<TextChunk*> tt(textNodes);
    while(tt.hasNext()) {
        TextChunk   *tx = tt.next();
        size += tx->text.length();
    }
    return size;
}

void Element::recalcSize(const bool isRecursive)
{
    int sizeOfData ;
    selfInfo.reset();
    if(isRecursive) {
        childrenInfo.reset();
    }

    selfInfo.numElements = childItems.size();

    switch(type) {
    default:
    case ET_ELEMENT: {
        // appends itself
        sizeOfData = textSize();
        selfInfo.totalSize += tag().length() * 2 + 5;
        selfInfo.totalSize += sizeOfData ;

        //itera sulla lista e prendi i valori dalla chiabe
        QVectorIterator<Attribute*>  attrs(attributes);
        while(attrs.hasNext()) {
            Attribute* attribute = attrs.next();
            selfInfo.totalSize += attribute->name.length() * 2 + 5;
            selfInfo.totalSize += attribute->value.length() ;
        }

        //append children
        if(isRecursive) {
            foreach(Element * value, childItems) {
                value->recalcSize(true);
                collectChildInfo(value, true);
            }
        }
    }
    break;

    case ET_PROCESSING_INSTRUCTION: {
        sizeOfData = getPITarget().length() + getPIData().length();
        selfInfo.totalSize = 5 + sizeOfData ;
    }
    break;

    case ET_COMMENT: {
        sizeOfData = getComment().length();
        selfInfo.totalSize = 7 + sizeOfData ;
    }
    break;
    case ET_TEXT:
        sizeOfData = text.length();
        selfInfo.totalSize = sizeOfData ;
        break;
    }
}

void Element::recalcChildSize()
{
    childrenInfo.reset();
    selfInfo.numElements = childItems.size();

    switch(type) {
    default:
        break;
    case ET_ELEMENT: {
        foreach(Element * child, childItems) {
            collectChildInfo(child, true);
        }
    }
    break;
    }
}


void Element::propagateChildInfoChange()
{
    if(parentRule->collectSizeData()) {
        if(NULL != parentElement) {
            parentElement->recalcChildSize();
            parentElement->displayWithPaintInfo(parentRule->getPaintInfo());
            parentElement->propagateChildInfoChange();
        }
    }
}

void Element::collectChildInfo(Element *child, const bool isAdd)
{
    if(isAdd) {
        childrenInfo.numElements += child->selfInfo.numElements;
        childrenInfo.totalSize += child->selfInfo.totalSize;
        childrenInfo.numElements += child->childrenInfo.numElements;
        childrenInfo.totalSize += child->childrenInfo.totalSize;
    } else {
        childrenInfo.numElements -= child->selfInfo.numElements;
        childrenInfo.totalSize -= child->selfInfo.totalSize;
        childrenInfo.numElements -= child->childrenInfo.numElements;
        childrenInfo.totalSize -= child->childrenInfo.totalSize;
    }
}


void Element::removeChildInfo(Element *childToRemove)
{
    if((parentRule != NULL) && parentRule->collectSizeData()) {
        collectChildInfo(childToRemove, false);
        propagateChildInfoChange();
    }
}

void Element::addChildInfo(Element *child)
{
    if((parentRule != NULL) && parentRule->collectSizeData()) {
        collectChildInfo(child, true);
        propagateChildInfoChange();
    }
}


void Element::updateSizeInfo()
{
    if((parentRule != NULL) && parentRule->collectSizeData()) {
        recalcSize(false);
        propagateChildInfoChange();
    }
}

void Element::incrementSizeInfo(const int size)
{
    selfInfo.totalSize += size ;
}

QString Element::tag()
{
    return _tag;
}

void Element::namespaceOfElement(QString &elNamespace, QString &elLocalName)
{
    QStringList ns = _tag.split(':');
    if(ns.length() < 2) {
        elNamespace = "" ;
        elLocalName = _tag;
    } else {
        elNamespace = ns.at(0);
        elLocalName = ns.at(1);
    }
}

QString Element::transformToText()
{
    QDomDocument    document;
    QString text ;
    if(generateDom(document, document)) {
        text = document.toString(4);
    }
    return text ;
}

int Element::indexOfSelfAsChild()
{
    if(NULL != parentElement) {
        return parentElement->childItems.indexOf(this);
    } else {
        if(NULL != parentRule) {
            return parentRule->indexOfTopLevelItem(this);
        } else {
            return -1 ;
        }
    }
}

bool Element::areChildrenLeavesHidden(QTreeWidgetItem *twi)
{
    if(NULL == twi) {
        return false;
    }
    QVariant res = twi->data(0, ShowDataRole);
    return res.toBool();
    //return twi->data(0, ShowDataRole).toBool();
}

void Element::setChildrenLeavesHiddenState(QTreeWidgetItem *twi, const bool newState)
{
    if(NULL != twi) {
        QVariant vState(newState);
        twi->setData(0, ShowDataRole, vState);
        //twi->setData(0, ShowDataRole, QVariant(newState));
    }
}

bool Element::hasElementInChildren()
{
    foreach(Element * child, getItems()) {
        if(child->getType() == Element::ET_ELEMENT) {
            return true ;
        }
    }
    return false;
}

bool Element::isALeaf()
{
    return !hasElementInChildren();
}

void Element::hideChildrenLeaves()
{
    setChildrenLeavesHiddenState(ui, true);
    if(!hasChildren()) {
        if(NULL != ui) {
            ui->setHidden(true);
        }
    } else {
        displayWithPaintInfo(parentRule->getPaintInfo());
    }
    foreach(Element * theChild, childItems) {
        theChild->hideChildrenLeaves();
    }
}

void Element::showChildrenLeaves()
{
    setChildrenLeavesHiddenState(ui, false);
    if(!hasChildren()) {
        if(NULL != ui) {
            ui->setHidden(false);
        }
    } else {
        displayWithPaintInfo(parentRule->getPaintInfo());
    }
    foreach(Element * theChild, childItems) {
        theChild->showChildrenLeaves();
    }
}

//returns the position of the element
QList<int> Element::indexPath()
{
    QList<int> list ;

    Element *parentE = parentElement;
    Element *target = this ;
    while(parentE != NULL) {
        list.prepend(parentE->childIndex(target));
        target = parentE ;
        parentE = parentE->parentElement ;
    }
    list.prepend(parentRule->getChildItems()->indexOf(target));

    return list ;
}

QStringList Element::path()
{
    QStringList list ;

    Element *parentE = parentElement;
    while(parentE != NULL) {
        list.prepend(parentE->tag());
        parentE = parentE->parentElement ;
    }
    return list ;
}

bool Element::copyTextNodesToTarget(Element *target)
{
    if(NULL != target) {
        copyHeaderAndDirectNodes(*target);
        QList<Element*> elementsToRemove;
        QList<Element*> elementsToMaintain;

        foreach(Element * e, target->childItems) {
            if(e->getType() == Element::ET_TEXT) {
                elementsToRemove.append(e);
            } else {
                elementsToMaintain.append(e);
            }
        }
        foreach(Element * e, elementsToRemove) {
            e->autoDelete(true);
            // delete it from the user interface too
        }
        // now the second list has elements only, no text nodes.
        int index = 0 ; // This is the index of the second list.
        int sourceIndex = 0 ; // This is the index of the second list.

        foreach(Element * e, childItems) {
            if(e->getType() == Element::ET_TEXT) {

                Element *textElement = new Element(target->getParentRule(), Element::ET_TEXT, target);
                textElement->setTextOfTextNode(e->text, e->isCDATA()) ;
                textElement->markEdited();
                target->getItems().insert(sourceIndex, textElement);
                textElement->caricaFigli(target->getUI()->treeWidget(), target->getUI(), target->getParentRule()->getPaintInfo(), true, sourceIndex);
            }
            index ++;
        } // foreach
        target->display(target->getUI(), target->getParentRule()->getPaintInfo());
        // Now, check if the elements correspond between the two implementations
        int numChildrenSource = getChildItemsNumber();
        int numChildrenTarget = target->getChildItemsNumber();
        if(numChildrenSource  != numChildrenTarget) {
            return false;
        }

        // Now, make sure that the new list is the current one for the target element.
        for(int i = 0 ; i < numChildrenSource ; i ++) {
            Element * sourceElement = childItems.at(i) ;
            Element * targetElement = target->childItems.at(i) ;
            // check for synchronization
            if((sourceElement->getType() != targetElement->getType())
                    || (sourceElement->tag() != targetElement->tag())
                    || (sourceElement->text != targetElement->text)) {
                return false;
            }
        } // for children
        return true;
    }
    return false;
}

QString Element::makeCSVFromSiblings()
{
    Element *theParent = parent() ;

    if(NULL == theParent) {
        QVector<Element*> list ;
        list.append(this);
        return insertAttributesListInCSV(list);
    } else {
        return insertAttributesListInCSV(theParent->childItems);
    }
}

void Element::insertAttributesInMap(QMap<QString, QString> &attributesNameMap)
{
    foreach(Attribute * attribute, attributes) {
        if(!attributesNameMap.contains(attribute->name)) {
            attributesNameMap.insert(attribute->name, _tag);
        }
    }
}


void Element::insertAttributesValuesInMap(QHash<QString, QString> &valuesMap)
{
    foreach(Attribute * attribute, attributes) {
        valuesMap.insert(attribute->name, attribute->value);
    }
}

QString Element::insertAttributesInCSV(QMap<QString, QString> &attributesNameMap)
{
    QHash<QString, QString> valuesMap;
    insertAttributesValuesInMap(valuesMap);

    QString result = "" ;
    foreach(QString key, attributesNameMap.keys()) {
        QString value = valuesMap.value(key);
        if(!valuesMap.contains(key)) {
            value = "";
        } else {
            value = valuesMap.value(key);
        }
        result += Utils::valueStringCSV(value, false);
    }
    result += "\n";
    return result ;
}

QString Element::insertAttributesListInCSV(QVector<Element*> &elements)
{
    QMap<QString, QString> attributesNameMap;
    QString result = "";
    // 1- get the titles
    foreach(Element * element, elements) {
        if(element->getType() == ET_ELEMENT) {
            element->insertAttributesInMap(attributesNameMap);
        }
    }
    // 2 - build the titles row
    result += tr("element");
    foreach(QString title, attributesNameMap.keys()) {
        result += Utils::separatorStringCSV();
        result += title ;
    }
    result += "\n";

    // 3- get the data
    foreach(Element * element, elements) {
        if(element->getType() == ET_ELEMENT) {
            result += Utils::valueStringCSV(element->tag(), true);
            result += element->insertAttributesInCSV(attributesNameMap);
        }
    }
    return result;
}

void Element::addElementChildrenInList(QList<Element*> &childrenList)
{
    foreach(Element * child, childItems) {
        childrenList.append(child);
    }
}

//----------------------------------------------------------------

Attribute::Attribute()
{
}

Attribute::Attribute(const QString & newName, const QString & newValue)
{
    name = newName;
    value = newValue ;
}


Attribute::~Attribute()
{
}


