/***************************************************************************
 *   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 <wx/wx.h>
#include <wx/filename.h>
#include <wx/splash.h>
#include <wx/datetime.h>
#include <wx/thread.h>
#include <wx/utils.h>
#include <wx/tokenzr.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>

#ifdef __WXMSW__
#include <wx/msw/registry.h>
#endif

#include "xpe.h"
#include "licensedlg.h"

#include "components/framework/tapplicationpanel.h"
#include "components/clock/clock_panelelement.h"
#include "components/calculator/calculator_pe.h"
#include "components/stdgui/tbitmap.h"
#include "xpe_components/res_id.h"
#include "xpe_components/editor/keywordsres.h"
#include "xpe_components/mainwindow.h"
#include "xpe_components/dlg/color_dlg.h"
#include "xpe_components/editor/ini_panelelement.h"
#include "xpe_components/editor/pov_panelelement.h"
#include "xpe_components/editor/stylesres.h"
#include "xpe_components/filepelt/imageviewer/imageviewer_pelt.h"
#include "xpe_components/lib/pathres.h"
#include "xpe_components/panelelt/documents_panelelt.h"
#include "xpe_components/panelelt/explorer_panelelt.h"
#include "xpe_components/panelelt/help_panelelt.h"
#include "xpe_components/panelelt/navig_panelelt.h"
#include "xpe_components/panelelt/notes_panelelt.h"
#include "xpe_components/panelelt/renderer_panelelt.h"
#include "xpe_components/panelelt/std_toolbars.h"
#include "xpe_components/panelelt/tools_panelelt.h"
#include "lib/lib_string.h"
#include "lib/lib_file.h"
#include "lib/lib_logging.h"

#include "bitmaps/splash.xpm"

IMPLEMENT_APP(XPE);

XPE::XPE()
    : TApplication(wxT("neXtgen Povray Editor")), splash(NULL)
{}
XPE::~XPE()
{ }

/**
 * Effectue les traitements de fermeture de l'application. N'est pas appelï¿½ si "bool start()" renvoie false
 */
void XPE::stop()
{
    // enregistrement du fichier de langue a utiliser
    wxString sLangFile = this->getTranslationFile();
    if(sLangFile.length() && libfile::exists(sLangFile))
    {
        wxString sLangConfFile = this->getApplConfigPath() + wxT("/language.conf");

        if(!libfile::mkdir(libfile::dirname(sLangConfFile)))
        {
            LOG_MESSAGE(wxString(wxT("Impossible de creer le repertoire de configuration de l'application : ")) + libfile::dirname(sLangConfFile),logging::_INFO_);
            return;
        }

        wxFFileOutputStream output(sLangConfFile,wxT("w"));
        wxTextOutputStream text( output );
        text.WriteString(sLangFile);
    }
}

bool XPE::start()
{
    // recherche du fichier de langue a utiliser
    wxString sLangConfFile = this->getApplConfigPath() + wxT("/language.conf");
    if(libfile::exists(sLangConfFile))
    {
        wxFFileInputStream input(sLangConfFile,wxT("r"));
        wxTextInputStream text( input );
        wxString sLangFile = text.ReadLine();
        if(libfile::exists(sLangFile))
            this->setTranslationFile(sLangFile);
    }

    // initialisation du répertoire d'installation
    wxString sExec(argv[0]);
    this->sInstallPrefix = libfile::normalize(libfile::dirname(sExec)) + wxT("/../");
    wxFileName fname(this->sInstallPrefix);
    fname.Normalize();
    this->sInstallPrefix = fname.GetFullPath();

    // init du répertoire d'installation de povray
#ifdef __WXMSW__
    wxRegKey key(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\POV-Ray"));
    if(key.Exists())
    {
        key.Open(wxRegKey::Read);
        // on cherche la version la plus récente
        size_t iNbSubs = 0;
        key.GetKeyInfo(&iNbSubs,NULL,NULL,NULL);
        if(iNbSubs > 0)
        {
            wxString sVersion;
            long i = 1L;
            key.GetFirstKey(sVersion,i);
            long iNumSub = long(iNbSubs)-2;
            if(iNumSub > 0)
                key.GetNextKey(sVersion,iNumSub);
            wxRegKey subkey(wxString(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\POV-Ray\\")) + sVersion + wxT("\\Windows"));
            if(subkey.Exists())
            {
                subkey.Open(wxRegKey::Read);
                subkey.QueryValue(wxT("Home"),this->sPovrayInstallPath);
                subkey.Close();
                this->sPovrayInstallPath = libfile::normalize(this->sPovrayInstallPath + wxT("/"));
            }
        }
        key.Close();
    }
#else
    wxString sPathList;
    wxGetEnv(wxT("PATH"),&sPathList);
    bool bPovrayFound = false;
    wxStringTokenizer tkz(sPathList, wxT(":"),wxTOKEN_STRTOK);
    while ( (!bPovrayFound) && tkz.HasMoreTokens() )
    {
        wxString sPath = tkz.GetNextToken();
        bPovrayFound = libfile::isFile(sPath + wxT("/povray")) || libfile::isFile(sPath + wxT("/megapov"));
        if(bPovrayFound)
        {
            this->sPovrayInstallPath = libfile::normalize(sPath);
            if(this->sPovrayInstallPath.GetChar(this->sPovrayInstallPath.length()-1) == wxT('/'))
                this->sPovrayInstallPath.Truncate(this->sPovrayInstallPath.length()-1);
            this->sPovrayInstallPath = libfile::dirname(this->sPovrayInstallPath);
        }
    }
#endif


    this->splash = new wxSplashScreen(TBitmap((const char **)splash_xpm), wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_NO_TIMEOUT|wxFRAME_NO_TASKBAR|wxNO_BORDER|wxSTAY_ON_TOP, 0, NULL, -1);

    this->initBmps();

    KeywordsListRes * keywords = new KeywordsListRes();
    this->publishRessource( POV_KEYWORDS_LIST_RESSOURCE_ID , keywords );

    keywords = new KeywordsListRes();
    this->publishRessource( INI_KEYWORDS_LIST_RESSOURCE_ID , keywords );

    PathsListRes * paths = new PathsListRes();
    this->publishRessource( PATHS_LIST_RESSOURCE_ID,paths);

    // création de la ressource des styles
    StylesRes * stylesRes = new StylesRes();
    stylesRes->loadDefaultParameters();
    this->publishRessource( STYLES_RESSOURCE_ID , stylesRes );

    ColorDlgRes * colorDlgRes = new ColorDlgRes();
    this->publishRessource( COLOR_DLG_RES_ID , colorDlgRes );

    return true;
}

/** déclare que l'init de l'application est terminé */
void XPE::applicationStarted()
{
    if(this->splash)
    {
        this->splash->Show(false);
        delete this->splash;
    }
    this->splash = NULL;

    static_cast<XPEMainWindow*>(this->getMainWindow())->checkForUpdate(true);
}

/** declare que l'init de l'application est termine en erreur */
void XPE::applicationStartupFailed()
{
    if(this->splash)
    {
        this->splash->Show(false);
        delete this->splash;
    }
    this->splash = NULL;
}

/** construit la fenetre principale et en renvoie le pointeur */
TMainWindow * XPE::buildMainWindow()
{
    return new XPEMainWindow(this);
}

/**
 * crée une instance de l'élément dont le type est passé en parametre et l'ajoute au panneau parent
 */
TPanelElement * XPE::builElement(const char * szType, TApplicationPanel * _owner, int iID)
{
    if(iID < 0)
        iID = this->getUPEID();

    TPanelElement * elt = NULL;
    if(!strcmp(szType,PELT_TYPE_POVEDITOR))
    {
        elt = new PovPanelElement(_owner,iID);
    }
    else if(!strcmp(szType,PELT_TYPE_INIEDITOR))
    {
        elt = new IniPanelElement(_owner,iID);
    }
    else if(!strcmp(szType,PELT_TYPE_IMAGEVIEWER))
    {
        elt = new ImageViewerPanelElement(_owner,iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TCLOCK))
    {
        elt = new ClockPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_DOCUMENTS))
    {
        elt = new DocumentsPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_FILE_EXPLORER))
    {
        elt = new FileExplorerPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_HELP))
    {
        elt = new HelpPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_NAVIGATOR))
    {
        elt = new NavigatorPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_NOTES))
    {
        elt = new NotesPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TCALCULATOR))
    {
        elt = new CalculatorPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_RENDERER))
    {
        elt = new RendererPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TOOLS))
    {
        elt = new ToolsPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TOOLBAR_FILE))
    {
        elt = new ToolbarFilePanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TOOLBAR_EDIT))
    {
        elt = new ToolbarEditPanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TOOLBAR_EDIT2))
    {
        elt = new ToolbarEdit2PanelElement(_owner, iID);
    }
    else if(!strcmp(szType,PELT_TYPE_TOOLBAR_SEARCH))
    {
        elt = new ToolbarSearchPanelElement(_owner, iID);
    }

    if(elt)
        _owner->addElement(elt);

    return elt;
}


/** genere un ID unique d'element de panel */
int XPE::getUPEID()
{
    static long iFirstTime = wxDateTime::GetTimeNow();
    static long iIdCount = 0;
    static wxCriticalSection UPEIDSection;

    wxCriticalSectionLocker locker(UPEIDSection); // synchronise l'acces a la fonction entre les threads

    wxDateTime now = wxDateTime::Now();
    int i = ((wxDateTime::GetTimeNow() - iFirstTime) * 1000 + now.GetMillisecond() + (++iIdCount));

    return i;
}

/** charge les parametres par défaut */
void XPE::loadDefaultParameters()
{
    TLicenseDlg dlg;
    if(dlg.ShowModal() != wxYES)
    {
        exit(1);
    }

    wxString sKeywordsFile = this->sInstallPrefix + wxT("/share/xpe/povKeywords.txt");
    if(!libfile::isFile(sKeywordsFile))
        sKeywordsFile = this->sInstallPrefix + wxT("/share/povKeywords.txt");
    if(libfile::isFile(sKeywordsFile))
    {
        KeywordsListRes * keywords = static_cast<KeywordsListRes *>(this->getRessource( POV_KEYWORDS_LIST_RESSOURCE_ID ));
        if(keywords)
            keywords->setKeywordsFile( sKeywordsFile );
    }
    sKeywordsFile = this->sInstallPrefix + wxT("/share/xpe/iniKeywords.txt");
    if(!libfile::isFile(sKeywordsFile))
        sKeywordsFile = this->sInstallPrefix + wxT("/share/iniKeywords.txt");
    if(libfile::isFile(sKeywordsFile))
    {
        KeywordsListRes * keywords = static_cast<KeywordsListRes *>(this->getRessource( INI_KEYWORDS_LIST_RESSOURCE_ID ));
        if(keywords)
            keywords->setKeywordsFile( sKeywordsFile );
    }

#ifdef __WXMSW__
    wxString sIncludesPath = this->sPovrayInstallPath + wxT("/include/");
#else
    wxString sIncludesPath = this->sPovrayInstallPath + wxT("/share/povray/include/");
#endif
    if(libfile::isDirectory(sIncludesPath))
    {
        PathsListRes * paths = static_cast<PathsListRes *>(this->getRessource( PATHS_LIST_RESSOURCE_ID ));
        if(paths)
            paths->addPath(sIncludesPath,true,1);
    }
    this->getMainWindow()->loadDefaultParameters();
}

#define XML_PARAM_NODE_RES_POV_KEYWORDS "PovKeywords"
#define XML_PARAM_NODE_RES_INI_KEYWORDS "IniKeywords"
#define XML_PARAM_NODE_RES_PATHS_LIST "PathsList"
#define XML_PARAM_NODE_RES_PATH_ELT "Path"
#define XML_PARAM_NODE_RES_PATH_ACTIVE_ATTR "active"
#define XML_PARAM_NODE_RES_COLOR_DLG "ColorDlg"
/** charge les parametres du composant a partir des informations contenues dans le noeud passé en paramètre */
bool XPE::loadParameters(TiXmlElement * parametersNode)
{
    if(!parametersNode)
        return false;

    TiXmlElement * elt = parametersNode->FirstChildElement( XML_PARAM_NODE_RES_POV_KEYWORDS );
    if(elt)
    {
        const char * szFile = elt->GetText();
        if(szFile && szFile[0])
        {
            KeywordsListRes * keywords = static_cast<KeywordsListRes *>(this->getRessource( POV_KEYWORDS_LIST_RESSOURCE_ID ));
            if(keywords)
            {
                keywords->setKeywordsFile( ISO2WX( szFile ) );
            }
        }
    }

    elt = parametersNode->FirstChildElement( XML_PARAM_NODE_RES_INI_KEYWORDS );
    if(elt)
    {
        const char * szFile = elt->GetText();
        if(szFile && szFile[0])
        {
            KeywordsListRes * keywords = static_cast<KeywordsListRes *>(this->getRessource( INI_KEYWORDS_LIST_RESSOURCE_ID ));
            if(keywords)
            {
                keywords->setKeywordsFile( ISO2WX( szFile ) );
            }
        }
    }

    elt = parametersNode->FirstChildElement( XML_PARAM_NODE_RES_PATHS_LIST );
    if(elt)
    {
        PathsListRes * paths = static_cast<PathsListRes *>(CurrentApplication()->getRessource(PATHS_LIST_RESSOURCE_ID));
        if(paths)
            paths->clear();

        TiXmlElement * pathElt = elt->FirstChildElement( XML_PARAM_NODE_RES_PATH_ELT );
        while(paths && pathElt)
        {
            const char * szPath = pathElt->GetText();
            bool bActive = false;
            if(pathElt->QueryValueAttribute(XML_PARAM_NODE_RES_PATH_ACTIVE_ATTR,&bActive) != TIXML_SUCCESS)
                szPath = NULL;
            if(szPath && szPath[0])
            {
                paths->addPath(ISO2WX( szPath ),bActive);
            }
            pathElt = pathElt->NextSiblingElement( XML_PARAM_NODE_RES_PATH_ELT );
        }
    }

    elt = parametersNode->FirstChildElement( XMLCC_NODE_STYLESRES_PARAMETERS );
    if(elt)
    {
        StylesRes * styles = static_cast<StylesRes *>(this->getRessource( STYLES_RESSOURCE_ID ));
        if(styles)
            styles->loadParameters( elt );
    }

    elt = parametersNode->FirstChildElement( XML_PARAM_NODE_RES_COLOR_DLG );
    if(elt)
    {
        ColorDlgRes * res = static_cast<ColorDlgRes *>(this->getRessource( COLOR_DLG_RES_ID ));
        if(res)
            res->loadParameters( elt );
    }

    return true;
}

/** renvoie les parametres du composant sous la forme d'un noeud xml */
TiXmlElement * XPE::getParameters()
{
    TiXmlElement * root = TApplication::getParameters();
    if(!root)
        root = new TiXmlElement(XMLCC_NODE_APP_PARAMS);

    KeywordsListRes * keywords = static_cast<KeywordsListRes *>(this->getRessource( POV_KEYWORDS_LIST_RESSOURCE_ID ));
    if(keywords)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_RES_POV_KEYWORDS);
        elt->LinkEndChild( new TiXmlText( (const char *)keywords->getKeywordsFile().fn_str() ) );
        root->LinkEndChild( elt );
    }

    keywords = static_cast<KeywordsListRes *>(this->getRessource( INI_KEYWORDS_LIST_RESSOURCE_ID ));
    if(keywords)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_RES_INI_KEYWORDS);
        elt->LinkEndChild( new TiXmlText( (const char *)keywords->getKeywordsFile().fn_str() ) );
        root->LinkEndChild( elt );
    }

    PathsListRes * paths = static_cast<PathsListRes *>(CurrentApplication()->getRessource(PATHS_LIST_RESSOURCE_ID));
    if(paths)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_RES_PATHS_LIST);

        PathsListRes::TPathsList list = paths->getPathsList();
        PathsListRes::TPathsList::const_iterator it = list.begin();
        while(it != list.end())
        {
            TiXmlElement * pathElt = new TiXmlElement(XML_PARAM_NODE_RES_PATH_ELT);

            pathElt->SetAttribute(XML_PARAM_NODE_RES_PATH_ACTIVE_ATTR,(*it).bActive);
            pathElt->LinkEndChild( new TiXmlText( (const char *)((*it).sPath.fn_str())) );

            elt->LinkEndChild( pathElt );
            it++;
        }
        root->LinkEndChild( elt );
    }

    StylesRes * styles = static_cast<StylesRes *>(this->getRessource( STYLES_RESSOURCE_ID ));
    if(styles)
    {
        root->LinkEndChild( styles->getParameters() );
    }

    ColorDlgRes * colorDlgRes = static_cast<ColorDlgRes *>(this->getRessource( COLOR_DLG_RES_ID ));
    if(colorDlgRes)
    {
        TiXmlElement * elt = new TiXmlElement(XML_PARAM_NODE_RES_COLOR_DLG);
        colorDlgRes->fillParameters(elt);
        root->LinkEndChild( elt );
    }

    return root;
}

