/*************************************************************************
 *
 *  $RCSfile: helponstartup.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: obo $ $Date: 2005-03-15 12:55:54 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

//_______________________________________________
// include own header

#ifndef __FRAMEWORK_JOBS_HELPONSTARTUP_HXX_
#include <jobs/helponstartup.hxx>
#endif

#ifndef __FRAMEWORK_THREADHELP_RESETABLEGUARD_HXX_
#include <threadhelp/resetableguard.hxx>
#endif

#ifndef __FRAMEWORK_TARGETHELPER_HXX_
#include <loadenv/targethelper.hxx>
#endif

#ifndef __FRAMEWORK_SERVICES_H_
#include <services.h>
#endif

//_______________________________________________
// include others

#ifndef _COMPHELPER_CONFIGURATIONHELPER_HXX_
#include <comphelper/configurationhelper.hxx>
#endif

#ifndef _COMPHELPER_SEQUENCEASHASHMAP_HXX_
#include <comphelper/sequenceashashmap.hxx>
#endif

#ifndef _UTL_CONFIGMGR_HXX_
#include <unotools/configmgr.hxx>
#endif

#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

#ifndef _SV_HELP_HXX
#include <vcl/help.hxx>
#endif

#ifndef _RTL_USTRBUF_HXX_
#include <rtl/ustrbuf.hxx>
#endif

//_______________________________________________
// include interfaces

#ifndef _COM_SUN_STAR_FRAME_FRAMESEARCHFLAG_HPP_
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#endif

#ifndef _COM_SUN_STAR_FRAME_XFRAMESSUPPLIER_HPP_
#include <com/sun/star/frame/XFramesSupplier.hpp>
#endif

#ifndef _COM_SUN_STAR_FRAME_XDESKTOP_HPP_
#include <com/sun/star/frame/XDesktop.hpp>
#endif

//_______________________________________________
// namespace

namespace framework{

//_______________________________________________
// definitions

// path to module config
static ::rtl::OUString CFG_PACKAGE_MODULES      = ::rtl::OUString::createFromAscii("/org.openoffice.Setup/Office/Factories");
static ::rtl::OUString CFG_PACKAGE_SETUP        = ::rtl::OUString::createFromAscii("/org.openoffice.Setup"             );
static ::rtl::OUString CFG_PACKAGE_COMMON       = ::rtl::OUString::createFromAscii("/org.openoffice.Office.Common"     );
static ::rtl::OUString CFG_PATH_L10N            = ::rtl::OUString::createFromAscii("L10N"                              );
static ::rtl::OUString CFG_PATH_HELP            = ::rtl::OUString::createFromAscii("Help"                              );
static ::rtl::OUString CFG_KEY_LOCALE           = ::rtl::OUString::createFromAscii("ooLocale"                          );
static ::rtl::OUString CFG_KEY_HELPSYSTEM       = ::rtl::OUString::createFromAscii("System"                            );

// props of job environment
static ::rtl::OUString PROP_ENVIRONMENT         = ::rtl::OUString::createFromAscii("Environment"                       );
static ::rtl::OUString PROP_JOBCONFIG           = ::rtl::OUString::createFromAscii("JobConfig"                         );
static ::rtl::OUString PROP_ENVTYPE             = ::rtl::OUString::createFromAscii("EnvType"                           );
static ::rtl::OUString PROP_MODEL               = ::rtl::OUString::createFromAscii("Model"                             );

// props of module config
static ::rtl::OUString PROP_HELP_BASEURL        = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpBaseURL"         );
static ::rtl::OUString PROP_AUTOMATIC_HELP      = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpOnOpen"          );

// special value of job environment
static ::rtl::OUString ENVTYPE_DOCUMENTEVENT    = ::rtl::OUString::createFromAscii("DOCUMENTEVENT"                     );

//-----------------------------------------------

DEFINE_XINTERFACE_4(HelpOnStartup                              ,
                    OWeakObject                                ,
                    DIRECT_INTERFACE(css::lang::XTypeProvider ),
                    DIRECT_INTERFACE(css::lang::XServiceInfo  ),
                    DIRECT_INTERFACE(css::task::XJob          ),
                    DIRECT_INTERFACE(css::lang::XEventListener))

DEFINE_XTYPEPROVIDER_4(HelpOnStartup            ,
                       css::lang::XTypeProvider ,
                       css::lang::XServiceInfo  ,
                       css::task::XJob          ,
                       css::lang::XEventListener)

DEFINE_XSERVICEINFO_MULTISERVICE(HelpOnStartup                   ,
                                      ::cppu::OWeakObject             ,
                                      SERVICENAME_JOB                 ,
                                      IMPLEMENTATIONNAME_HELPONSTARTUP)

DEFINE_INIT_SERVICE(HelpOnStartup,
                    {
                        /*  Attention
                            I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
                            to create a new instance of this class by our own supported service factory.
                            see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
                        */
                        // create some needed uno services and cache it
                        m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >(
                            m_xSMGR->createInstance(SERVICENAME_MODULEMANAGER),
                            css::uno::UNO_QUERY_THROW);

                        m_xDesktop = css::uno::Reference< css::frame::XFrame >(
                            m_xSMGR->createInstance(SERVICENAME_DESKTOP),
                            css::uno::UNO_QUERY_THROW);

                        m_xConfig = css::uno::Reference< css::container::XNameAccess >(
                            ::comphelper::ConfigurationHelper::openConfig(
                                m_xSMGR,
                                CFG_PACKAGE_MODULES,
                                ::comphelper::ConfigurationHelper::E_READONLY),
                            css::uno::UNO_QUERY_THROW);

                        // ask for office locale
                        ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xSMGR,
                            CFG_PACKAGE_SETUP,
                            CFG_PATH_L10N,
                            CFG_KEY_LOCALE,
                            ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sLocale;

                        // detect system
                        ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xSMGR,
                            CFG_PACKAGE_COMMON,
                            CFG_PATH_HELP,
                            CFG_KEY_HELPSYSTEM,
                            ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sSystem;

                        // Start listening for disposing events of these services,
                        // so we can react e.g. for an office shutdown
                        css::uno::Reference< css::lang::XComponent > xComponent;
                        xComponent = css::uno::Reference< css::lang::XComponent >(m_xModuleManager, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                        xComponent = css::uno::Reference< css::lang::XComponent >(m_xDesktop, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                        xComponent = css::uno::Reference< css::lang::XComponent >(m_xConfig, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                    }
                   )

//-----------------------------------------------
HelpOnStartup::HelpOnStartup(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
    : ThreadHelpBase(     )
    , m_xSMGR       (xSMGR)
{
}

//-----------------------------------------------
HelpOnStartup::~HelpOnStartup()
{
}

//-----------------------------------------------
// css.task.XJob
css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
    throw(css::lang::IllegalArgumentException,
          css::uno::Exception                ,
          css::uno::RuntimeException         )
{
    // Analyze the given arguments; try to locate a model there and
    // classify it's used application module.
    ::rtl::OUString sModule = its_getModuleIdFromEnv(lArguments);

    // Attention: We are bound to events for openeing any document inside the office.
    // That includes e.g. the help module itself. But we have to do nothing then!
    if (!sModule.getLength())
        return css::uno::Any();

    // check current state of the help module
    // a) help isnt open                       => show default page for the detected module
    // b) help shows any other default page(!) => show default page for the detected module
    // c) help shows any other content         => do nothing (user travelled to any other content and leaved the set of default pages)
    ::rtl::OUString sCurrentHelpURL                = its_getCurrentHelpURL();
    sal_Bool        bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL);
    sal_Bool        bShowIt                        = sal_False;

    // a)
    if (!sCurrentHelpURL.getLength())
        bShowIt = sal_True;
    else
    // b)
    if (bCurrentHelpURLIsAnyDefaultURL)
        bShowIt = sal_True;

    if (bShowIt)
    {
        // retrieve the help URL for the detected application module
        ::rtl::OUString sModuleDependendHelpURL = its_checkIfHelpEnabledAndGetURL(sModule);
        if (sModuleDependendHelpURL.getLength())
        {
            // Show this help page.
            // Note: The help window brings itself to front ...
            Help* pHelp = Application::GetHelp();
            if (pHelp)
                pHelp->Start(sModuleDependendHelpURL, 0);
        }
    }

    return css::uno::Any();
}

//-----------------------------------------------
void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent)
    throw(css::uno::RuntimeException)
{
    // SAFE ->
    ResetableGuard aLock(m_aLock);

    if (aEvent.Source == m_xModuleManager)
        m_xModuleManager.clear();
    else
    if (aEvent.Source == m_xDesktop)
        m_xDesktop.clear();
    else
    if (aEvent.Source == m_xConfig)
        m_xConfig.clear();

    aLock.unlock();
    // <- SAFE
}

//-----------------------------------------------
::rtl::OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
{
    ::comphelper::SequenceAsHashMap lArgs        (lArguments);
    ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault(PROP_ENVIRONMENT, css::uno::Sequence< css::beans::NamedValue >());
    ::comphelper::SequenceAsHashMap lJobConfig   = lArgs.getUnpackedValueOrDefault(PROP_JOBCONFIG  , css::uno::Sequence< css::beans::NamedValue >());

    // check for right environment.
    // If its not a DocumentEvent, which triggered this job,
    // we cant work correctly! => return immediatly and do nothing
    ::rtl::OUString sEnvType = lEnvironment.getUnpackedValueOrDefault(PROP_ENVTYPE, ::rtl::OUString());
    if (!sEnvType.equals(ENVTYPE_DOCUMENTEVENT))
        return ::rtl::OUString();

    css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault(PROP_MODEL, css::uno::Reference< css::frame::XModel >());
    if (!xDoc.is())
        return ::rtl::OUString();

    // be sure that we work on top level documents only, which are registered
    // on the desktop instance. Ignore e.g. life previews, which are top frames too ...
    // but not registered at this global desktop instance.
    css::uno::Reference< css::frame::XDesktop >    xDesktopCheck;
    css::uno::Reference< css::frame::XFrame >      xFrame       ;
    css::uno::Reference< css::frame::XController > xController  = xDoc->getCurrentController();
    if (xController.is())
        xFrame = xController->getFrame();
    if (xFrame.is() && xFrame->isTop())
        xDesktopCheck = css::uno::Reference< css::frame::XDesktop >(xFrame->getCreator(), css::uno::UNO_QUERY);
    if (!xDesktopCheck.is())
        return ::rtl::OUString();

    // OK - now we are sure this document is a top level document.
    // Classify it.
    // SAFE ->
    ResetableGuard aLock(m_aLock);
    css::uno::Reference< css::frame::XModuleManager > xModuleManager = m_xModuleManager;
    aLock.unlock();
    // <- SAFE

    if (!xModuleManager.is())
        return ::rtl::OUString();

    ::rtl::OUString sModuleId;
    try
    {
        sModuleId = xModuleManager->identify(xDoc);
    }
    catch(const css::uno::RuntimeException& exRun)
        { throw exRun; }
    catch(const css::uno::Exception&)
        { sModuleId = ::rtl::OUString(); }

    return sModuleId;
}

//-----------------------------------------------
::rtl::OUString HelpOnStartup::its_getCurrentHelpURL()
{
    // SAFE ->
    ResetableGuard aLock(m_aLock);
    css::uno::Reference< css::frame::XFrame > xDesktop = m_xDesktop;
    aLock.unlock();
    // <- SAFE

    if (!xDesktop.is())
        return ::rtl::OUString();

    css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN);
    if (!xHelp.is())
        return ::rtl::OUString();

    ::rtl::OUString sCurrentHelpURL;
    try
    {
        css::uno::Reference< css::frame::XFramesSupplier >  xHelpRoot  (xHelp                 , css::uno::UNO_QUERY_THROW);
        css::uno::Reference< css::container::XIndexAccess > xHelpChilds(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW);

        css::uno::Reference< css::frame::XFrame >      xHelpChild  ;
        css::uno::Reference< css::frame::XController > xHelpView   ;
        css::uno::Reference< css::frame::XModel >      xHelpContent;

        xHelpChilds->getByIndex(0) >>= xHelpChild;
        if (xHelpChild.is())
            xHelpView = xHelpChild->getController();
        if (xHelpView.is())
            xHelpContent = xHelpView->getModel();
        if (xHelpContent.is())
            sCurrentHelpURL = xHelpContent->getURL();
    }
    catch(css::uno::RuntimeException& exRun)
        { throw exRun; }
    catch(css::uno::Exception&)
        { sCurrentHelpURL = ::rtl::OUString(); }

    return sCurrentHelpURL;
}

//-----------------------------------------------
::sal_Bool HelpOnStartup::its_isHelpUrlADefaultOne(const ::rtl::OUString& sHelpURL)
{
    if (!sHelpURL.getLength())
        return sal_False;

    // SAFE ->
    ResetableGuard aLock(m_aLock);
    css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR   (m_xSMGR, css::uno::UNO_QUERY_THROW);
    css::uno::Reference< css::container::XNameAccess >     xConfig = m_xConfig;
    ::rtl::OUString                                        sLocale = m_sLocale;
    ::rtl::OUString                                        sSystem = m_sSystem;
    aLock.unlock();
    // <- SAFE

    if (!xConfig.is())
        return sal_False;

    // check given help url against all default ones
    const css::uno::Sequence< ::rtl::OUString > lModules = xConfig->getElementNames();
    const ::rtl::OUString*                      pModules = lModules.getConstArray();
          ::sal_Int32                           c        = lModules.getLength();
          ::sal_Int32                           i        = 0;

    for (i=0; i<c; ++i)
    {
        try
        {
            css::uno::Reference< css::container::XNameAccess > xModuleConfig;
            xConfig->getByName(pModules[i]) >>= xModuleConfig;
            if (!xModuleConfig.is())
                continue;

            ::rtl::OUString sHelpBaseURL;
            xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL;
            ::rtl::OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
            if (sHelpURL.equals(sHelpURLForModule))
                return sal_True;
        }
        catch(const css::uno::RuntimeException& exRun)
            { throw exRun; }
        catch(const css::uno::Exception&)
            {}
    }

    return sal_False;
}

//-----------------------------------------------
::rtl::OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const ::rtl::OUString& sModule)
{
    // SAFE ->
    ResetableGuard aLock(m_aLock);
    css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
    ::rtl::OUString                                    sLocale = m_sLocale;
    ::rtl::OUString                                    sSystem = m_sSystem;
    aLock.unlock();
    // <- SAFE

    ::rtl::OUString sHelpURL;

    try
    {
        css::uno::Reference< css::container::XNameAccess > xModuleConfig;
        if (xConfig.is())
            xConfig->getByName(sModule) >>= xModuleConfig;

        sal_Bool bHelpEnabled = sal_False;
        if (xModuleConfig.is())
            xModuleConfig->getByName(PROP_AUTOMATIC_HELP) >>= bHelpEnabled;

        if (bHelpEnabled)
        {
            ::rtl::OUString sHelpBaseURL;
            xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL;
            sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
        }
    }
    catch(const css::uno::RuntimeException& exRun)
        { throw exRun; }
    catch(const css::uno::Exception&)
        { sHelpURL = ::rtl::OUString(); }

    return sHelpURL;
}

//-----------------------------------------------
::rtl::OUString HelpOnStartup::ist_createHelpURL(const ::rtl::OUString& sBaseURL,
                                                 const ::rtl::OUString& sLocale ,
                                                 const ::rtl::OUString& sSystem )
{
    ::rtl::OUStringBuffer sHelpURL(256);
    sHelpURL.append     (sBaseURL    );
    sHelpURL.appendAscii("?Language=");
    sHelpURL.append     (sLocale     );
    sHelpURL.appendAscii("&System="  );
    sHelpURL.append     (sSystem     );

    return sHelpURL.makeStringAndClear();
}

} // namespace framework
