/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
 */
 
// include own header
#include <jobs/helponstartup.hxx>
#include <loadenv/targethelper.hxx>
#include <services.h>
 
// include others
#include <comphelper/configurationhelper.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <unotools/configmgr.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
#include <rtl/ustrbuf.hxx>
 
// include interfaces
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/frame/ModuleManager.hpp>
#include <com/sun/star/frame/XFramesSupplier.hpp>
#include <com/sun/star/frame/Desktop.hpp>
 
namespace framework{
 
DEFINE_XSERVICEINFO_MULTISERVICE_2(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 information!
                        */
                        // create some needed uno services and cache it
                        m_xModuleManager = css::frame::ModuleManager::create( m_xContext );
 
                        m_xDesktop = css::frame::Desktop::create(m_xContext);
 
                        m_xConfig.set(
                            ::comphelper::ConfigurationHelper::openConfig(
                                m_xContext,
                                "/org.openoffice.Setup/Office/Factories",
                                ::comphelper::EConfigurationModes::ReadOnly),
                            css::uno::UNO_QUERY_THROW);
 
                        // ask for office locale
                        ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xContext,
                            "/org.openoffice.Setup",
                            "L10N",
                            "ooLocale",
                            ::comphelper::EConfigurationModes::ReadOnly) >>= m_sLocale;
 
                        // detect system
                        ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xContext,
                            "/org.openoffice.Office.Common",
                            "Help",
                            "System",
                            ::comphelper::EConfigurationModes::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.set(m_xModuleManager, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                        xComponent.set(m_xDesktop, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                        xComponent.set(m_xConfig, css::uno::UNO_QUERY);
                        if (xComponent.is())
                            xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
                    }
                   )
 
HelpOnStartup::HelpOnStartup(const css::uno::Reference< css::uno::XComponentContext >& xContext)
    : m_xContext    (xContext)
{
}
 
HelpOnStartup::~HelpOnStartup()
{
}
 
// css.task.XJob
css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
{
    // Analyze the given arguments; try to locate a model there and
    // classify it's used application module.
    OUString sModule = its_getModuleIdFromEnv(lArguments);
 
    // Attention: we are bound to events for opening any document inside the office.
    // That includes e.g. the help module itself. But we have to do nothing then!
    if (sModule.isEmpty())
        return css::uno::Any();
 
    // check current state of the help module
    // a) help isn't 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)
    OUString sCurrentHelpURL                = its_getCurrentHelpURL();
    bool        bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL);
    bool        bShowIt                        = false;
 
    // a)
    if (sCurrentHelpURL.isEmpty())
        bShowIt = true;
    // b)
    else if (bCurrentHelpURLIsAnyDefaultURL)
        bShowIt = true;
 
    if (bShowIt)
    {
        // retrieve the help URL for the detected application module
        OUString sModuleDependentHelpURL = its_checkIfHelpEnabledAndGetURL(sModule);
        if (!sModuleDependentHelpURL.isEmpty())
        {
            // Show this help page.
            // Note: The help window brings itself to front ...
            Help* pHelp = Application::GetHelp();
            if (pHelp)
                pHelp->Start(sModuleDependentHelpURL, static_cast<vcl::Window*>(nullptr));
        }
    }
 
    return css::uno::Any();
}
 
void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent)
{
    osl::MutexGuard g(m_mutex);
    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();
}
 
OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
{
    ::comphelper::SequenceAsHashMap lArgs        (lArguments);
    ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault("Environment", css::uno::Sequence< css::beans::NamedValue >());
 
    // check for right environment.
    // If it's not a DocumentEvent, which triggered this job,
    // we can't work correctly! => return immediately and do nothing
    OUString sEnvType = lEnvironment.getUnpackedValueOrDefault("EnvType", OUString());
    if (sEnvType != "DOCUMENTEVENT")
        return OUString();
 
    css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault("Model", css::uno::Reference< css::frame::XModel >());
    if (!xDoc.is())
        return 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.set(xFrame->getCreator(), css::uno::UNO_QUERY);
    if (!xDesktopCheck.is())
        return OUString();
 
    // OK - now we are sure this document is a top level document.
    // Classify it.
    // SAFE ->
    osl::ClearableMutexGuard aLock(m_mutex);
    css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = m_xModuleManager;
    aLock.clear();
    // <- SAFE
 
    OUString sModuleId;
    try
    {
        sModuleId = xModuleManager->identify(xDoc);
    }
    catch(const css::uno::RuntimeException&)
        { throw; }
    catch(const css::uno::Exception&)
        { sModuleId.clear(); }
 
    return sModuleId;
}
 
OUString HelpOnStartup::its_getCurrentHelpURL()
{
    // SAFE ->
    osl::ClearableMutexGuard aLock(m_mutex);
    css::uno::Reference< css::frame::XDesktop2 > xDesktop = m_xDesktop;
    aLock.clear();
    // <- SAFE
 
    if (!xDesktop.is())
        return OUString();
 
    css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN);
    if (!xHelp.is())
        return OUString();
 
    OUString sCurrentHelpURL;
    try
    {
        css::uno::Reference< css::frame::XFramesSupplier >  xHelpRoot  (xHelp                 , css::uno::UNO_QUERY_THROW);
        css::uno::Reference< css::container::XIndexAccess > xHelpChildren(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;
 
        xHelpChildren->getByIndex(0) >>= xHelpChild;
        if (xHelpChild.is())
            xHelpView = xHelpChild->getController();
        if (xHelpView.is())
            xHelpContent = xHelpView->getModel();
        if (xHelpContent.is())
            sCurrentHelpURL = xHelpContent->getURL();
    }
    catch(const css::uno::RuntimeException&)
        { throw; }
    catch(const css::uno::Exception&)
        { sCurrentHelpURL.clear(); }
 
    return sCurrentHelpURL;
}
 
bool HelpOnStartup::its_isHelpUrlADefaultOne(const OUString& sHelpURL)
{
    if (sHelpURL.isEmpty())
        return false;
 
    // SAFE ->
    osl::ClearableMutexGuard aLock(m_mutex);
    css::uno::Reference< css::container::XNameAccess >     xConfig = m_xConfig;
    OUString                                        sLocale = m_sLocale;
    OUString                                        sSystem = m_sSystem;
    aLock.clear();
    // <- SAFE
 
    if (!xConfig.is())
        return false;
 
    // check given help url against all default ones
    const css::uno::Sequence< OUString > lModules = xConfig->getElementNames();
    const 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;
 
            OUString sHelpBaseURL;
            xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
            OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
            if (sHelpURL == sHelpURLForModule)
                return true;
        }
        catch(const css::uno::RuntimeException&)
            { throw; }
        catch(const css::uno::Exception&)
            {}
    }
 
    return false;
}
 
OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const OUString& sModule)
{
    // SAFE ->
    osl::ClearableMutexGuard aLock(m_mutex);
    css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
    OUString                                    sLocale = m_sLocale;
    OUString                                    sSystem = m_sSystem;
    aLock.clear();
    // <- SAFE
 
    OUString sHelpURL;
 
    try
    {
        css::uno::Reference< css::container::XNameAccess > xModuleConfig;
        if (xConfig.is())
            xConfig->getByName(sModule) >>= xModuleConfig;
 
        bool bHelpEnabled = false;
        if (xModuleConfig.is())
            xModuleConfig->getByName("ooSetupFactoryHelpOnOpen") >>= bHelpEnabled;
 
        if (bHelpEnabled)
        {
            OUString sHelpBaseURL;
            xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
            sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
        }
    }
    catch(const css::uno::RuntimeException&)
        { throw; }
    catch(const css::uno::Exception&)
        { sHelpURL.clear(); }
 
    return sHelpURL;
}
 
OUString HelpOnStartup::ist_createHelpURL(const OUString& sBaseURL,
                                                 const OUString& sLocale ,
                                                 const OUString& sSystem )
{
    OUStringBuffer sHelpURL(256);
    sHelpURL.append     (sBaseURL    );
    sHelpURL.append("?Language=");
    sHelpURL.append     (sLocale     );
    sHelpURL.append("&System="  );
    sHelpURL.append     (sSystem     );
 
    return sHelpURL.makeStringAndClear();
}
 
} // namespace framework
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression 'bHelpEnabled' is always false.