/***************************************************************************
 *   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 "navig_panelelt.h"

#include <wx/imaglist.h>

#include "bmp_id.h"

#include "lib/lib_string.h"
#include "lib/lib_logging.h"

#include "components/framework/tapplicationpanel.h"
#include "components/framework/tapplication.h"
#include "components/stdgui/tpanel.h"
#include "components/stdgui/tbitmap.h"
#include "xpe_components/res_id.h"
#include "xpe_components/mainwindow.h"
#include "xpe_components/editor/editor_panelelement.h"
#include "xpe_components/editor/pov_panelelement.h"
#include "xpe_components/parser/povfileinfo.h"
#include "xpe_components/parser/parserres.h"
#include "xpe_components/parser/povparserres.h"
#include "help_panelelt.h"


#define ICO_POV_FILE 0
#define ICO_LOCAL 1
#define ICO_DECLARE 2
#define ICO_MACRO 3
#define ICO_BOOKMARK 4

NavigatorPanelElement * NavigatorPanelElement::instance = NULL;

NavigatorPanelElement::NavigatorPanelElement(TApplicationPanel * _owner, int iID)
    : TPanelElement(_owner, iID, wxT(""), wxT("")),
    contents(NULL),
    btnLocal(NULL),
    btnDeclare(NULL),
    btnMacro(NULL),
    btnBookmarks(NULL),
    btnIncludes(NULL),
    bUpdateRunning(false),
    resPov(NULL),
    currentElt(NULL)
{
    instance = this;

    this->setTooltip(wxTr("Document content explorer"));
    this->setExtensivity(2);
    this->setBitmap(getDefaultIcon());

    // création des ressources si nécessaire
    this->resPov = dynamic_cast<PovFileParserRes *>(CurrentApplication()->getRessource( POV_FILES_PARSER_RESSOURCE_ID ));
    if(!this->resPov)
    {
        this->resPov = new PovFileParserRes();
        if(!CurrentApplication()->publishRessource( POV_FILES_PARSER_RESSOURCE_ID, this->resPov ))
        {
            delete this->resPov;
            this->resPov = dynamic_cast<PovFileParserRes *>(CurrentApplication()->getRessource( POV_FILES_PARSER_RESSOURCE_ID ));
        }
    }

    TApplicationPanel * panel = getMainWindowCenterPanel();
    panel->addPanelListener( this );
    for(int i = 0 ; i < panel->getElementsCount() ; i++)
        this->addingElement(panel, panel->getElementAt(i) );

    if(this->resPov)
    {
        this->resPov->addListener( this );
        this->resPov->startParser();
    }

    if(HelpPanelElement::getInstance())
        this->addSelectionListener(HelpPanelElement::getInstance());
}

NavigatorPanelElement::~NavigatorPanelElement()
{
    instance = NULL;

    if(getMainWindowCenterPanel())
        getMainWindowCenterPanel()->removePanelListener( this );
    if(this->resPov)
    {
        this->resPov->stopParser();
        this->resPov->dumpAll();
    }
}

const TBitmap * NavigatorPanelElement::getDefaultIcon()
{
    return GET_BMP(BMP_INCS_ID);
}

/** construit le panneau conteneur et son contenu (appellé automatiquement par getContentPanel et détruit automatiquement par le destructeur) */
TPanel * NavigatorPanelElement::buildContentPanel()
{
    TPanel * contentPanel = new TPanel(this->getOwner()->getContentPanel());
    wxBoxSizer * sizer = new wxBoxSizer( wxVERTICAL);
    contentPanel->SetSizer(sizer);

    TPanel * btnsPanel = new TPanel(contentPanel);
    wxBoxSizer * btnsSizer = new wxBoxSizer( wxHORIZONTAL);
    btnsPanel->SetSizer(btnsSizer);

    this->btnLocal = new TGenButton(btnsPanel);
    this->btnLocal->setText(wxT("L"));
    this->btnLocal->SetToolTip(wxTr("Display local range variables"));
    this->btnLocal->setToggleable( true );
    this->btnLocal->setToggled( true );
    this->btnLocal->setFlat( true );
    this->btnLocal->addButtonListener( this );
    btnsSizer->Add(this->btnLocal,0, wxEXPAND);

    btnsSizer->AddSpacer(10);

    this->btnDeclare = new TGenButton(btnsPanel);
    this->btnDeclare->setText(wxT("D"));
    this->btnDeclare->SetToolTip(wxTr("Display full range variables"));
    this->btnDeclare->setToggleable( true );
    this->btnDeclare->setToggled( true );
    this->btnDeclare->setFlat( true );
    this->btnDeclare->addButtonListener( this );
    btnsSizer->Add(this->btnDeclare,0, wxEXPAND);

    btnsSizer->AddSpacer(10);

    this->btnMacro = new TGenButton(btnsPanel);
    this->btnMacro->setText(wxT("M"));
    this->btnMacro->SetToolTip(wxTr("Display macros"));
    this->btnMacro->setToggleable( true );
    this->btnMacro->setToggled( true );
    this->btnMacro->setFlat( true );
    this->btnMacro->addButtonListener( this );
    btnsSizer->Add(this->btnMacro,0, wxEXPAND);

    btnsSizer->AddSpacer(10);

    this->btnBookmarks = new TGenButton(btnsPanel);
    this->btnBookmarks->setText(wxT("B"));
    this->btnBookmarks->SetToolTip(wxTr("Display persistant bookmarks"));
    this->btnBookmarks->setToggleable( true );
    this->btnBookmarks->setToggled( true );
    this->btnBookmarks->setFlat( true );
    this->btnBookmarks->addButtonListener( this );
    btnsSizer->Add(this->btnBookmarks,0, wxEXPAND);

    btnsSizer->AddSpacer(10);

    this->btnIncludes = new TGenButton(btnsPanel);
    this->btnIncludes->setText(wxT("I"));
    this->btnIncludes->SetToolTip(wxTr("Display includes"));
    this->btnIncludes->setToggleable( true );
    this->btnIncludes->setToggled( true );
    this->btnIncludes->setFlat( true );
    this->btnIncludes->addButtonListener( this );
    btnsSizer->Add(this->btnIncludes,0, wxEXPAND);

    sizer->Add(btnsPanel,0,wxALIGN_CENTER);
    sizer->Show(btnsPanel,true);

    this->contents = new wxTreeCtrl(contentPanel, -1, wxDefaultPosition, wxDefaultSize,
                                    wxTR_DEFAULT_STYLE | wxTR_SINGLE | wxTR_HIDE_ROOT);

    wxImageList * imglst = new wxImageList(16,16,true);
    imglst->Add(*GET_BMP(BMP_FILE_POVRAY_ID));
    imglst->Add(*GET_BMP(BMP_SHARP_LOCALE_ID));
    imglst->Add(*GET_BMP(BMP_SHARP_DECLARE_ID));
    imglst->Add(*GET_BMP(BMP_SHARP_MACRO_ID));
    imglst->Add(*GET_BMP(BMP_SHARP_BOOKMARK_ID));
    this->contents->AssignImageList(imglst);

    this->contents->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED,wxTreeEventHandler(NavigatorPanelElement::onTreeLeftClick), NULL, this);
    this->contents->Connect(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,wxTreeEventHandler(NavigatorPanelElement::onTreeDblClick), NULL, this);
    this->contents->Connect(wxEVT_COMMAND_TREE_ITEM_MENU,wxTreeEventHandler(NavigatorPanelElement::onTreeRightClick), NULL, this);

    sizer->Add(this->contents,1,wxEXPAND | wxTOP, 3);
    sizer->Show(this->contents,true);

    return contentPanel;
}

/** indique qu'un element va etre supprime d'un paneau */
void NavigatorPanelElement::removingElement(TApplicationPanel * panel, TPanelElement * elt)
{
    if(this->currentElt == elt)
        this->currentElt = NULL;

    FileParserRes * res = this->getParserRes(elt);
    if(res)
        res->updateEditorInfo( elt->getID() );
    this->clear();
}

/** indique qu'un element est ajoute à un panneau */
void NavigatorPanelElement::addingElement(TApplicationPanel * panel, TPanelElement * elt)
{
    FileParserRes * res = this->getParserRes(elt);
    if(res)
        res->updateEditorInfo( elt->getID() );

    if(!panel->isElementVisible( elt->getID() ))
        return;

    this->currentElt = elt;
    EditorPanelElement * edElt = dynamic_cast<EditorPanelElement *>(elt);

    this->bUpdateRunning = true;
    if( res )
    {
        res->stopParser();
        res->requestImmediateScan( edElt->getID() );
        res->startParser();
    }
    this->bUpdateRunning = false;
    this->reinitDisplay();
}

/** indique que les données d'un element ont changé */
void NavigatorPanelElement::elementDataChanged(TApplicationPanel * panel, const TPanelElement * elt)
{
    FileParserRes * res = this->getParserRes(elt);
    if(res)
        res->updateEditorInfo( elt->getID() );

    if(!panel->isElementVisible( elt->getID() ))
        return;

    this->currentElt = elt;
    const EditorPanelElement * edElt = dynamic_cast<const EditorPanelElement *>(elt);

    this->bUpdateRunning = true;
    if( res )
    {
        res->stopParser();
        res->requestImmediateScan( edElt->getID() );
        res->startParser();
    }
    this->bUpdateRunning = false;
    this->reinitDisplay();
}

/** indique qu'un element devient visible / cache */
void NavigatorPanelElement::elementVisibilityChanged(TApplicationPanel * panel, TPanelElement * elt, bool bVisible)
{
    if(!bVisible)
        return;

    this->currentElt = elt;

    this->bUpdateRunning = true;
    FileParserRes * res = this->getParserRes(elt);
    EditorPanelElement * edElt = dynamic_cast<EditorPanelElement *>(elt);
    if( res )
    {
        res->stopParser();
        res->requestImmediateScan( edElt->getID() );
        res->startParser();
    }
    this->bUpdateRunning = false;
    this->reinitDisplay();
}

void NavigatorPanelElement::onTreeLeftClick(wxTreeEvent & event)
{
    if(!this->contents)
        return;

    NavigatorItemData * data = dynamic_cast<NavigatorItemData *>(this->contents->GetItemData(event.GetItem()));

    this->fireSelectionChanged(data);
}

void NavigatorPanelElement::onTreeDblClick(wxTreeEvent & event)
{
    if(!this->contents)
        return;

    NavigatorItemData * data = dynamic_cast<NavigatorItemData *>(this->contents->GetItemData(event.GetItem()));
    if(!data)
        return;

    NavigatorItemData * parentData = dynamic_cast<NavigatorItemData *>(this->contents->GetItemData(this->contents->GetItemParent(event.GetItem())));

    wxString sFile;
    if(parentData)
        sFile = parentData->getSearchString();
    else
        sFile = data->getSearchString();

    XPEMainWindow * mainwindow = static_cast<XPEMainWindow *>(CurrentApplication()->getMainWindow());
    EditorPanelElement * elt = dynamic_cast<EditorPanelElement *>(mainwindow->getTabFromFile(sFile));
    if(!elt)
    {
        mainwindow->loadFile(sFile);
        elt = dynamic_cast<EditorPanelElement *>(mainwindow->getTabFromFile(sFile));
        if(!elt)
            return;
    }

    mainwindow->getCenterPanel()->setElementVisible(elt->getID(),true);
    elt->getEditor()->SetFocus();

    if(!parentData)
        return;

    TDocument * doc = elt->getEditor()->getDocument();

    wxString sSearchString = data->getSearchString();
    TPoint pt = doc->find(sSearchString,TPoint(0,0),true);

    if(pt.isValid())
        elt->getEditor()->getCursor()->setPosition(pt);
}

void NavigatorPanelElement::onTreeRightClick(wxTreeEvent & event)
{
    if(!this->contents)
        return;

//    NavigatorItemData * data = dynamic_cast<NavigatorItemData *>(this->contents->GetItemData(event.GetItem()));

}

void NavigatorPanelElement::buttonToggled(TGenButton * btn)
{
    this->reinitDisplay();
}

/** signale des modifications sur un fichier */
void NavigatorPanelElement::fileInfoUpdated(FileParserRes * parser, FileInfo * file)
{
    if(this->bUpdateRunning)
        return;
    this->reinitDisplay();
}

/** efface et réactualise complètement l'affichage */
void NavigatorPanelElement::reinitDisplay()
{
    if(!this->contents)
        return;

    this->clear();

    if(!this->currentElt)
        return;

    const PovPanelElement * povElt = dynamic_cast<const PovPanelElement *>(this->currentElt);
    if(povElt)
        this->initDisplayFor(povElt);
}

/**
 * actualise complètement l'affichage (apres qu'il ait été effacé)
 * @see reinitDisplay()
 */
void NavigatorPanelElement::initDisplayFor(const PovPanelElement * elt)
{
    FileParserRes * res = this->getParserRes(elt);

    FileInfoLocker locker(res,elt->getID());
    PovFileInfo * info = dynamic_cast<PovFileInfo *>(locker.getInfo());
    if(!info)
        return;

    wxTreeItemId rootId = this->contents->GetRootItem();
    wxString sName = elt->getName();
    wxTreeItemId eltID = this->contents->AppendItem(rootId,sName,ICO_POV_FILE,-1,new NavigatorItemData(sName,elt->getFilename(),wxT("")));
    this->fillTreeNodeWith(eltID,info);
    this->contents->Expand(eltID);
    if(this->btnIncludes->isToggled())
    {
        TStringSet deps = res->getFullDependanciesList( elt->getID() );
        TStringSet::iterator it = deps.begin();
        while(it != deps.end())
        {
            FileInfoLocker locker2(res,(*it));
            info = dynamic_cast<PovFileInfo *>(locker2.getInfo());
            if(!info)
            {
                it++;
                continue;
            }

            eltID = this->contents->AppendItem(rootId,libfile::basename(*it),ICO_POV_FILE,-1,new NavigatorItemData(libfile::basename(*it),(*it),wxT("")));
            this->fillTreeNodeWith(eltID,info, true);

            it++;
        }
    }
}

void NavigatorPanelElement::fillTreeNodeWith(wxTreeItemId node, PovFileInfo * info, bool bIsInclude)
{
    TNamedPovFileEltMap elts = info->getDistinctElements();
    TNamedPovFileEltMap::iterator it = elts.begin();
    while(it != elts.end())
    {
        int iIcon = -1;
        wxString sSearchStringPrefix;
        if((!bIsInclude) && (*it).second->getType() == PovFileElt::Local && this->btnLocal->isToggled())
        {
            iIcon = ICO_LOCAL;
            sSearchStringPrefix = wxT("#local ");
        }
        else if((*it).second->getType() == PovFileElt::Declare && this->btnDeclare->isToggled())
        {
            iIcon = ICO_DECLARE;
            sSearchStringPrefix = wxT("#declare ");
        }
        else if((*it).second->getType() == PovFileElt::Macro && this->btnMacro->isToggled())
        {
            iIcon = ICO_MACRO;
            sSearchStringPrefix = wxT("#macro ");
        }
        else if((!bIsInclude) && (*it).second->getType() == PovFileElt::Bookmark && this->btnBookmarks->isToggled())
        {
            iIcon = ICO_BOOKMARK;
            sSearchStringPrefix = wxT("//\247 ");
        }
        else
        {
//            fprintf(stderr,"skipping %s\n",(const char *)(*it).first.fn_str());
            it++;
            continue;
        }

        wxTreeItemId eltID = this->contents->AppendItem(node,(*it).first,iIcon,-1,new NavigatorItemData((*it).first, sSearchStringPrefix + (*it).second->getName(), (*it).second->getHelp()));

        it++;
    }
}

/** retourne l'instance de parserRes adaptée au fichier en cours d'édition */
FileParserRes * NavigatorPanelElement::getParserRes(const TPanelElement * elt)
{
    if(!elt)
        elt = this->currentElt;

    const PovPanelElement * povElt = dynamic_cast<const PovPanelElement *>(elt);
    if(povElt)
        return this->resPov;

    return NULL;
}

/** efface le contenu de l'arbre et toutes les données associées */
void NavigatorPanelElement::clear()
{
    this->contents->DeleteAllItems();
    wxTreeItemId rootId = this->contents->AddRoot(wxT("root"));
}

/** ajout un listener de selection */
void NavigatorPanelElement::addSelectionListener(NavigatorSelectionListener * l)
{
    if(!l)
        return;

    this->listeners.insert(l);
}

/** supprime un listener de selection */
void NavigatorPanelElement::removeSelectionListener(NavigatorSelectionListener * l)
{
    if(!l)
        return;

    this->listeners.erase(l);
}

/** propage un changement de sélection */
void NavigatorPanelElement::fireSelectionChanged(NavigatorItemData * data)
{
    TListenerList::iterator it = this->listeners.begin();
    while(it != this->listeners.end())
    {
        (*it)->selectionChanged(this,data);
        it++;
    }
}

