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

#include <wx/scrolwin.h>
#include <wx/string.h>
#include "components/stdgui/tlistctrl.h"

#include <deque>
#include <queue>
#include <map>

#include "lib/commons.h"

#include "components/stdgui/tbitmap.h"

#include "components/codeeditor/tdocument.h"
#include "components/codeeditor/tcursor.h"
#include "components/codeeditor/tselection.h"
#include "components/codeeditor/tenlighter.h"
#include "components/codeeditor/tstyle.h"

#include "components/framework/translation.h"

#define TCODEEDITOR_DEFAULT_SELECTION 0
#define TCODEEDITOR_OPEN_BRACKET_SELECTION 1
#define TCODEEDITOR_CLOSE_BRACKET_SELECTION 2

class TCodeEditorListener;
class TCodeEditorAutoCompletionSource;

DECLARE_EVENT_TYPE(EVENT_AUTO_COMPLETION, -1);

/**
 * @short Editeur de code
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TCodeEditor : public wxScrolledWindow, public TSelectionListener, public TCursorListener, public TDocumentListener, virtual public TranslatableCmp
{
    TRANSLATABLE;

    public:
        enum TypeMode { Insert, Overwrite };

        TCodeEditor(wxWindow* parent, wxWindowID id, const wxString& name = wxPanelNameStr);
        virtual ~TCodeEditor();

        /** calcule la taille optimale de la zone de saisie */
        wxSize DoGetBestSize() const;

        /**
         * redessine le contenu du composant
         */
        virtual void OnDraw(wxDC& dc);

        /**
         * donne les cordonnees des points extremes visible dans la fenetre de scroll
         * plus une marge pour eviter les effets de bord des flickers si bWithMarge == true
         */
        virtual void GetDrawableArea(int & iYMin, int & iYMax, bool bWithMarge = true);

        /**
         * gere l'insertion de caracteres
         */
        virtual void OnCharEvent(wxKeyEvent & _keyEvent);

        /**
         * gere les evenements clavier
         */
        virtual void OnKeyDownEvent(wxKeyEvent & _keyEvent);

        /**
         * gere les evenements souris
         */
        virtual void OnMouseEvent(wxMouseEvent & _mouseEvent);

        /** le composant recoit le focus */
        virtual void OnFocusIn(wxFocusEvent & e);
        /** le composant perd le focus */
        virtual void OnFocusOut(wxFocusEvent & e);

        /**
         * remplace le caractere sous le curseur et deplace celui-ci
         */
        virtual void overwrite(const wxChar c);

        /**
         * insere un caractere sous le curseur et deplace celui-ci
         */
        virtual void insert(const wxChar c);

        /**
         * insere une chaine de caracteres sous le curseur et deplace celui-ci
         */
        virtual void insert(const wxChar * sz);

        /**
         * revoie la position du curseur
         */
        virtual TCursor * getCursor();

        /**
         * deplace le curseur dans le sens indique
         */
        virtual void moveCursor(TDirection direction, uint iQuantity);

        /**
         * s'assure que le curseur est visible
         */
        virtual void ensureCursorIsVisible();

        /**
         * charge un fichier
         */
        virtual bool loadFile(const wxString &  sFileName);

        /**
         * ecris un fichier
         */
        virtual bool writeFile(const wxString &  sFileName);

        /**
         * efface le composant et son contenu
         */
        virtual void clear();

        /**
         * renvoie la hauteur d'une ligne
         */
        virtual int getLineHeight() const;

        /**
         * renvoie la largeur d'un caractere
         */
        virtual int getCharWidth() const { return this->iCharWidth; }

        /**
         * renvoie la position en Y de la ligne iLine
         */
        virtual int getLineYPos(uint iLine);

        /**
         * renvoie la position en X de la colone iColumn sur la ligne iLine
         */
        virtual int getColumnXPos(uint iLine,uint iColumn);

        /**
         * renvoie la ligne a la position indiquee en convertissant la position
         * iScreenY de l'ecran en position absolue (tiens compte du scroll)
         */
        virtual int getScreenLineAt(int iScreenY);

        /**
         * renvoie la ligne � la position absolue indiqu�e
         */
        virtual int getLineAt(int iYPos);

        /**
         * renvoie la colonne a la position indiquee en convertissant la position
         * iScreenX de l'ecran en position absolue en se basant sur le contenu de
         * la ligne iLine
         */
        virtual int getScreenColumnAt(int iLine, int iScreenX);

        /**
         * renvoie la colonne a la position absolue indiquee en se basant sur
         * le contenu de la ligne iLine
         */
        virtual int getColumnAt(int iLine, int iXPos);

        /**
         * suprime la selection et renvoie true.
         * Si la selection est invalide renvoie false et ne fait rien
         */
        virtual bool eraseSelection(int iSelection = TCODEEDITOR_DEFAULT_SELECTION);

        /**
         * gere le mouvement du curseur sur une pression de touche
         */
        virtual void requestCursorMovment(int iKeyCode, bool bControl, bool bAlt, bool bShift);

        /**
         * renvoie un pointeur sur le document utilise en interne
         * pour la gestion des donnees de l'editeur
         */
        TDocument * getDocument() { return &this->document; }

        /**
         * copie la selection dans le presse papier et l'efface
         */
        virtual void clipboardCut();

        /**
         * copie la selection dans le presse papier
         * @param bX11Primary copier dans le presse papier de la souris sous X11
         */
        virtual void clipboardCopy(bool bX11Primary = false);

        /**
         * colle le contenu du presse papier
         * @param bX11Primary coller a partir du presse papier de la souris sous X11
         */
        virtual void clipboardPaste(bool bX11Primary = false);

        /**
         * defini un colorateur syntaxique
         */
        virtual void setEnlighter(TEnlighter * enlighter);

        /**
         * renvoie le colorateur syntaxique (ne pas delete-er)
         */
        TEnlighter * getEnlighter() { return this->enlighter; }

        /** defini le style pour un ID donne */
        void setStyle(sbyte iStyleID, const TStyle & style);

        /** supprime le style pour un ID donne. Les styles par defaut ne peuvent pas etre supprimes */
        void removeStyle(sbyte iStyleID);

        /** Sets the font for this window. This function should not be called for the parent window if you don't want its font to be inherited by its children, use SetOwnFont instead in this case and see InheritAttributes for more explanations. */
        virtual bool SetFont(const wxFont& font);

        /**
         * annule la derniere modification
         */
        virtual void undo();

        /**
         * retabli la derniere modification annulee
         */
        virtual void redo();

        /**
         * defini le style de la selection
         * @param iStyle ID du style a appliquer a la s�lection (voir setStyle)
         * @param iSelection id de la selection a manipuler
         */
        void setSelectionStyle(const sbyte iStyleId, const int iSelection = TCODEEDITOR_DEFAULT_SELECTION);

        /**
         * renvoie le style de la selection
         * @param iSelection id de la selection a manipuler
         */
        sbyte getSelectionStyle(const int iSelection = TCODEEDITOR_DEFAULT_SELECTION);

        /**
         * defini la selection
         * @param selBegin point de depart de la selection
         * @param selEnd point de fin de la selection
         * @param iSelection id de la selection a manipuler
         */
        virtual void setSelection(const TPoint & selBegin, const TPoint & selEnd, const int iSelection = TCODEEDITOR_DEFAULT_SELECTION);

        /**
         * recupere la selection dans selBegin et selEnd
         * @param selBegin point de depart de la selection
         * @param selEnd point de fin de la selection
         * @param iSelection id de la selection a manipuler
         * @return true si la selection est valide, false dans le cas contraire
         */
        virtual bool getSelection(TPoint & selBegin, TPoint & selEnd, const int iSelection = TCODEEDITOR_DEFAULT_SELECTION) const;

        /** renvoie le texte de la selection */
        virtual wxString getSelectedText(const int iSelection = TCODEEDITOR_DEFAULT_SELECTION) const;

        /** invalide une selection */
        virtual void discardSelection(const int iSelection = TCODEEDITOR_DEFAULT_SELECTION);

        /** renvoie le mode de saisie (insertion / ecrasment) */
        virtual TypeMode getTypeMode() { return this->typeMode; }
        /** defini le mode de saisie (insertion / ecrasement) */
        virtual void setTypeMode(TypeMode typeMode);

        /** envoie un evenement indiquant que le mode de saisie a change */
        virtual void fireTypeModeChanged();
        /** ajoute un ecouteur a l'editeur */
        virtual bool addCodeEditorListener(TCodeEditorListener * cel);
        /** enleve un ecouteur a l'editeur */
        virtual bool removeCodeEditorListener(TCodeEditorListener * cel);

        /** active / d�sactive la mise en valeur des parenth�ses/accolades/crochets ... */
        void setBracketsHighlightingEnabled(bool b);
        /** indique si la mise en valeur des parenth�ses/accolades/crochets ... est active */
        bool isBracketsHighlightingEnabled() const { return this->bBracketsHighlightingEnabled; }

        /**
         * ajoute une paire de "brackets" a g�rer dans la mise en valeur
         * @param cOpen caract�re ouvrant
         * @param cClose caract�re fermant
         * @param bMultiPurpose indique que les carat�res de la paire pourraient etre autre chose que des brackets
         */
        void addBracketsPair(char cOpen, char cClose, bool bMultiPurpose = false);

        /** indique si la position pass�e en parametre est dans une zone de commentaires */
        bool isInCommentArea(const TPoint & pt) const;

        /** indique que les zones qui sont du style indiqu� sont des zones de commentaires */
        void declareCommentStyle(sbyte iStyleID);

        /** indique que les zones qui sont du style indiqu� ne sont pas des zones de commentaires */
        void undeclareCommentStyle(sbyte iStyleID);

        /** indente une ligne */
        void indentLine(uint iLine);
        /** d�sindente une ligne */
        void unindentLine(uint iLine);

        /**
         * defini la source des completions
         * @param src source des completions (NULL pour desactiver l'auto-completion)
         */
        void setAutoCompletionSource(TCodeEditorAutoCompletionSource * src) { this->autoCompletionSrc = src; }
        /**
         * renvoie la source des completions (eventuellement NULL si desactivee)
         */
        const TCodeEditorAutoCompletionSource * setAutoCompletionSource() const { return this->autoCompletionSrc; }

    protected:
        /**
         * dessine une selection avec le style demande
         */
        virtual void drawSelection(wxDC & dc, TSelection * sel, sbyte iStyleID);

        /**
         * dessine une ligne a partir de sa segmentation stylistique
         */
        virtual void drawLine(wxDC & dc, int iLine, const TLineEnlightenmentInformation & info, int iYPos, int iFirstCol = -1, int iLastCol = -1);

        /**
         * redessine toutes les selections
         */
        void redrawSelections(wxDC & dc, bool bDoClear);

    // ECOUTEURS
        /**
         * gestion des changements de selection ecoutees
         */
        virtual void selectionChanged(TSelection * sel);

        /**
         * nettoyage d'une selection (definitif ou avant un reaffichage)
         */
        virtual void clearSelection(TSelection * sel);

        /**
         * ecoute les mouvements du curseur
         */
        virtual void cursorMoved(TCursor * cursor);

        /** signale un changement dans le document. Cet evenement intervient systematiquement apres un autre plus specialise (documentTextInserted ou documentTextRemoved) */
        virtual void documentChanged(TDocument * doc);
        /**
         * signale l'insertion de texte dans le document.
         * rien ne doit etre fait qui puisse demander le contenu de l'enlightenment ici
         * car l'enlighter peut ne pas encore avoir recu l'�v�nement de texte ins�r�
         */
        virtual void documentTextInserted(TDocument * doc, const TPoint & ptFrom, const TPoint & ptTo);
        /**
         * signale la suppression de texte dans le document.
         * rien ne doit etre fait qui puisse demander le contenu de l'enlightenment ici
         * car l'enlighter peut ne pas encore avoir recu l'�v�nement de texte supprim�
         */
        virtual void documentTextRemoved(TDocument * doc, const TPoint & ptFrom, const TPoint & ptTo);
        /**
         * signale de grosses modifs sur le document. puis appelle documentChanged
         * rien ne doit etre fait qui puisse demander le contenu de l'enlightenment ici
         * car l'enlighter peut ne pas encore avoir recu l'�v�nement de texte modifi�
         */
        virtual void documentHeavilyModified(TDocument * doc);

        /** mets a jour la taille virtuelle du composant en fonction de la derni�re collecte d'infos */
        virtual void updateVirtualSize();

        /** effectue la mise en valeur des "brackets" � la position courrante du curseur */
        void highlightBrackets();

        /** recherche le caractere apparent� a ce caract�re de fermeture (0 si non trouv�) */
        char getOpeningBracketFor(char cClosingBracket);
        /** recherche le caractere apparent� a ce caract�re d'ouverture (0 si non trouv�) */
        char getClosingBracketFor(char cOpeningBracket);

        /** affiche le composant de completion */
        void startAutoCompletion();
        /** cache le composant de completion */
        void endAutoCompletion();
        /** valide la s�lection actuelle de l'auto completion */
        void onAutoCompletionEvent( wxCommandEvent& event );
        /** validation de la s�lection dans l'auto completion */
        void validateAutoCompletion();
        /** mets a jour la liste des completions affich�es */
        void updateCompletionsList();
        /** double clic dans l'auto completion */
        void onAutoCompletionDblClick(wxListEvent & event);
        /** touche press�e l'auto completion */
        void onAutoCompletionKeyDown(wxListEvent & event);


    private:

        int             iLineHeight;
        int             iCharWidth;
        int             iLastMaxLineLength;

        TDocument       document;

        TEnlighter *    enlighter;
        TEnlighter *    defaultEnlighter;
        TStylesMap      styles;

        TCursor *       cursor;
        TPoint          futureCursorPosition;

        TListCtrl *    autoCompletionCtrl;
        TCodeEditorAutoCompletionSource * autoCompletionSrc;

        TSelectionMap   selections;
        bool            bUpdatingDefaultSelection;

        bool            bLinuxSelectionToClipboard;

        bool            bRemovingText;

        TypeMode        typeMode;

        bool            bBracketsHighlightingEnabled;

        typedef std::map<char,char> TBracketsMap;
        TBracketsMap forwardBracketsMap;
        TBracketsMap backwardBracketsMap;
        typedef std::set<char> TCharSet;
        TCharSet multiPurposeBrackets;

        typedef std::set<int> TIntSet;
        TIntSet commentStyles;

        typedef std::set<TCodeEditorListener *> TCodeEditorListenerList;
        /** liste des ecouteurs */
        TCodeEditorListenerList listeners;

        DECLARE_EVENT_TABLE();
};

/**
 * @short Definition d'un ecouteur de modifications des parametres d'un composant d'edition de code
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TCodeEditorListener
{
    public :
        virtual ~TCodeEditorListener(){}
        /** indique que le mode de saisie a change */
        virtual void typeModeChanged(TCodeEditor * ce) = 0;
};

/**
 * @short Definition d'un fournisseur d'auto-completions
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TCodeEditorAutoCompletionSource
{
    public :
        virtual ~TCodeEditorAutoCompletionSource(){}
        /**
         * renvoie la liste des completions correspondant � un mot
         * @param sWordStart debut du mot a completer
         */
        virtual TStringList getCompletionsForWord(TCodeEditor * ce, const wxString & sWordStart) = 0;
};


#endif // _TCODEEDITOR_H_
