/***************************************************************************
 *   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 "editor_panelelement.h"
#include "editor_styles.h"
#include "xpe_components/res_id.h"
#include "xpe_components/mainwindow.h"
#include "xpe_components/lib/pathres.h"
#include "components/codeeditor/tcodeeditor.h"
#include "components/framework/tapplicationpanel.h"
#include "components/framework/tapplication.h"
#include "components/framework/tmainwindow.h"
#include "components/stdgui/tpanel.h"
#include "components/stdgui/tbitmap.h"

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

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

EditorPanelElement::EditorPanelElement(TApplicationPanel * _owner, int iID)
    : FileViewerPanelElement(_owner, iID),
        resStyles(NULL),
        resKeywords(NULL),
        editor(NULL),
        bEditorPopupMenuBuild(false),
        openFileMenuItem(NULL),
        cutMenuItem(NULL),
        copyMenuItem(NULL),
        pasteMenuItem(NULL)
{
}

EditorPanelElement::~EditorPanelElement()
{
    if(this->resStyles)
        this->resStyles->removeRessourceListener( this );

    this->setKeywordsListRes(NULL);

    this->setEditor(NULL);
}

/** libère les ressources graphiques */
void EditorPanelElement::discardContentPanel()
{
    this->setEditor(NULL);
    FileViewerPanelElement::discardContentPanel();
}


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

    TCodeEditor * editor = new TCodeEditor(contentPanel, -1);

    editor->addBracketsPair('(',')');
    editor->addBracketsPair('{','}');
    editor->addBracketsPair('[',']');
    if(bWithRafterBrackets)
        editor->addBracketsPair('<','>',true);

    editor->declareCommentStyle( STYLE_COMMENT );

    sizer->Add(editor,1,wxEXPAND);
    sizer->Show(editor,true);

    this->setEditor(editor);

    this->resStyles = static_cast<StylesRes *>(CurrentApplication()->getRessource(STYLES_RESSOURCE_ID));
    if(this->resStyles)
    {
        this->resStyles->addRessourceListener( this );
        this->resStyles->applyStyles( editor );
    }

    return contentPanel;
}

/** charge un fichier apres avoir vidangé le contenu du composant */
bool EditorPanelElement::loadFileImpl(const wxString & sFile)
{
    return this->getEditor()->loadFile(sFile);
}

/** ecris un fichier */
bool EditorPanelElement::writeFileImpl(const wxString & sFile)
{
    return this->getEditor()->writeFile(sFile);
}

/** signale un changement dans le document. Cet évènement intervient systématiquement après un autre plus spécialisé (documentTextInserted ou documentTextRemoved) */
void EditorPanelElement::documentChanged(TDocument * doc)
{
    this->setModified(true);
}

/** défini l'éditeur de texte */
void EditorPanelElement::setEditor(TCodeEditor * ed)
{
    if(this->editor)
    {
        this->editor->getDocument()->removeDocumentListener(this);
        this->editor->Disconnect(wxEVT_RIGHT_DOWN,wxMouseEventHandler(EditorPanelElement::onMouseRightClick),NULL,this);
        this->editor->setAutoCompletionSource(NULL);
    }

    this->editor = ed;
    if(ed)
    {
        this->editor->Connect(wxEVT_RIGHT_DOWN,wxMouseEventHandler(EditorPanelElement::onMouseRightClick),NULL,this);
        this->editor->getDocument()->addDocumentListener(this);
        this->editor->setAutoCompletionSource(this);
    }
}

/** indique qu'un evenement s'est declanche sur la ressource */
void EditorPanelElement::ressourceEvent(TApplicationRessource * res, const int iEvent)
{
    if(this->getEditor() && this->resStyles && res == this->resStyles)
    {
        this->resStyles->applyStyles( this->getEditor() );
    }
    else if(this->getEditor() && this->resKeywords && res == this->resKeywords)
    {
        this->getEditor()->getEnlighter()->invalidateAll();
        this->getEditor()->Refresh();
    }
}

/** défini la ressources de mots clés gérée par l'enlighter et démarre l'écoute */
void EditorPanelElement::setKeywordsListRes(KeywordsListRes * res)
{
    if(this->resKeywords)
        this->resKeywords->removeRessourceListener(this);
    this->resKeywords = res;
    if(this->resKeywords)
        this->resKeywords->addRessourceListener(this);
}

void EditorPanelElement::onMouseRightClick(wxMouseEvent & event)
{
    if(event.GetEventObject() == this->editor)
    {
        if(!this->bEditorPopupMenuBuild)
            this->buildEditorPopupMenu(&(this->editorPopupMenu));

        this->prepareEditorPopupMenu(&(this->editorPopupMenu), event.GetX(), event.GetY());

        this->editor->PopupMenu(&(this->editorPopupMenu), event.GetX(), event.GetY());
    }
}

/** construit le menu et le connecte au listeners */
void EditorPanelElement::buildEditorPopupMenu(wxMenu * menu)
{
    this->bEditorPopupMenuBuild = true;
    if(!this->openFileMenuItem)
    {
        this->openFileMenuItem = menu->Append(OpenFile,wxTr("Open file"));
        this->editor->Connect( OpenFile, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(EditorPanelElement::onEditorPopupMenuCommand), NULL, this );
    }
    if(!this->cutMenuItem)
    {
        this->cutMenuItem = menu->Append(Cut,wxTr("Cut"));
        this->editor->Connect( Cut, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(EditorPanelElement::onEditorPopupMenuCommand), NULL, this );
    }
    if(!this->copyMenuItem)
    {
        this->copyMenuItem = menu->Append(Copy,wxTr("Copy"));
        this->editor->Connect( Copy, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(EditorPanelElement::onEditorPopupMenuCommand), NULL, this );
    }
    if(!this->pasteMenuItem)
    {
        this->pasteMenuItem = menu->Append(Paste,wxTr("Paste"));
        this->editor->Connect( Paste, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(EditorPanelElement::onEditorPopupMenuCommand), NULL, this );
    }
}

/** prépare le menu construit par buildEditorPopupMenu (etat et textes des wxMenuItems) */
void EditorPanelElement::prepareEditorPopupMenu(wxMenu * menu, long iEditorX, long iEditorY)
{
    bool bFileToOpen = this->checkForFileNameAt(this->sFileToOpen, iEditorX, iEditorY);
    this->openFileMenuItem->SetText(wxTr("Open file") + wxT(" \"") + libfile::basename(this->sFileToOpen) + wxT("\""));
    this->openFileMenuItem->Enable(bFileToOpen);

    TPoint pt1, pt2;
    this->editor->getSelection( pt1, pt2 );
    bool bHasSel = pt1.isValid() && pt2.isValid() && pt1 != pt2;

    this->cutMenuItem->Enable(bHasSel);
    this->copyMenuItem->Enable(bHasSel);
}

/** recherche la présence d'un nom de fichier à la position indiquée du composant */
bool EditorPanelElement::checkForFileNameAt(wxString & sFilename, long iEditorX, long iEditorY)
{
    sFilename.Clear();

    PathsListRes * paths = static_cast<PathsListRes *>(CurrentApplication()->getRessource(PATHS_LIST_RESSOURCE_ID));
    if(!paths)
        return false;

    int iLine = this->editor->getScreenLineAt( iEditorY );
    int iCol = this->editor->getScreenColumnAt( iLine, iEditorX );

    wxString sLine = this->editor->getDocument()->getLine( iLine );

    if(iCol < 0 || uint(iCol) >= sLine.length())
        return false;

    bool bInQuotes = false;
    uint iQuoteCol = 0;
    for(uint i = 0 ; i < uint(iCol) ; i++)
    {
        if(sLine[i] == '\"')
        {
            iQuoteCol = i;
            bInQuotes = !bInQuotes;
        }
    }

    if(!bInQuotes)
        return false;

    wxString sFile;
    for(uint i = iQuoteCol+1 ; i < sLine.length() ; i++)
    {
        if(sLine[i] == '\"')
            break;
        sFile << sLine[i];
    }

    if(!sFile.length())
        return false;

    sFilename = paths->getAbsoluteFilePath(sFile);
    return sFilename.length() > 0;
}

void EditorPanelElement::onEditorPopupMenuCommand(wxCommandEvent & event)
{
    if(event.GetId() == OpenFile)
        static_cast<XPEMainWindow *>(CurrentApplication()->getMainWindow())->loadFile(this->sFileToOpen);
    else if(event.GetId() == Cut)
        this->editor->clipboardCut();
    else if(event.GetId() == Copy)
        this->editor->clipboardCopy(false);
    else if(event.GetId() == Paste)
        this->editor->clipboardPaste(false);
}

/**
 * renvoie la liste des completions correspondant à un mot
 * @param sWordStart debut du mot a completer
 */
TStringList EditorPanelElement::getCompletionsForWord(TCodeEditor * ce, const wxString & sWordStart)
{
    TStringList completions;

    // on ajoute les mots cle
    TStringList::iterator it = this->resKeywords->getKeywordsList()->begin();
    while(it != this->resKeywords->getKeywordsList()->end())
    {
        if((*it).StartsWith(sWordStart))
            completions.push_back((*it));
        it++;
    }

    return completions;
}


