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

#include <map>
#include <queue>
#include <deque>
#include <set>
#include <string>

#include <wx/gdicmn.h>
#include <wx/event.h>

#include "lib/commons.h"
#include "components/stdgui/tgenbuttonlistener.h"

#include "components/framework/xmlcfgcmp.h"
#include "components/framework/translation.h"

class TPanel;
class TBitmap;
class TApplicationWindow;
class TPanelElement;
class TApplicationPanelListener;
class wxMenu;
class wxStaticText;

/**
 * @short Un panneau principal dans une application
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TApplicationPanel : public TGenButtonListener , public XmlConfiguredComponent, virtual public TranslatableCmp, public wxEvtHandler
{
    TRANSLATABLE;
    protected:
        struct TPanelElementMeta
        {
            TPanelElementMeta(TPanelElement * _elt = NULL) : iDisplayOrder(0), bVisible(false), elt(_elt), btn(NULL), closeBtn(NULL), container(NULL), title(NULL) {}
            int iDisplayOrder;
            bool bVisible;
            TPanelElement * elt;
            TGenButton * btn;
            TGenButton * closeBtn;
            TPanel * container;
            wxStaticText * title;
        };
        friend bool operator>(const TPanelElementMeta & a, const TPanelElementMeta & b);
        typedef std::map<int,TPanelElementMeta> TPanelElementMap;
        typedef std::priority_queue<TPanelElementMeta,
                                    std::deque<TPanelElementMeta>,
                                    std::greater<TPanelElementMeta>
                                   > TPanelElementOrderedList;

    public :
        /** orientation generale d'un panneau */
        enum Orientation{Horizontal,Vertical};
        /** position du panneau par rapport a la fenetre a laquelle il appartient */
        enum Position{Top,Left,Right,Bottom,Center};
        /** mode d'affichage des paneaux de contenu :
            - Exclusive : seuls les panneaux "toujours visibles" et le dernier a avoir ete selectionne sont affiches
            - Shared : tant qu'on ne releve pas son bouton activateur, un panneau reste visible
            */
        enum DisplayMode{Exclusive,Shared};

        /**
         * constructeur du panel
         * @param iID les ID negatifs sont reserves au framework
         */
        TApplicationPanel(TApplicationWindow * _owner, int iID, Position pos)
            : owner(_owner), iPanelId(iID), displayMode(Exclusive), bContentPanelVisible(true),
              contentPanel(NULL), bButtonsBarVisible(true), subButtonsPanel(NULL), buttonsPanel(NULL),
              buttonsBackwardBtn(NULL), buttonsForwardBtn(NULL), buttonsShowBtn(NULL), lastButtonsMenu(NULL),
              composedPanel(NULL), iVisibleElements(0), bMightHaveNoVisibleElement(true),
              bUpdatingVisibleElements(false)
            { this->setPosition(pos); }

        /** tous les elements restants sont detruits */
        virtual ~TApplicationPanel();

        /**
         * Supprime un element du panneau
         * @param iEltId ID de l'element a supprimer
         * @param bAutoDestroy indique si le panneau doit se charger de la destruction de l'element
         * @return un pointeur sur l'element qui vient d'etre supprime ou NULL si celui-ci n'existait pas ou a ete detruit
         */
        TPanelElement * removeElement(const int iEltId, bool bAutoDestroy = true);

        /** renvoie un pointeur sur l'element identifie par iEltId ou NULL si celui-ci n'existe pas */
        TPanelElement * getElement(const int iEltId) const;

        /**
         * ajoute un element au panneau
         * @param elt element a rajouter
         * @param iDisplayOrder ordre d'affichage de l'element (-1 = en fin)
         */
        void addElement(TPanelElement * elt, int iDisplayOrder = -1);

        /** affiche ou cache un element */
        void setElementVisible(const int iId, bool bVisible);

        /** indique si un element est visible ou non */
        bool isElementVisible(const int iId) const;

        /** affiche/cache la barre de boutons */
        void setButtonsBarVisible(const bool bVisible);
        /** indique si la barre de boutons est visible */
        bool isButtonsBarVisible() const { return this->bContentPanelVisible; }

        /** affiche/cache le panneau principal */
        void setContentPanelVisible(const bool bVisible);
        /** indique si le panneau principal est visible */
        bool isContentPanelVisible() const { return this->bContentPanelVisible; }

        /** renvoie l'ID du composant */
        int getID() const { return this->iPanelId; }
        /** definit l'ID du composant */
        void setID(const int iID) { this->iPanelId = iID; }

        /** indique pour quelle position le panneau sera construit */
        Position getPosition() const { return this->position; }
        /** defini la position du panneau dans l'application. Ceci n'est qu'a titre indicatif pour la construction du panneau : ca ne le deplace pas */
        void setPosition(Position pos);

        /** indique l'orientation du panneau (definit par la position) */
        Orientation getOrient() const { return this->orientation; }

        /** renvoie l'application dont le panneau depend */
        TApplicationWindow * getOwner() const { return this->owner; }

        /** defini le mode d'affichage des elements */
        void setDisplayMode(DisplayMode mode) { this->displayMode = mode; }
        /** indique le mode d'affichage des elements */
        DisplayMode getDisplayMode() const { return this->displayMode; }

        /** defini si on peut cacher tous les elements */
        void setMightHaveNoVisibleElement(bool b) { this->bMightHaveNoVisibleElement = b; }
        /** indique si on peut cacher tous les elements */
        bool getMightHaveNoVisibleElement() const { return this->bMightHaveNoVisibleElement; }

        /** mets a jour la taille du panneau */
        void updateComposedPanelSize();

        /** mets a jour le bouton dans le panneau */
        void panelElementNameUpdated(const TPanelElement * elt);

        /** mets a jour le bouton dans le panneau */
        void panelElementTooltipUpdated(const TPanelElement * elt);

        /** mets a jour la taille du composant dans le panneau */
        void panelElementSizeUpdated(const TPanelElement * elt);

        /** mets a jour de l'icone du composant dans le panneau */
        void panelElementBitmapUpdated(const TPanelElement * elt);

        /**
         * renvoie le premier element marque comme visible
         * @param bMightBeAlwaysVisible indique si un element marque comme toujours visible peut etre retourne
         * @return NULL si aucun element correspondant aux criteres n'a ete trouve
         */
        TPanelElement * getFirstVisibleElement(bool bMightBeAlwaysVisible = false);

        /** indique le nombre d'elements dans le panneau */
        long getElementsCount() const { return this->elements.size(); }

        /** indique le nombre d'elements actuellement visibles */
        long getVisibleElementsCount() const { return this->iVisibleElements; }

        /** mets a jour l'ordre d'affichage d'un element */
        void setElementDisplayOrder(TPanelElement * elt, int iDisplayOrder, bool bUpdatePanel = true);

        /** renvoie l'element situe a un index */
        TPanelElement * getElementAt(long iIdx) const;

        /** charge les elements d'un panneau a partir des informations stockes sous forme XML */
        virtual bool loadElements(TiXmlElement * eltsNode);

        /** sauvegarde les elements d'un panneau en XML */
        TiXmlElement * getElementsNode();

        /** charge les parametres du composant a partir des informations contenues dans le noeud passe en parametre */
        virtual bool loadParameters(TiXmlElement * parametersNode);
        /** renvoie les parametres du composant sous la forme d'un noeud xml */
        virtual TiXmlElement * getParameters();

        /** renvoie le panneau des contenus actifs */
        TPanel * getContentPanel();
        /** renvoie le panneau compose apres l'avoir construit si necessaire */
        TPanel * getComposedPanel();
        /** renvoie le panneau compose apres l'avoir construit si necessaire */
        const TPanel * getComposedPanel() const { return this->composedPanel; };

        /** ajoute un ecouteur */
        virtual bool addPanelListener(TApplicationPanelListener * l);
        /** enleve un ecouteur */
        virtual bool removePanelListener(TApplicationPanelListener * l);
        /** envoie un evenement indiquant qu'un element va etre supprime */
        virtual void fireRemovingElement(TPanelElement * elt);
        /** envoie un evenement indiquant qu'un element est ajoute */
        virtual void fireAddingElement(TPanelElement * elt);
        /** envoie un evenement indiquant qu'un element devient visible / cache */
        virtual void fireElementVisibilityChanged(TPanelElement * elt, bool bVisible);
        /** envoie un evenement que les donnees d'un element ont change */
        virtual void fireElementDataChanged(const TPanelElement * elt);


    protected :
        TPanelElementMap::iterator getElementIterator(const int iId);
        TPanelElementMap::const_iterator getElementIterator(const int iId) const;

        /** renvoie le panneau des icones */
        TPanel * getButtonsPanel();

        /** construit le panneau compose des icones et du contenu actif */
        void buildComposedPanel();
        /** reconstruit le panneau complet */
        void discardComposedPanel();

        /** detruit les panneaux d'icones et de contenu */
        void discardAll();

        /** construit le panneau des icones */
        void buildButtonsPanel();
        /** reconstruit le panneau des icones */
        void discardButtonsPanel();

        /** construit le panneau des contenus actifs */
        void buildContentPanel();
        /** reconstruit le panneau des contenus */
        void discardContentPanel();

        /** actualise la map indexee par ordre d'affichage */
        void updateDisplayMap();

        /** un bouton a ete active */
        virtual void buttonActivated(TGenButton * btn);
        /** un bouton a ete bascule */
        virtual void buttonToggled(TGenButton * btn);
        /** un clic droit sur un bouton */
        virtual void buttonRightClicked(TGenButton * btn);

        virtual void onMenuClick(wxCommandEvent& evt);

        /** affiche / cache les boutons de scroll des tabs */
        void updateScrollButtonsState();

    private :
        /** detruit le panneau d'icones */
        void destroyButtonsPanel();
        /** mets le panneau de contenu de cote (penser a memoriser sa reference) */
        void destroyContentPanel();

        /** detruit les references aux boutons de la barre */
        void clearButtonsRefs();

        /** applique les proprietes de visibilite a un element */
        void applyVisibility(TPanelElementMap::iterator it, bool bVisible);

        typedef std::map<int,TPanelElement *> TElementsDisplayMap;

        TApplicationWindow * owner;
        int                 iPanelId;
        TPanelElementMap    elements;
        TElementsDisplayMap orderedElements;
        Orientation         orientation;
        Position            position;
        DisplayMode         displayMode;
        bool                bContentPanelVisible;
        TPanel *            contentPanel;
        bool                bButtonsBarVisible;
        TPanel *            subButtonsPanel;
        TPanel *            buttonsPanel;
        TGenButton *        buttonsBackwardBtn;
        TGenButton *        buttonsForwardBtn;
        TGenButton *        buttonsForwardBtn2;
        TGenButton *        buttonsShowBtn;
        wxMenu *            lastButtonsMenu;
        TPanel *            composedPanel;
        int                 iVisibleElements;
        bool                bMightHaveNoVisibleElement;
        wxSize              panelSize;
        bool                bUpdatingVisibleElements;

        typedef std::set<TApplicationPanelListener *> TApplicationPanelListenerList;
        /** liste des ecouteurs */
        TApplicationPanelListenerList listeners;

        static const TBitmap * getLeftArrowBmp();
        static const TBitmap * getRightArrowBmp();
        static const TBitmap * getUpArrowBmp();
        static const TBitmap * getDownArrowBmp();
        static const TBitmap * getCrossBmp();
        // images statiques pour les boutons de defilement
        static TBitmap *    _leftBmp;
        static TBitmap *    _rightBmp;
        static TBitmap *    _upBmp;
        static TBitmap *    _downBmp;
        static TBitmap *    _crossBmp;
};

bool operator>(const TApplicationPanel::TPanelElementMeta & a, const TApplicationPanel::TPanelElementMeta & b);



/**
 * @short Definition d'un ecouteur de modifications de panneau d'application
 * @author Thierry CHARLES <thierry@les-charles.net>
 * @version 0.1
 */
class TApplicationPanelListener
{
    public :
        virtual ~TApplicationPanelListener(){}
        /** indique qu'un element va etre supprime */
        virtual void removingElement(TApplicationPanel * panel, TPanelElement * elt) {}
        /** indique qu'un element est ajoute */
        virtual void addingElement(TApplicationPanel * panel, TPanelElement * elt) {}
        /** indique qu'un element devient visible / cache */
        virtual void elementVisibilityChanged(TApplicationPanel * panel, TPanelElement * elt, bool bVisible) {}
        /** indique que les donnees d'un element ont change */
        virtual void elementDataChanged(TApplicationPanel * panel, const TPanelElement * elt) {}
};

#endif // _TAPPLICATION_PANEL_H_
