/***************************************************************************
 *   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.             *
 ***************************************************************************/

#include "tmainwindow.h"

#include <wx/sizer.h>
#include <wx/textctrl.h>

#include "lib/lib_logging.h"

#include "tapplicationpanel.h"
#include "components/stdgui/tpanel.h"

TMainWindow::TApplicationPanelMeta::~TApplicationPanelMeta()
{
    if(this->panel)
    {
        LOG_MESSAGE("Content panel still existing at meta-data destruction : this will result in memory loss",logging::_WARN_);
    }
}

void TMainWindow::TApplicationPanelMeta::destroyContent(TApplicationWindow * owner)
{
    if(owner && this->panel)
        owner->unregisterPanel(this->panel->getID());
    if(this->panel)
        delete this->panel;
    this->panel = NULL;
    this->bVisible = false;
}

TMainWindow::TMainWindow(wxWindow* parent, int iWindowId, const wxString & sTitle, long style, const wxString & sName)
    : TFrame(parent, iWindowId, sTitle, style, sName),
      TApplicationWindow(wxT("main")),
      topSplitter(NULL), leftSplitter(NULL), rightSplitter(NULL), bottomSplitter(NULL), bInitialized(false)
{
     this->build();
     this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler(TMainWindow::onCloseWindow));
}

TMainWindow::~TMainWindow()
{
    this->destroyWindowContent();
    TApplicationPanelMap::iterator it = this->floatingPanels.begin();
    while(it != this->floatingPanels.end())
    {
        delete (*it).second;
        it++;
    }
    this->floatingPanels.erase(this->floatingPanels.begin(), this->floatingPanels.end());
}

/** supprime tout le contenu de la fenetre. Devrait être appelé avant le destructeur, tant que toute la structure de l'application est encore accessible */
void TMainWindow::destroyWindowContent()
{
    this->centerPanel.destroyContent(this);
    this->topPanel.destroyContent(this);
    this->leftPanel.destroyContent(this);
    this->rightPanel.destroyContent(this);
    this->bottomPanel.destroyContent(this);
    TApplicationPanelMap::iterator it = this->floatingPanels.begin();
    while(it != this->floatingPanels.end())
    {
        (*it).second->destroyContent(this);
        it++;
    }

    this->updatePanels(); // autrement ca explose sur un resize

    TApplicationWindow::destroyWindowContent();
}

void TMainWindow::updatePanels()
{
    TApplicationWindow::updatePanels();

    if(!this->bInitialized)
        return;

    if(CurrentApplication()->isInClosingPhase())
        return;

    TPanel * elt1 = NULL;
    TPanel * elt2 = NULL;

    // panneau central
    if(this->centerPanel.panel)
    {
        elt1 = this->centerPanel.panel->getComposedPanel();
        elt1->Reparent(this->rightSplitter);
    }
    else
    {
        elt1 = new TPanel(this->rightSplitter);
    }

    // Panneau de droite
    if(this->rightPanel.panel && this->rightPanel.bVisible)
    {
        elt2 = this->rightPanel.panel->getComposedPanel();
        elt2->Reparent(this->rightSplitter);

        this->rightSplitter->SplitVertically(elt1,elt2);
    }
    else
        this->rightSplitter->Initialize(elt1);

    // panneau de gauche
    if(this->leftPanel.panel && this->leftPanel.bVisible)
    {
        elt1 = this->leftPanel.panel->getComposedPanel();
        elt1->Reparent(this->leftSplitter);

        this->leftSplitter->SplitVertically(elt1,this->rightSplitter);
    }
    else
        this->leftSplitter->Initialize(this->rightSplitter);

    // panneau du bas
    if(this->bottomPanel.panel && this->bottomPanel.bVisible)
    {
        elt2 = this->bottomPanel.panel->getComposedPanel();
        elt2->Reparent(this->bottomSplitter);

        this->bottomSplitter->SplitHorizontally(this->leftSplitter,elt2);
    }
    else
        this->bottomSplitter->Initialize(this->leftSplitter);

    // pannneau du haut
    if(this->topPanel.panel && this->topPanel.bVisible)
    {
        elt1 = this->topPanel.panel->getComposedPanel();
        elt1->Reparent(this->topSplitter);

        this->topSplitter->SplitHorizontally(elt1,this->bottomSplitter);
    }
    else
        this->topSplitter->Initialize(this->bottomSplitter);

    // Ajustement des dimensions des panels
    if(this->centerPanel.panel) this->centerPanel.panel->updateComposedPanelSize();
    if(this->rightPanel.panel)  this->rightPanel.panel->updateComposedPanelSize();
    if(this->leftPanel.panel)   this->leftPanel.panel->updateComposedPanelSize();
    if(this->bottomPanel.panel) this->bottomPanel.panel->updateComposedPanelSize();
    if(this->topPanel.panel)    this->topPanel.panel->updateComposedPanelSize();
}


/** defini le panneau du haut */
void TMainWindow::setTopPanel(TApplicationPanel * panel, bool bDeleteOld)
{
    TApplicationPanel * old = this->topPanel.panel;
    this->topPanel.panel = panel;
    this->topPanel.bVisible = (panel != NULL);
    if(old != NULL && (bDeleteOld || old->getID() == panel->getID()))
        this->unregisterPanel( old->getID() );
    this->registerPanel(panel);
    this->updatePanels();
    if(bDeleteOld && old != NULL)
        delete old;
}

/** defini le panneau de droite */
void TMainWindow::setRightPanel(TApplicationPanel * panel, bool bDeleteOld)
{
    TApplicationPanel * old = this->rightPanel.panel;
    this->rightPanel.panel = panel;
    this->rightPanel.bVisible = (panel != NULL);
    if(old != NULL && (bDeleteOld || old->getID() == panel->getID()))
        this->unregisterPanel( old->getID() );
    this->registerPanel(panel);
    this->updatePanels();
    if(bDeleteOld && old != NULL)
        delete old;
}

/** defini le panneau du bas */
void TMainWindow::setBottomPanel(TApplicationPanel * panel, bool bDeleteOld)
{
    TApplicationPanel * old = this->bottomPanel.panel;
    this->bottomPanel.panel = panel;
    this->bottomPanel.bVisible = (panel != NULL);
    if(old != NULL && (bDeleteOld || old->getID() == panel->getID()))
        this->unregisterPanel( old->getID() );
    this->registerPanel(panel);
    this->updatePanels();
    if(bDeleteOld && old != NULL)
        delete old;
}

/** defini le panneau de gauche */
void TMainWindow::setLeftPanel(TApplicationPanel * panel, bool bDeleteOld)
{
    TApplicationPanel * old = this->leftPanel.panel;
    this->leftPanel.panel = panel;
    this->leftPanel.bVisible = (panel != NULL);
    if(old != NULL && (bDeleteOld || old->getID() == panel->getID()))
        this->unregisterPanel( old->getID() );
    this->registerPanel(panel);
    this->updatePanels();
    if(bDeleteOld && old != NULL)
        delete old;
}

/** defini le panneau central */
void TMainWindow::setCenterPanel(TApplicationPanel * panel, bool bDeleteOld)
{
    TApplicationPanel * old = this->centerPanel.panel;
    this->centerPanel.panel = panel;
    this->centerPanel.bVisible = (panel != NULL);
    if(old != NULL && (bDeleteOld || old->getID() == panel->getID()))
        this->unregisterPanel( old->getID() );
    this->registerPanel(panel);
    this->updatePanels();
    if(bDeleteOld && old != NULL)
        delete old;
}

void TMainWindow::build()
{
    this->topSplitter = new TSplitter(this);
    this->bottomSplitter = new TSplitter(this->topSplitter);
    this->leftSplitter = new TSplitter(this->bottomSplitter);
    this->rightSplitter = new TSplitter(this->leftSplitter);

    this->topSplitter->SetMinimumPaneSize(1);
    this->topSplitter->SetSashGravity(0.0);
    this->leftSplitter->SetMinimumPaneSize(1);
    this->leftSplitter->SetSashGravity(0.0);
    this->rightSplitter->SetMinimumPaneSize(1);
    this->rightSplitter->SetSashGravity(1.0);
    this->bottomSplitter->SetMinimumPaneSize(1);
    this->bottomSplitter->SetSashGravity(1.0);
}

void TMainWindow::onCloseWindow(wxCloseEvent & evt)
{
    if(CurrentApplication()->isInClosingPhase())
        evt.Skip();

    // sauvegarder les options
    if(this->tryToClose())
    {
        this->Show(false);

        // arret des threads
        TThreadHandlersList::iterator it = this->threadHandlers.begin();
        while(it != this->threadHandlers.end())
        {
            (*it)->haltThread();
            it++;
        }

        bool bClosingApp = (CurrentApplication()->getMainWindow() == this);
        if(bClosingApp)
        {
            // sauvegarde de la config
            CurrentApplication()->saveParameters();
        }
        // démontage de l'application
        this->destroyWindowContent();
        if(bClosingApp)
        {
            // déchargement des ressources
            CurrentApplication()->unloadAllRessources();
            CurrentApplication()->setInClosingPhase( true );
        }

        evt.Skip();

#ifdef __WXMSW__
        exit(CurrentApplication()->forceExit());
#endif
    }
}

/** ajoute un handler sur un thread qui doit etre géré lors de l'arret de l'appli */
void TMainWindow::addManageableThread(ThreadHandler * t)
{
    this->threadHandlers.insert(t);
}

/** supprime un handler sur un thread qui ne doit plus etre géré lors de l'arret de l'appli */
void TMainWindow::removeManageableThread(ThreadHandler * t)
{
    this->threadHandlers.erase(t);
}

#define XML_PARAM_NODE_SPLITTERS_SIZE "SplittersSize"
#define XML_PARAM_NODE_SPLITTER "Splitter"
#define XML_PARAM_ATTR_SPLITTER_ID "id"
#define XML_PARAM_ATTR_SIZE "size"


/** charge les parametres du composant a partir des informations contenues dans le noeud passé en paramètre */
bool TMainWindow::loadParameters(TiXmlElement * parametersNode)
{
    TApplicationWindow::loadParameters(parametersNode);

    TiXmlElement * elt = parametersNode->FirstChildElement(XML_PARAM_NODE_SPLITTERS_SIZE);
    if(elt)
    {
        elt = elt->FirstChildElement(XML_PARAM_NODE_SPLITTER);
        while(elt)
        {
            int iSplitterId = -1;
            bool b = elt->QueryIntAttribute( XML_PARAM_ATTR_SPLITTER_ID , &iSplitterId ) == TIXML_SUCCESS;
            int iSize = -1;
            if(b && elt->QueryIntAttribute( XML_PARAM_ATTR_SIZE , &iSize ) == TIXML_SUCCESS )
            {
                TSplitter * splitter = NULL;
                switch(iSplitterId)
                {
                    case TOP_PANEL_ID:
                        splitter = this->topSplitter;
                        break;
                    case LEFT_PANEL_ID:
                        splitter = this->leftSplitter;
                        break;
                    case RIGHT_PANEL_ID:
                        splitter = this->rightSplitter;
                        break;
                    case BOTTOM_PANEL_ID:
                        splitter = this->bottomSplitter;
                        break;
                }

                this->splittersPosMap[splitter] = iSize;
            }
            elt = elt->NextSiblingElement(XML_PARAM_NODE_SPLITTER);
        }
    }
    return true;
}

/** renvoie les parametres du composant sous la forme d'un noeud xml */
TiXmlElement * TMainWindow::getParameters()
{
    TiXmlElement * root = TApplicationWindow::getParameters();

    TiXmlElement * splittersSizeElt = new TiXmlElement(XML_PARAM_NODE_SPLITTERS_SIZE);
    if(this->topSplitter)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_SPLITTER);
        elt->SetAttribute(XML_PARAM_ATTR_SPLITTER_ID,TOP_PANEL_ID);
        elt->SetAttribute(XML_PARAM_ATTR_SIZE,this->topSplitter->GetSashPosition());
        splittersSizeElt->LinkEndChild(elt);
    }
    if(this->leftSplitter)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_SPLITTER);
        elt->SetAttribute(XML_PARAM_ATTR_SPLITTER_ID,LEFT_PANEL_ID);
        elt->SetAttribute(XML_PARAM_ATTR_SIZE,this->leftSplitter->GetSashPosition());
        splittersSizeElt->LinkEndChild(elt);
    }
    if(this->rightSplitter)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_SPLITTER);
        elt->SetAttribute(XML_PARAM_ATTR_SPLITTER_ID,RIGHT_PANEL_ID);
        elt->SetAttribute(XML_PARAM_ATTR_SIZE,this->rightSplitter->GetSize().GetWidth() - this->rightSplitter->GetSashPosition());
        splittersSizeElt->LinkEndChild(elt);
    }
    if(this->bottomSplitter)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_SPLITTER);
        elt->SetAttribute(XML_PARAM_ATTR_SPLITTER_ID,BOTTOM_PANEL_ID);
        elt->SetAttribute(XML_PARAM_ATTR_SIZE,this->bottomSplitter->GetSize().GetHeight() - this->bottomSplitter->GetSashPosition());
        splittersSizeElt->LinkEndChild(elt);
    }
    root->LinkEndChild( splittersSizeElt );

    return root;
}

/** charge les panneaux d'une fenetre a partir des informations stockés sous forme XML */
bool TMainWindow::loadPanels(TiXmlElement * panelsNode)
{
    if(!TApplicationWindow::loadPanels(panelsNode))
        return false;

    bool bVisible = this->IsShown();
    this->Show(true);
    this->Refresh();

    std::map<TSplitter *,int>::iterator it = this->splittersPosMap.begin();
    while(it != this->splittersPosMap.end())
    {
        if((*it).first == this->rightSplitter)
            (*it).first->SetSashPosition((*it).first->GetSize().GetWidth() - (*it).second);
        else if((*it).first == this->bottomSplitter)
            (*it).first->SetSashPosition((*it).first->GetSize().GetHeight() - (*it).second);
        else
            (*it).first->SetSashPosition((*it).second);
        (*it).first->UpdateSize();
        it++;
    }

    this->Show(bVisible);

    return true;
}
