/***************************************************************************
 *   Copyright (C) 2005 by Thierry CHARLES                                 *
 *   thierry@les-charles.net                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _TDOCUMENT_H_
#define _TDOCUMENT_H_

#include <deque>
#include <set>
//#include <string>
#include <iostream>

#include <wx/string.h>

#include "lib/commons.h"
#include "lib/lib_string.h"
#include "components/codeeditor/tdocaction.h"
#include "components/codeeditor/tpoint.h"
#include "components/framework/translation.h"

class TDocumentListener;

/**
 * @short Document représentant le code source a afficher dans l'éditeur
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TDocument : virtual public TranslatableCmp
{
    TRANSLATABLE;

    public:
        /** unités élémentaires de mesure dans un document */
        enum TUnit{Character, Word};

        TDocument();
        virtual ~TDocument();

        /** insère un caratère à la position indiquée
         * @return le nb de caractères insérés
         */
        int insertAt(wxChar c, uint iLine, uint iColumn);
        /**
         * insère une chaine de caratères (null terminated string) à la position indiquée.
         * @return le nb de caractères insérés
         */
        int insertAt(const wxChar * sz, uint iLine, uint iColumn);

        /** supprime un caractère dans le document */
        void removeCharAt(const TPoint & pt) { this->removeCharAt(pt.y,pt.x); }
        /** supprime un caractère dans le document */
        void removeCharAt(uint iLine, int iColumn);
        /** supprime une portion du document (le plus grand est exclu) */
        void removeRange(const TPoint & pt1, const TPoint & pt2);

        /** renvoie une référence inaltérable sur une ligne */
        const wxString & getLine(uint iLine) const;
        /** renvoie le nombre de lignes dans le document */
        uint getLinesCount() const;
        /** renvoie la longueur d'une ligne */
        uint getLineLength(uint iLine) const;
        /** renvoie le contenu de tout le document */
        wxString getFullText() const { return this->getTextBetween(TPoint(0,0),TPoint(0,this->getLinesCount())); }
        /** renvoie une portion du document sous forme d'une seule chaine de caracteres (pt2 est exclu) */
        wxString getTextBetween(const TPoint & pt1, const TPoint & pt2) const;
        /** renvoie le charactère situé à la position indiquée */
        char getCharAt(const TPoint & pos) const { return this->getLine(pos.y).GetChar(pos.x); }

        /**
         * calcule une position dans le document a partir d'une autre
         * @param ptFrom point de départ du calcul de position
         * @param iSens direction du calcul (>= 0 vers l'avant) et nombre d'itérations
         * @param unit l'unité du déplacement : un caractère ou un mot
         */
        TPoint getPosition(const TPoint & ptFrom, int iSens, TUnit unit);

        /** vide le contenu du document */
        void clear();

        /** renvoie la taille du contenu de l'éditeur (équivalent à la taille si on écrivait le fichier sur disque) */
        uint getContentSize();

        /** charge un fichier apres avoir vidé le document */
        bool loadFile(const wxString & sFileName);
        /** charge du texte depuis un flux (sans vider le document) */
        void loadFromStream(std::istream & stream);
        /** écrit le contenu du document dans un flux */
        void writeToStream(std::ostream & stream);
        /** écrit le contenu du document dans un fichier */
        bool writeFile(const wxString & sFileName);

        /**
         * défini la longueur du tampon d'actions pour les undo/redo
         * @param iUndoCapacity taille du tampon d'undo/redo (<0 pour illimité, 0 pour hors service)
         */
        virtual void setUndoBufferCapacity(int iUndoCapacity);
        /** Undo last action. Attention : réinitialise l'état d'action composée */
        virtual void undo();
        /** Redo last action */
        virtual void redo();
        /** détruit toutes les infos de undo/redo. Attention : réinitialise l'état d'action composée */
        virtual void clearUndoRedo();
        /**
         * démarre une action composée.
         * Une action composée est une action en regroupant d'autres plus petites pour
         * qu'un undo les annule toutes en une seule fois.
         */
        virtual void startComposedAction();
        /**
         * termine une action composée.
         * si il y a eu plusieurs appels a startComposedAction, il en faut autant a stopComposedAction
         */
        virtual void stopComposedAction();


        /** ajoute un ecouteur */
        virtual bool addDocumentListener(TDocumentListener * l);
        /** enleve un ecouteur */
        virtual bool removeDocumentListener(TDocumentListener * l);

        /** envoie un évenement indiquant que le contenu du document a changé */
        virtual void fireDocumentChanged();
        /** envoie un évenement indiquant l'insertion de texte */
        virtual void fireDocumentTextInserted(const TPoint & ptFrom, const TPoint & ptTo);
        /** envoie un évenement indiquant la suppression de texte */
        virtual void fireDocumentTextRemoved(const TPoint & ptFrom, const TPoint & ptTo);
        /** envoie un évenement indiquant de gros changements dans le document */
        virtual void fireDocumentHeavilyModified();

        /** recherche la ligne la plus longue et renvoie sa taille */
        virtual long calcMaxLineLength() const;

        /** recherche le texte passe en parametre dans le document a partir de la position ptFrom */
        virtual TPoint find(wxString s, const TPoint & ptFrom, bool bCaseSensitive);

    protected:
        /** renvoie une référence altérable sur une ligne */
        wxString & _getLineRef(uint iLine);
        /** renvoie un itérateur sur une ligne */
        TStringList::iterator getLineIterator(uint iLine);
        /** renvoie un itérateur sur une ligne */
        TStringList::const_iterator getConstLineIterator(uint iLine) const;
        /** ajoute une action a la pile des actions undo/redo */
        virtual void addAction(TDocAction * action);

    private:
        TStringList textData;
        /** indique si les actions sont ajoutées à la pile undo(true) ou redo(false) */
        bool bModeUndo;
        bool bDoingRedo;
        int iUndoCapacity;
        TComposedDocAction * currentComposedAction;
        int iComposedActionStack;

        typedef std::set<TDocumentListener *> TDocumentListenersList;
        /** liste des ecouteurs */
        TDocumentListenersList listeners;

        /** piles d'actions pour l'undo/redo */
        TDocActionsList undoActions;
        TDocActionsList redoActions;
};

/**
 * @short Definition d'un écouteur de modifications de document
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TDocumentListener
{
    public :
        virtual ~TDocumentListener() {}
        /** signale un changement dans le document. Cet évènement intervient systématiquement après un autre plus spécialisé (documentTextInserted ou documentTextRemoved) */
        virtual void documentChanged(TDocument * doc) = 0;
        /** signale l'insertion de texte dans le document. puis appelle documentChanged */
        virtual void documentTextInserted(TDocument * doc, const TPoint & ptFrom, const TPoint & ptTo) = 0;
        /** signale la suppression de texte dans le document. puis appelle documentChanged */
        virtual void documentTextRemoved(TDocument * doc, const TPoint & ptFrom, const TPoint & ptTo) = 0;
        /** signale de grosses modifs sur le document. puis appelle documentChanged */
        virtual void documentHeavilyModified(TDocument * doc) = 0;
};


#endif // _TDOCUMENT_H_
