/**************************************************************************
 *  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                                            *
 **************************************************************************/
#ifndef QXMLEDITWIDGET_DOMITEM_H
#define QXMLEDITWIDGET_DOMITEM_H

#include <QtXml/QDomNode>
#include <QHash>
#include <QtGui>
#include <QRegExp>
#include "libQXmlEdit_global.h"

#include "paintinfo.h"

class Regola;
class FindTextParams;

class TextChunk
{
public:
    QString text;
    bool  isCDATA;

    TextChunk(const bool isCDATA, const QString &text);

    TextChunk *clone();
};

#define NO_ERROR (0)
#define ERROR_1 (1)
class QXmlException
{
public:
    QString cause;
    int code;
    QXmlException(const int newCode, const QString &theCause) {
        code = newCode ;
        cause = theCause ;
    }
};

class Attribute
{

public:
    QString name;
    QString value;

    Attribute();
    Attribute(const QString &newName, const QString &newValue);
    ~Attribute();
};

class ElementInfo
{
public:
    int numElements;
    int totalSize;

    ElementInfo();
    ~ElementInfo();

    void reset();
};

class LIBQXMLEDITSHARED_EXPORT Element
{

    Q_DECLARE_TR_FUNCTIONS(Element)

public:

    static const int MAX_LIMIT_TEXTLEN = 100;
    static const int ATTR_PER_COLUMN = 5;
    static const int WIDTH_LEN_CHARS = 6;
#define DEFAULT_FIXEDWIDTHFONT "Courier"

    enum EViewModes {
        E_VM_NONE,
        /**
          a base 64 text
        */
        E_VM_BASE64TEXT,
        /**
          Base 64 coded XML
         */
        E_VM_BASE64XML,
        /**
          an XML as payload (CData or not )
          */
        E_VM_XML,

    };
    // TODO uso di fields :1
//TODO private
    QString text;
    //QString nameSpace;
    QString _tag;
    QString tag();
    bool isShowTextBase64;
    bool wasOpen ;
    ElementInfo selfInfo;
    ElementInfo childrenInfo;
    bool _isCData;
    //bool _isEntityReference;

    static QFont fixedWidthFont;
    static QFont *attrFont ;
    static int defaultAttrFonts ;
    static int fixedSizeAttrFonts ;


    enum ElType { ET_ELEMENT, ET_PROCESSING_INSTRUCTION, ET_COMMENT, ET_TEXT};

    Element(const QString newTag, const QString &text, Regola *regola, Element *parent = 0);
    Element(Regola *regola, const ElType newType, Element *parent);
    Element(Regola *regola);
    ~Element();
    Element *child(int i);
    Element *parent();

    void namespaceOfElement(QString &elNamespace, QString &elLocalName);

    static QRegExp terminatorSearch;
    static bool firstTimeFixedWidthFont;
    static QString textCompactViewPrefix;

    void caricaFigli(QTreeWidget *pTree, QTreeWidgetItem *parent, PaintInfo *paintInfo, const bool isGUI = true, const int pos = -1);
    void display(QTreeWidgetItem *me, PaintInfo *paintInfo);
    void displayRecursive(PaintInfo *paintInfo);
    void displayWithPaintInfo(PaintInfo *paintInfo);
    void refreshUI();

    bool isEmpty() const;
    bool generateDom(QDomDocument &document, QDomNode &parent);

    void clearAttributes();
    void addAttribute(const QString &name, const QString &value);
    void addChild(Element *newChild);
    void addChildAt(Element *newElement, const int position);
    int addChildAfter(Element *newElement, Element *brotherElement);

    bool moveDown(Element *element);
    bool moveUp(Element *element);

    static bool moveDown(QVector<Element*> &items, Element *element);
    static bool moveUp(QVector<Element*> &items, Element *element);

    void autoDelete(const bool deleteMe);
    void autoDeleteRecursive();

    static Element *fromItemData(QTreeWidgetItem *item);
    static Element *fromModelIndex(const QModelIndex & index);

    Element* copyToClipboard();
    Element *cutToClipboard();
    Element* copyTo(Element &newElement);
    Element *copyTo(Element &newElement, const bool isRecursive);
    void copyHeader(Element &newElement);
    void copyHeaderAndDirectNodes(Element &newElement);

    QTreeWidgetItem *getUI() const {
        return ui;
    }
    bool isFirstChild() ;
    bool isLastChild() ;

    void expand(QTreeWidget *tree);
    void deleteUI();

    void setText(const QString &data);
    void setTextOfTextNode(const QString &data, const bool isCData);

    ElType getType() const {
        return type ;
    }
    QVector<Element*> *getChildItems() {
        return &childItems;
    }
    int getChildItemsNumber() {
        return childItems.size();
    }

    QVector<Element*> &getItems() {
        return childItems;
    }

    Element* getChildAt(const int childIndex);
    int childIndex(Element *child);

    QVector<TextChunk*> &getTextChunks() {
        return textNodes;
    }

    int getTextChunksNumber() {
        return textNodes.size();
    }

    bool hasChildren() {
        return !childItems.isEmpty();
    }

    const QString &getComment();
    void setComment(const QString & Comment);
    static void loadIcons();

    //Attributi
    // TODO consider using an array
    QVector<Attribute*> attributes;
    //QMap<QString*> attributeNames; TODO: for styles

    void addTextNode(TextChunk *text);
    void clearTextNodes();
    bool isElement() const {
        return ET_ELEMENT == type ? true : false ;
    }

    const QString &getPITarget();
    const QString getPIData();
    void setPITarget(const QString & target);
    void setPIData(const QString & data);

    bool findText(FindTextParams &findArgs);
    bool searchInScope(FindTextParams &findArgs);

    void unhilite();
    void hilite();

    long getInstanceId() const {
        return instanceId;
    }

    QTreeWidgetItem *goToPreviousBrother();
    QTreeWidgetItem *goToNextBrother();
    void isFirstOrLastChild(bool &isFirst, bool &isLast);
    QString getRow();
    Regola *getParentRule();

    //--- hide / show
    void hideBrothers();
    void showBrothers();
    bool isNormalViewState();
    void registerState();

    bool isShownBase64() const ;
    void setShownBase64(const bool shownAs);

    QList<Attribute*>getAttributesList();
    Attribute* getAttribute(const QString &attributeName);

    QString getAsText();
    QString getAsSimpleText(const bool isBase64);
    QString getAsSimpleTextXml(const bool isBase64);

    bool isCDATA();
    void setAsSingleTextNode(const QString &newText, const bool isBase64, const bool isCData);

    EViewModes viewMode() const ;
    void setViewMode(const EViewModes newMode) ;

    void markSavedRecursive();
    void markEditedRecursive();
    void markEdited();

    bool edited();
    bool saved();

    void recalcSize();
    int textSize();

    void updateSizeInfo();
    void modifiedChildInfo();
    void addChildInfo(Element *child);
    void removeChildInfo(Element *childToRemove);
    void recalcSize(const bool isRecursive);
    void incrementSizeInfo(const int size);
    int setItemLike(Element *newElement, Element* oldElement);
    QString transformToText();
    int indexOfSelfAsChild();
    bool isMixedContent() ;

    //returns the position of the element
    QList<int> indexPath();
    QStringList path();
    // copy the first level attributes, text nodes and attributes to the target
    bool copyTextNodesToTarget(Element *target);

    QString makeCSVFromSiblings();

private:
    static const int ShowDataRole;
    static long   instances;

    long    instanceId;
    Element *parentElement;
    Regola *parentRule;
    QTreeWidgetItem *ui;
    EViewModes _viewMode;
    bool _edited;
    bool _saved;

    void houseWork(Regola *regola, Element *parent);

    void zeroUI();
    void zeroUISelf(const bool emitMe);

    QVector<TextChunk*> textNodes;
    ElType  type;
    QVector<Element*> childItems;

    //------------------------------------------------------------
    enum EVisibilityState {
        EVN_NORMAL,
        EVN_SHOW,
        EVN_HIDDEN,
        EVN_ELLIPSIS
    };
    EVisibilityState visibilityState ;
    //------------------------------------------------------------

    static QBrush commentBrush;
    static QBrush defaultBrush;
    static QBrush procInstrBrush;
    static QBrush attrBrush;

    static bool isLoadedIcons;
    static QIcon procInstrIcon;
    static QIcon commentIcon;
    static QIcon elementIcon;
    static QIcon procInstrBkmIcon;
    static QIcon commentBkmIcon;
    static QIcon elementBkmIcon;
    static QIcon bookmarkIcon;
    static QIcon textIcon;
    static QIcon textBkmIcon;
    static QIcon childrenHiddenIcon;
    static QIcon childrenHiddenBkmIcon;
    static QIcon filteredAttributesIcon;

    void generateId();

    QTreeWidgetItem *findPreviousBrother(QVector<Element*> &items);
    QTreeWidgetItem *findNextBrother(QVector<Element*> &items);
    QVector<Element*> & getParentChildren();
    void findFirstAndLast(QVector<Element*> &items, bool &isFirst, bool &isLast);
    static QString formatRowNumber(const int index);
    QString topLevelItemIndex(QTreeWidgetItem *topLevelItem);
    void resetVisibilityState();
    void setVisibilityStateEllipsis();
    void setVisibilityStateHidden();
    void setVisibilityStateShow();
    bool isVisibilityStateEllipsis();
    void createFixedWidthFonts();
    QFont createElementFonts(QFont defaultFont);
    const QString styleElementTag(QTreeWidgetItem *me, PaintInfo *paintInfo);

    void collectChildInfo(Element *child, const bool isAdd);
    void propagateChildInfoChange();
    void recalcChildSize();

    static QString limitTextWithEllipsis(const QString &inputText);
    void autoDeleteRecursiveInner();

    void setChildrenLeavesHiddenState(QTreeWidgetItem *twi, const bool newState);

    void insertAttributesInMap(QMap<QString, QString> &attributesNameMap);
    void insertAttributesValuesInMap(QHash<QString, QString> &valuesMap);
    QString insertAttributesInCSV(QMap<QString, QString> &attributesNameMap);
    static QString insertAttributesListInCSV(QVector<Element*> &elements);

public:
    bool areChildrenLeavesHidden(QTreeWidgetItem *twi);

    static void setZoomFactor(const int zoom);
    void hideChildrenLeaves();
    void showChildrenLeaves();
    bool isALeaf();
    bool hasElementInChildren();

    const QString styleElementTagString(PaintInfo *paintInfo);
    QFont styleElementTagFont(PaintInfo *paintInfo);
    QBrush styleElementTagColor(PaintInfo *paintInfo);
    QIcon styleElementTagIcon();
    QVariant columnViewTooltipData();
    QVariant columnViewDisplayData(PaintInfo *paintInfo, const int rowOrdinal);

    void addElementChildrenInList(QList<Element*> &childrenList);

};

#endif //QXMLEDITWIDGET_DOMITEM_H
