/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office 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/.
 */

#include <officecfg/Office/Common.hxx>
#include <com/sun/star/frame/XSubToolbarController.hpp>
#include <sidebar/ControllerFactory.hxx>
#include <sfx2/weldutils.hxx>
#include <vcl/commandinfoprovider.hxx>
#include <vcl/settings.hxx>
#include <vcl/weldutils.hxx>

namespace
{
bool lcl_RTLizeCommandURL(OUString& rCommandURL)
{
    if (rCommandURL == ".uno:ParaLeftToRight")
    {
        rCommandURL = u".uno:ParaRightToLeft"_ustr;
        return true;
    }
    if (rCommandURL == ".uno:ParaRightToLeft")
    {
        rCommandURL = u".uno:ParaLeftToRight"_ustr;
        return true;
    }
    if (rCommandURL == ".uno:LeftPara")
    {
        rCommandURL = u".uno:RightPara"_ustr;
        return true;
    }
    if (rCommandURL == ".uno:RightPara")
    {
        rCommandURL = u".uno:LeftPara"_ustr;
        return true;
    }
    if (rCommandURL == ".uno:AlignLeft")
    {
        rCommandURL = u".uno:AlignRight"_ustr;
        return true;
    }
    if (rCommandURL == ".uno:AlignRight")
    {
        rCommandURL = u".uno:AlignLeft"_ustr;
        return true;
    }
    return false;
}
}

// for now all controllers are in the sidebar
vcl::ImageType ToolbarUnoDispatcher::GetIconSize()
{
    vcl::ImageType eType = vcl::ImageType::Size16;
    switch (static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::SidebarIconSize::get()))
    {
        case ToolBoxButtonSize::Large:
            eType = vcl::ImageType::Size26;
            break;
        case ToolBoxButtonSize::Size32:
            eType = vcl::ImageType::Size32;
            break;
        case ToolBoxButtonSize::DontCare:
        case ToolBoxButtonSize::Small:
            break;
    }
    return eType;
}

ToolbarUnoDispatcher::ToolbarUnoDispatcher(weld::Toolbar& rToolbar, weld::Builder& rBuilder,
                                           const css::uno::Reference<css::frame::XFrame>& rFrame,
                                           bool bSideBar, weld::Label* pFirstLabel)
    : m_xFrame(rFrame)
    , m_pToolbar(&rToolbar)
    , m_pBuilder(&rBuilder)
    , m_bSideBar(bSideBar)
{
    rToolbar.connect_clicked(LINK(this, ToolbarUnoDispatcher, SelectHdl));
    rToolbar.connect_menu_toggled(LINK(this, ToolbarUnoDispatcher, ToggleMenuHdl));

    OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
    vcl::ImageType eSize = GetIconSize();
    rToolbar.set_icon_size(eSize);

    bool bRTL = AllSettings::GetLayoutRTL();

    for (int i = 0, nItems = rToolbar.get_n_items(); i < nItems; ++i)
    {
        OUString sIdent(rToolbar.get_item_ident(i));
        if (!sIdent.startsWith(".uno:"))
            continue;
        OUString sCommand = sIdent;
        if (bRTL && lcl_RTLizeCommandURL(sCommand))
            rToolbar.set_item_ident(i, sCommand);

        auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand, aModuleName);
        OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
        rToolbar.set_item_label(i, aLabel);
        if (i == 0 && pFirstLabel)
            pFirstLabel->set_label(aLabel);
        OUString aTooltip(
            vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, rFrame));
        rToolbar.set_item_tooltip_text(i, aTooltip);
        auto xImage(vcl::CommandInfoProvider::GetXGraphicForCommand(sCommand, rFrame, eSize));
        rToolbar.set_item_image(i, xImage);

        CreateController(sCommand);
    }

    rtl::Reference xWidget(new weld::TransportAsXWindow(m_pToolbar, m_pBuilder));
    m_xImageController = sfx2::sidebar::ControllerFactory::CreateImageController(m_xFrame, xWidget);
    m_aToolbarOptions.AddListenerLink(LINK(this, ToolbarUnoDispatcher, ChangedIconSizeHandler));
}

void ToolbarUnoDispatcher::CreateController(const OUString& rCommand)
{
    css::uno::Reference<css::frame::XToolbarController> xController(
        sfx2::sidebar::ControllerFactory::CreateToolBoxController(
            *m_pToolbar, *m_pBuilder, rCommand, m_xFrame, m_xFrame->getController(), m_bSideBar));

    if (xController.is())
        maControllers.insert(std::make_pair(rCommand, xController));
}

css::uno::Reference<css::frame::XToolbarController>
ToolbarUnoDispatcher::GetControllerForCommand(const OUString& rCommand) const
{
    ControllerContainer::const_iterator iController(maControllers.find(rCommand));
    if (iController != maControllers.end())
        return iController->second;

    return css::uno::Reference<css::frame::XToolbarController>();
}

IMPL_LINK(ToolbarUnoDispatcher, SelectHdl, const OUString&, rCommand, void)
{
    css::uno::Reference<css::frame::XToolbarController> xController(
        GetControllerForCommand(rCommand));

    if (xController.is())
        xController->execute(0);
}

IMPL_LINK(ToolbarUnoDispatcher, ToggleMenuHdl, const OUString&, rCommand, void)
{
    css::uno::Reference<css::frame::XToolbarController> xController(
        GetControllerForCommand(rCommand));

    if (xController.is())
        xController->click();
}

IMPL_LINK_NOARG(ToolbarUnoDispatcher, ChangedIconSizeHandler, LinkParamNone*, void)
{
    vcl::ImageType eSize = GetIconSize();
    m_pToolbar->set_icon_size(eSize);

    for (int i = 0, nItems = m_pToolbar->get_n_items(); i < nItems; ++i)
    {
        OUString sIdent(m_pToolbar->get_item_ident(i));
        auto xImage(vcl::CommandInfoProvider::GetXGraphicForCommand(sIdent, m_xFrame, eSize));
        m_pToolbar->set_item_image(sIdent, xImage);
    }

    for (auto const& it : maControllers)
    {
        css::uno::Reference<css::frame::XSubToolbarController> xController(it.second,
                                                                           css::uno::UNO_QUERY);
        if (xController.is() && xController->opensSubToolbar())
        {
            // The button should show the last function that was selected from the
            // dropdown. The controller should know better than us what it was.
            xController->updateImage();
        }
    }
}

void ToolbarUnoDispatcher::dispose()
{
    if (!m_pToolbar)
        return;

    m_aToolbarOptions.RemoveListenerLink(LINK(this, ToolbarUnoDispatcher, ChangedIconSizeHandler));

    ControllerContainer aControllers;
    aControllers.swap(maControllers);
    for (auto const& controller : aControllers)
    {
        css::uno::Reference<css::lang::XComponent> xComponent(controller.second,
                                                              css::uno::UNO_QUERY);
        if (xComponent.is())
            xComponent->dispose();
    }

    m_xImageController->dispose();
    m_pToolbar->connect_clicked(Link<const OUString&, void>());
    m_pToolbar = nullptr;
    m_pBuilder = nullptr;
}

ToolbarUnoDispatcher::~ToolbarUnoDispatcher() { dispose(); }

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
