/**************************************************************************
 *  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_REGOLA_H
#define QXMLEDITWIDGET_REGOLA_H

#include <QAbstractItemModel>
#include <QtXml/QDomDocument>
#include <QtXml/QDomNode>
#include <QVariant>
#include <QtGui>
#include <QAbstractItemModel>

#include "libQXmlEdit_global.h"
#include "UIDelegate.h"

#include "element.h"
#include "bookmark.h"
#include "paintinfo.h"

class Element;
class DocumentType;


class LIBQXMLEDITSHARED_EXPORT Regola : public QAbstractItemModel
{
    Q_OBJECT
    Q_PROPERTY(bool modified READ isModified WRITE setModified)

    friend class TestXsd;

    static const int ModelName;
    static const int ModelValue;
    static const int ModelColumns;

    // constants
    enum EConsts {
        // undo limit
        UndoLimitCount = 10
    };

    bool _useMixedContent;
    QUndoStack _undoStack;
public:
    typedef bool (*EditHook)(QWidget *const parentWindow, QTreeWidgetItem *item, UIDelegate *uiDelegate, Element* element);
    typedef bool (*EditTextHook)(QWidget *const parentWindow, QTreeWidgetItem *item, UIDelegate *uiDelegate, Element* element);
private:
    // This hook will throw an error, when used.
    EditHook _editHook ;
    EditTextHook _editTextHook ;

public:
    Regola(QDomDocument &document, const QString &name, const bool useMixedContent = false);
    Regola();
    ~Regola();

    //---------------------------------- Model Interface
    QVariant data(const QModelIndex &index, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
    //QVariant itemData(const QModelIndex &index) const;
    virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    QModelIndex index(int row, int column, const QModelIndex &parent) const;
    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QModelIndex parent(const QModelIndex &child) const;
    //bool hasChildren(const QModelIndex &index = QModelIndex()) const;

    //---------------------------------- end Model Interface

    bool isUseMixedContent();
    void setUseMixedContent(const bool value);

    QString docType();
    void setDocType(const QString& newDocType, const QString &systemId, const QString &publicId);

    void setPaintInfo(PaintInfo *newValue);
    PaintInfo *getPaintInfo();

    void setEditHook(EditHook theEditHook);
    void setEditTextHook(EditTextHook theEditTextHook);

    void caricaValori(QTreeWidget *pTree);
    bool isEmpty(const bool isRealElement);
    Element  *root() const;
    void setRootElement(Element *newRoot);
    bool write(const QString &filePath);
    bool write(const QString &filePath, const bool isMarkSaved);
    QByteArray writeMemory();
    QString getAsText();

    Element *newElement();
    Element *newElement(const QString &tagName);
    Element *newElement(const Element::ElType newType);

    void setModified(const bool state);
    bool isModified() const;
    void setEmptyRoot();
    void expand(QTreeWidget *tree);

    QString &fileName();
    void setFileName(const QString &newFileName);

    void addChild(QWidget *window, QTreeWidget *tree);
    void addChild(QWidget *window, QTreeWidget *tree, Element *preElement);
    Element* addChildToElement(QWidget *window, QTreeWidget *tree, Element *parentElement, const QString &elementTag, const bool useUndo = false);
    void addBrother(QWidget* window, QTreeWidget *tree);
    void addBrother(QWidget *window, QTreeWidget *tree, Element* theNewElement);
    void paste(QWidget *window, QTreeWidget *tree, Element *pasteElement);
    void pasteInternals(QTreeWidget *tree, Element *parentElement, Element *pasteElement, const int position);
    void pasteNoUI(Element *pasteElement, Element *pasteTo);
    void insertInternal(QTreeWidget *tree, Element *parentElement, Element *pasteElement, const int position);

    void redisplay();
    void redisplayElement(Element *element);
    void markSaved();
    bool editElement(QWidget * const parentWindow, QTreeWidgetItem *item, UIDelegate *uiDelegate) ;
    bool editInnerXMLBase64Element(QTreeWidgetItem *item, UIDelegate *uiDelegate);
    bool editInnerXMLElement(QTreeWidgetItem *item, UIDelegate *uiDelegate);
    bool editTextNodeElementBase64(QWidget *const parentWindow, QTreeWidgetItem *item, UIDelegate *uiDelegate);
    bool editAndSubstituteTextInNodeElement(QWidget *const parentWindow, Element *pElement, UIDelegate *uiDelegate);
    bool editEntry(QWidget * const parentWindow, const QString &title, const QString &label, const QString &actualText, QString &result);

    bool addTopElement(Element *theNewElement, const int position = -1);
    void notifyDeletionTopElement(Element *pEL);
    void appendComment(QWidget *window, QTreeWidget *tree);
    void appendComment(QWidget *window, QTreeWidget *tree, Element *newComment);
    void addComment(QWidget *window, QTreeWidget *tree);
    void addComment(QWidget *window, QTreeWidget *tree, Element *newComment);
    void appendProcessingInstruction(QWidget *window, QTreeWidget *tree);
    void appendProcessingInstruction(QWidget *window, QTreeWidget *tree, Element *newPI);
    void addProcessingInstruction(QWidget *window, QTreeWidget *tree);
    void addProcessingInstruction(QWidget *window, QTreeWidget *tree, Element *newPI);

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

    static QBrush hiliteBkBrush;

    void findText(FindTextParams &findArgs, Element *selectedItem);
    void unhilite();

    void takeOutElement(Element*element);
    void addHilite(Element *element);
    void unhiliteAll();

    // bookmarks
    void clearBookmarks();
    void addBookmark(Element* element);
    void removeBookmark(Element* element);
    void toggleBookmark(Element *element);
    bool isBookmarked(Element* element);

    Element *gotoPreviousBookmark();
    Element *gotoNextBookmark();

    int bookmarkSize();
    int nextBookmark();
    int previousBookmark();
    void handleSelection(Element *element);
    void notifyUIDeleted(Element *element);
    void takeoutFormBookmarks(Element *element);

    void deleteBookmarked();
    void cleanBookmarkedContents() ;

    bool loadXplore(const QString &fileName);

    QString documentXsd() {
        return _documentXsd ;
    }
    QString userDefinedXsd() {
        return _userDefinedXsd;
    }
    void setUserAndDocumentXsd(const QString &newXsd);

    void setUserDefinedXsd(const QString &path);
    void pasteXML(QWidget *window, QTreeWidget *tree, Regola *newRegola) ;

    bool collectSizeData();
    void setCollectSizeData(const bool isCollect);
    void assignCollectSizeDataFlag(const bool isCollect);

    QString addNameToPool(const QString &inputString);
    QString getAttributeString(const QString &attributeName);
    bool isValidXsd();
    void transformInComment(QWidget *window, QTreeWidget *tree, Element *elementToTransform);
    bool generateFromComment(QTreeWidget *tree, UIDelegate *uiDelegate, Element *elementToTransform);
    int indexOfTopLevelItem(Element *element);
    bool hasMoreThanOneXsdFile();

    Element *findChildElementByArray(Element *element, QList<int> &selection, const int listPos);
    Element *findElementByArray(QList<int> &selection);

    enum EInsSchemaRefInfo {
        INSERT_SCHEMA_ATTR_NOERROR = 0,
        INSERT_SCHEMA_ATTR_ERROR_NOROOT,
        INSERT_SCHEMA_ATTR_INFO_SCHEMAPRESENT,
        INSERT_SCHEMA_ATTR_INFO_REFPRESENT
    };
    EInsSchemaRefInfo insertNoNamespaceSchemaReferenceAttributes();
    EInsSchemaRefInfo insertSchemaReferenceAttributes();

    QString textOfCantEditMixedContentElementText();
    void hideLeafNodes();
    void showLeafNodes();

    bool moveUp(Element *element);
    bool moveDown(Element *element);
    bool internalMoveUp(Element *element, const bool registerInUndo);
    bool internalMoveDown(Element *element, const bool registerInUndo);

    void redo();
    void undo();
    bool canUndo();
    bool canRedo();
    int undoCount();

    bool checkIfUIItemsCorrespondToElements(Element *target);

    bool deleteElement(Element* element);
    bool deleteElement(Element* element, const bool isAutoDelete);

    void emptyUndoStack();
    void dumpDocument(QDomDocument &document);

signals:
    void wasModified();
    void undoStateChanged();
    void uiDeleted(Element *element);
    void docTypeChanged(const QString &docType);

private:
    Element     *rootItem;
    // processing instructions and so on
    QVector<Element*> childItems;
    QString     xmlFileName;
    bool        modified;
    QHash<int, Element*> selection;
    Bookmarks   bookmarks;
    PaintInfo   *paintInfo;


    /**
      XSD file referenced in the document (if any)
      */
    QString     _noNameSpaceXsd;
    QString     _documentXsd ; // see the note about xsd handling
    // list of namespaces - namespace name e.g.: <xsi, http://www.w3.org/2001/XMLSchema-instance>
    QHash<QString, QString> _namespacesByPrefixAndName;
    // the inverse of the previous one, <http://www.w3.org/2001/XMLSchema-instance, xsi>
    QHash<QString, QString> _namespacesByNameAndPrefix;
    // list of schemas - location e.g.: <http://www.w3.org/2001/XMLSchema-instance, xxx.xsd>
    QHash<QString, QString> _schemaLocationsByNamespace;
    QString     _userDefinedXsd;
    /**
      collect or not size informations about elements while editing
      */
    bool        _collectSizeData;
    QMap<QString, int>   _namesMap;
    QSet<QString>   _namesPool;
    //QList<QString> _namesPool;
    QSet<QString>   _attributeValuesPool;
    DocumentType *_docType;

    void clear();
    void clearUndo();
    void addUndoInsert(QTreeWidget * tree, Element * element);
    QTreeWidgetItem *getSelItem(QTreeWidget *tree);
    bool editNodeElementAsXML(const bool isBase64Coded, Element *pElement, UIDelegate *uiDelegate);
    bool editNodeElement(QWidget *const parentWindow, Element *pElement);
    bool editNodeComment(QWidget * const parentWindow, Element *pElement);
    bool editTextNodeElement(QWidget *const parentWindow, const bool isBase64Coded, Element *pElement);
    void recalcSize();

    void getBookmarkedElements(QVector<Element*> &elementsBookmarked);

    void checkValidationReference();
    void decodeASchema(const QString &value);

    int setTopLevelItemLike(Element *newElement, Element* oldElement);
    int swapAndDeleteElement(Element *newElement, Element* oldElement);

    void insertElementInternal(Element *theNewElement, Element *parentElement, QTreeWidget *tree);
    void insertElementInternal(Element *theNewElement, Element *parentElement, QTreeWidget *tree, const bool useUndo);
    void appendElementInternal(Element *theNewElement, Element *brotherElement, QTreeWidget *tree, const bool useUndo = true);
    void afterInsertHousekeeping(Element *theNewElement, QTreeWidget *tree);
    void afterInsertHousekeeping(Element *theNewElement, QTreeWidget *tree, const bool useUndo);
    void assignMixedContentText(Element *parent, const QString &text, const bool isCData, QVector<Element*> *collection);
    Element *assegnaValori(QDomNode &node, Element *parent, QVector<Element*> *collection);
    bool addUndoDeleteElement(Element* element);
    bool editAndSubstituteTextInNodeElementInternal(QWidget *const parentWindow, Element *pElement, UIDelegate *uiDelegate);
    void housework();
    void processDocument(QDomDocument &document);
    QDomDocument createNewDocument();
};

// external edit
bool EditCommentNode(QWidget * parent, Element *pTarget);
bool editProcessingInstruction(QWidget * const parentWindow, Element *pElement);

#endif // QXMLEDITWIDGET_REGOLA_H
