/* -*- 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 "PresenterTheme.hxx"
#include "PresenterBitmapContainer.hxx"
#include "PresenterCanvasHelper.hxx"
#include "PresenterConfigurationAccess.hxx"
#include <PresenterHelper.hxx>
#include <com/sun/star/rendering/PanoseWeight.hpp>
#include <osl/diagnose.h>
#include <rtl/ref.hxx>
#include <map>
#include <numeric>
#include <utility>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace sdext::presenter {

namespace {

class BorderSize
{
public:
    const static sal_Int32 mnInvalidValue = -10000;

    BorderSize() : mnLeft(mnInvalidValue),
                        mnTop(mnInvalidValue),
                        mnRight(mnInvalidValue),
                        mnBottom(mnInvalidValue) {}

    sal_Int32 mnLeft;
    sal_Int32 mnTop;
    sal_Int32 mnRight;
    sal_Int32 mnBottom;

    std::vector<sal_Int32> ToVector()
    {
        return
        {
            mnLeft == mnInvalidValue ? 0 : mnLeft,
            mnTop == mnInvalidValue ? 0 : mnTop,
            mnRight == mnInvalidValue ? 0 : mnRight,
            mnBottom == mnInvalidValue ? 0 : mnBottom
        };
    };

    void Merge (const BorderSize& rBorderSize)
    {
        if (mnLeft == mnInvalidValue)
            mnLeft = rBorderSize.mnLeft;
        if (mnTop == mnInvalidValue)
            mnTop = rBorderSize.mnTop;
        if (mnRight == mnInvalidValue)
            mnRight = rBorderSize.mnRight;
        if (mnBottom == mnInvalidValue)
            mnBottom = rBorderSize.mnBottom;
    }
};

/** Reading a theme from the configurations is done in various classes.  The
    ReadContext gives access to frequently used objects and functions to make
    the configuration handling easier.
*/
class ReadContext
{
public:
    Reference<rendering::XCanvas> mxCanvas;

    ReadContext (
        const Reference<rendering::XCanvas>& rxCanvas);

    /** Read data describing a font from the node that can be reached from
        the given root via the given path.
        @param rsFontPath
            May be empty.
    */
    static PresenterTheme::SharedFontDescriptor ReadFont (
        const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxTheme,
        const PresenterTheme::SharedFontDescriptor& rpDefault);
    static PresenterTheme::SharedFontDescriptor ReadFont (
        const Reference<beans::XPropertySet>& rxFontProperties,
        const PresenterTheme::SharedFontDescriptor& rpDefault);

    std::shared_ptr<PresenterTheme::Theme> ReadTheme (
        PresenterConfigurationAccess& rConfiguration,
        const OUString& rsThemeName);

    static BorderSize ReadBorderSize (const Reference<container::XNameAccess>& rxNode);

private:
    static Any GetByName (
        const Reference<container::XNameAccess>& rxNode,
        const OUString& rsName);
};

/** A PaneStyle describes how a pane is rendered.
*/
class PaneStyle
{
public:
    PaneStyle();

    SharedBitmapDescriptor GetBitmap (const OUString& sBitmapName) const;

    OUString msStyleName;
    std::shared_ptr<PaneStyle> mpParentStyle;
    PresenterTheme::SharedFontDescriptor mpFont;
    BorderSize maInnerBorderSize;
    BorderSize maOuterBorderSize;
    std::shared_ptr<PresenterBitmapContainer> mpBitmaps;

    PresenterTheme::SharedFontDescriptor GetFont() const;
};

typedef std::shared_ptr<PaneStyle> SharedPaneStyle;

class PaneStyleContainer
{
private:
    ::std::vector<SharedPaneStyle> mStyles;

public:
    void Read (
        const ReadContext& rReadContext,
        const Reference<container::XHierarchicalNameAccess>& rThemeRoot);

    SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;

private:
    void ProcessPaneStyle (
        ReadContext const & rReadContext,
        const ::std::vector<css::uno::Any>& rValues);
};

/** A ViewStyle describes how a view is displayed.
*/
class ViewStyle
{
public:
    ViewStyle();

    SharedBitmapDescriptor GetBitmap (std::u16string_view sBitmapName) const;

    PresenterTheme::SharedFontDescriptor GetFont() const;

    OUString msStyleName;
    std::shared_ptr<ViewStyle> mpParentStyle;
    PresenterTheme::SharedFontDescriptor mpFont;
    SharedBitmapDescriptor mpBackground;
};

typedef std::shared_ptr<ViewStyle> SharedViewStyle;

class ViewStyleContainer
{
private:
    ::std::vector<SharedViewStyle> mStyles;

public:
    void Read (
        const ReadContext& rReadContext,
        const Reference<container::XHierarchicalNameAccess>& rThemeRoot);

    SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;

private:
    void ProcessViewStyle(
        ReadContext const & rReadContext,
        const Reference<beans::XPropertySet>& rxProperties);
};

class StyleAssociationContainer
{
public:
    void Read (
        const Reference<container::XHierarchicalNameAccess>& rThemeRoot);

    OUString GetStyleName (const OUString& rsResourceName) const;

private:
    typedef std::map<OUString, OUString> StyleAssociations;
    StyleAssociations maStyleAssociations;

    void ProcessStyleAssociation(
        const ::std::vector<css::uno::Any>& rValues);
};

} // end of anonymous namespace

class PresenterTheme::Theme
{
public:
    Theme (
        const Reference<container::XHierarchicalNameAccess>& rThemeRoot,
        OUString sNodeName);

    void Read (
        PresenterConfigurationAccess& rConfiguration,
        ReadContext& rReadContext);

    OUString msConfigurationNodeName;
    std::shared_ptr<Theme> mpParentTheme;
    SharedBitmapDescriptor mpBackground;
    PaneStyleContainer maPaneStyles;
    ViewStyleContainer maViewStyles;
    StyleAssociationContainer maStyleAssociations;
    Reference<container::XHierarchicalNameAccess> mxThemeRoot;
    std::shared_ptr<PresenterBitmapContainer> mpIconContainer;
    typedef std::map<OUString,SharedFontDescriptor> FontContainer;
    FontContainer maFontContainer;

    SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;
    SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;

private:
    void ProcessFont(
        const OUString& rsKey,
        const Reference<beans::XPropertySet>& rxProperties);
};

//===== PresenterTheme ========================================================

PresenterTheme::PresenterTheme (
    css::uno::Reference<css::uno::XComponentContext> xContext,
    css::uno::Reference<css::rendering::XCanvas> xCanvas)
    : mxContext(std::move(xContext)),
      mxCanvas(std::move(xCanvas))
{
    mpTheme = ReadTheme();
}

PresenterTheme::~PresenterTheme()
{
}

std::shared_ptr<PresenterTheme::Theme> PresenterTheme::ReadTheme()
{
    ReadContext aReadContext(mxCanvas);

    PresenterConfigurationAccess aConfiguration (
        mxContext,
        u"/org.openoffice.Office.PresenterScreen/"_ustr,
        PresenterConfigurationAccess::READ_ONLY);

    return aReadContext.ReadTheme(aConfiguration, OUString());
}

bool PresenterTheme::HasCanvas() const
{
    return mxCanvas.is();
}

void PresenterTheme::ProvideCanvas (const Reference<rendering::XCanvas>& rxCanvas)
{
    if ( ! mxCanvas.is() && rxCanvas.is())
    {
        mxCanvas = rxCanvas;
        ReadTheme();
    }
}

OUString PresenterTheme::GetStyleName (const OUString& rsResourceURL) const
{
    OUString sStyleName;
    std::shared_ptr<Theme> pTheme (mpTheme);
    while (sStyleName.isEmpty() && pTheme != nullptr)
    {
        sStyleName = pTheme->maStyleAssociations.GetStyleName(rsResourceURL);
        pTheme = pTheme->mpParentTheme;
    }
    return sStyleName;
}

::std::vector<sal_Int32> PresenterTheme::GetBorderSize (
    const OUString& rsStyleName,
    const bool bOuter) const
{
    OSL_ASSERT(mpTheme != nullptr);

    SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
    if (pPaneStyle)
        if (bOuter)
            return pPaneStyle->maOuterBorderSize.ToVector();
        else
            return pPaneStyle->maInnerBorderSize.ToVector();
    else
    {
        return ::std::vector<sal_Int32>(4,0);
    }
}

PresenterTheme::SharedFontDescriptor PresenterTheme::ReadFont (
    const Reference<container::XHierarchicalNameAccess>& rxNode,
    const PresenterTheme::SharedFontDescriptor& rpDefault)
{
    return ReadContext::ReadFont(rxNode, rpDefault);
}

bool PresenterTheme::ConvertToColor (
    const Any& rColorSequence,
    sal_uInt32& rColor)
{
    Sequence<sal_Int8> aByteSequence;
    if (rColorSequence >>= aByteSequence)
    {
        rColor = std::accumulate(std::cbegin(aByteSequence), std::cend(aByteSequence), sal_uInt32(0),
            [](const sal_uInt32 nRes, const sal_uInt8 nByte) { return (nRes << 8) | nByte; });
        return true;
    }
    else
        return false;
}

std::shared_ptr<PresenterConfigurationAccess> PresenterTheme::GetNodeForViewStyle (
    const OUString& rsStyleName) const
{
    if (mpTheme == nullptr)
        return std::shared_ptr<PresenterConfigurationAccess>();

    // Open configuration for writing.
    auto pConfiguration = std::make_shared<PresenterConfigurationAccess>(
            mxContext,
            "/org.openoffice.Office.PresenterScreen/",
            PresenterConfigurationAccess::READ_WRITE);

    // Get configuration node for the view style container of the current
    // theme.
    if (pConfiguration->GoToChild( OUString(
        "Presenter/Themes/" + mpTheme->msConfigurationNodeName + "/ViewStyles")))
    {
        pConfiguration->GoToChild(
            [&rsStyleName] (OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
            {
                return PresenterConfigurationAccess::IsStringPropertyEqual(
                        rsStyleName, u"StyleName"_ustr, xProps);
            });
    }
    return pConfiguration;
}

SharedBitmapDescriptor PresenterTheme::GetBitmap (
    const OUString& rsStyleName,
    const OUString& rsBitmapName) const
{
    if (mpTheme != nullptr)
    {
        if (rsStyleName.isEmpty())
        {
            if (rsBitmapName == "Background")
            {
                std::shared_ptr<Theme> pTheme (mpTheme);
                while (pTheme != nullptr && !pTheme->mpBackground)
                    pTheme = pTheme->mpParentTheme;
                if (pTheme != nullptr)
                    return pTheme->mpBackground;
                else
                    return SharedBitmapDescriptor();
            }
        }
        else
        {
            SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
            if (pPaneStyle)
            {
                SharedBitmapDescriptor pBitmap (pPaneStyle->GetBitmap(rsBitmapName));
                if (pBitmap)
                    return pBitmap;
            }

            SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
            if (pViewStyle)
            {
                SharedBitmapDescriptor pBitmap (pViewStyle->GetBitmap(rsBitmapName));
                if (pBitmap)
                    return pBitmap;
            }
        }
    }

    return SharedBitmapDescriptor();
}

SharedBitmapDescriptor PresenterTheme::GetBitmap (
    const OUString& rsBitmapName) const
{
    if (mpTheme != nullptr)
    {
        if (rsBitmapName == "Background")
        {
            std::shared_ptr<Theme> pTheme (mpTheme);
            while (pTheme != nullptr && !pTheme->mpBackground)
                pTheme = pTheme->mpParentTheme;
            if (pTheme != nullptr)
                return pTheme->mpBackground;
            else
                return SharedBitmapDescriptor();
        }
        else
        {
            if (mpTheme->mpIconContainer != nullptr)
                return mpTheme->mpIconContainer->GetBitmap(rsBitmapName);
        }
    }

    return SharedBitmapDescriptor();
}

std::shared_ptr<PresenterBitmapContainer> PresenterTheme::GetBitmapContainer() const
{
    if (mpTheme != nullptr)
        return mpTheme->mpIconContainer;
    else
        return std::shared_ptr<PresenterBitmapContainer>();
}

PresenterTheme::SharedFontDescriptor PresenterTheme::GetFont (
    const OUString& rsStyleName) const
{
    if (mpTheme != nullptr)
    {
        SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
        if (pPaneStyle)
            return pPaneStyle->GetFont();

        SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
        if (pViewStyle)
            return pViewStyle->GetFont();

        std::shared_ptr<Theme> pTheme (mpTheme);
        while (pTheme != nullptr)
        {
            Theme::FontContainer::const_iterator iFont (pTheme->maFontContainer.find(rsStyleName));
            if (iFont != pTheme->maFontContainer.end())
                return iFont->second;

            pTheme = pTheme->mpParentTheme;
        }
    }

    return SharedFontDescriptor();
}

//===== FontDescriptor ========================================================

PresenterTheme::FontDescriptor::FontDescriptor (
    const std::shared_ptr<FontDescriptor>& rpDescriptor)
    : mnSize(12),
      mnColor(0x00000000),
      msAnchor(u"Left"_ustr),
      mnXOffset(0),
      mnYOffset(0)
{
    if (rpDescriptor != nullptr)
    {
        msFamilyName = rpDescriptor->msFamilyName;
        msStyleName = rpDescriptor->msStyleName;
        mnSize = rpDescriptor->mnSize;
        mnColor = rpDescriptor->mnColor;
        msAnchor = rpDescriptor->msAnchor;
        mnXOffset = rpDescriptor->mnXOffset;
        mnYOffset = rpDescriptor->mnYOffset;
    }
}

bool PresenterTheme::FontDescriptor::PrepareFont (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    if (mxFont.is())
        return true;

    if ( ! rxCanvas.is())
        return false;

    const double nCellSize (GetCellSizeForDesignSize(rxCanvas, mnSize));
    mxFont = CreateFont(rxCanvas, nCellSize);

    return mxFont.is();
}

Reference<rendering::XCanvasFont> PresenterTheme::FontDescriptor::CreateFont (
    const Reference<rendering::XCanvas>& rxCanvas,
    const double nCellSize) const
{
    rendering::FontRequest aFontRequest;
    aFontRequest.FontDescription.FamilyName = msFamilyName;
    if (msFamilyName.isEmpty())
        aFontRequest.FontDescription.FamilyName = "Tahoma";
    aFontRequest.FontDescription.StyleName = msStyleName;
    aFontRequest.CellSize = nCellSize;

    // Make an attempt at translating the style name(s)into a corresponding
    // font description.
    if (msStyleName == "Bold")
        aFontRequest.FontDescription.FontDescription.Weight = rendering::PanoseWeight::HEAVY;

    return rxCanvas->createFont(
        aFontRequest,
        Sequence<beans::PropertyValue>(),
        geometry::Matrix2D(1,0,0,1));
}

double PresenterTheme::FontDescriptor::GetCellSizeForDesignSize (
    const Reference<rendering::XCanvas>& rxCanvas,
    const double nDesignSize) const
{
    // Use the given design size as initial value in calculating the cell
    // size.
    double nCellSize (nDesignSize);

    if ( ! rxCanvas.is())
    {
        // We need the canvas to do the conversion.  Return the design size,
        // it is the our best guess in this circumstance.
        return nDesignSize;
    }

    Reference<rendering::XCanvasFont> xFont (CreateFont(rxCanvas, nCellSize));
    if ( ! xFont.is())
        return nDesignSize;

    geometry::RealRectangle2D aBox (PresenterCanvasHelper::GetTextBoundingBox (xFont, u"X"_ustr));

    const double nAscent (-aBox.Y1);
    //tdf#112408
    if (nAscent == 0)
        return nDesignSize;
    const double nDescent (aBox.Y2);
    const double nScale = (nAscent+nDescent) / nAscent;
    return nDesignSize * nScale;
}

//===== Theme =================================================================

PresenterTheme::Theme::Theme (
    const Reference<container::XHierarchicalNameAccess>& rxThemeRoot,
    OUString sNodeName)
    : msConfigurationNodeName(std::move(sNodeName)),
      maPaneStyles(),
      maViewStyles(),
      maStyleAssociations(),
      mxThemeRoot(rxThemeRoot)
{
}

void PresenterTheme::Theme::Read (
    PresenterConfigurationAccess& rConfiguration,
    ReadContext& rReadContext)
{
    // Parent theme name.
    OUString sParentThemeName;
    if ((PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, u"ParentTheme"_ustr)
            >>= sParentThemeName)
        && !sParentThemeName.isEmpty())
    {
        mpParentTheme = rReadContext.ReadTheme(rConfiguration, sParentThemeName);
    }

    // Background.
    mpBackground = PresenterBitmapContainer::LoadBitmap(
        mxThemeRoot,
        u"Background"_ustr,
        rReadContext.mxCanvas,
        SharedBitmapDescriptor());

    // Style associations.
    maStyleAssociations.Read(mxThemeRoot);

    // Pane styles.
    maPaneStyles.Read(rReadContext, mxThemeRoot);

    // View styles.
    maViewStyles.Read(rReadContext, mxThemeRoot);

    // Read bitmaps.
    mpIconContainer = std::make_shared<PresenterBitmapContainer>(
        Reference<container::XNameAccess>(
            PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, u"Bitmaps"_ustr), UNO_QUERY),
        mpParentTheme != nullptr ? mpParentTheme->mpIconContainer
                                 : std::shared_ptr<PresenterBitmapContainer>(),
        rReadContext.mxCanvas);

    // Read fonts.
    Reference<container::XNameAccess> xFontNode(
        PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, u"Fonts"_ustr),
        UNO_QUERY);
    PresenterConfigurationAccess::ForAll(
        xFontNode,
        [this] (OUString const& rKey, uno::Reference<beans::XPropertySet> const& xProps)
        {
            return this->ProcessFont(rKey, xProps);
        });
}

SharedPaneStyle PresenterTheme::Theme::GetPaneStyle (const OUString& rsStyleName) const
{
    SharedPaneStyle pPaneStyle (maPaneStyles.GetPaneStyle(rsStyleName));
    if (pPaneStyle)
        return pPaneStyle;
    else if (mpParentTheme != nullptr)
        return mpParentTheme->GetPaneStyle(rsStyleName);
    else
        return SharedPaneStyle();
}

SharedViewStyle PresenterTheme::Theme::GetViewStyle (const OUString& rsStyleName) const
{
    SharedViewStyle pViewStyle (maViewStyles.GetViewStyle(rsStyleName));
    if (pViewStyle)
        return pViewStyle;
    else if (mpParentTheme != nullptr)
        return mpParentTheme->GetViewStyle(rsStyleName);
    else
        return SharedViewStyle();
}

void PresenterTheme::Theme::ProcessFont(
    const OUString& rsKey,
    const Reference<beans::XPropertySet>& rxProperties)
{
    maFontContainer[rsKey] = ReadContext::ReadFont(rxProperties, SharedFontDescriptor());
}

namespace {

//===== ReadContext ===========================================================

ReadContext::ReadContext(const Reference<rendering::XCanvas>& rxCanvas)
    : mxCanvas(rxCanvas)
{
}

PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
    const Reference<container::XHierarchicalNameAccess>& rxNode,
    const PresenterTheme::SharedFontDescriptor& rpDefault)
{
    if ( ! rxNode.is())
        return PresenterTheme::SharedFontDescriptor();

    try
    {
        Reference<container::XHierarchicalNameAccess> xFont (
            PresenterConfigurationAccess::GetConfigurationNode(
                rxNode,
                /*rsFontPath*/u""_ustr),
                UNO_QUERY_THROW);

        Reference<beans::XPropertySet> xProperties (xFont, UNO_QUERY_THROW);
        return ReadFont(xProperties, rpDefault);
    }
    catch (Exception&)
    {
        OSL_ASSERT(false);
    }

    return PresenterTheme::SharedFontDescriptor();
}

PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
    const Reference<beans::XPropertySet>& rxProperties,
    const PresenterTheme::SharedFontDescriptor& rpDefault)
{
    auto pDescriptor = std::make_shared<PresenterTheme::FontDescriptor>(rpDefault);

    PresenterConfigurationAccess::GetProperty(rxProperties, u"FamilyName"_ustr) >>= pDescriptor->msFamilyName;
    PresenterConfigurationAccess::GetProperty(rxProperties, u"Style"_ustr) >>= pDescriptor->msStyleName;
    PresenterConfigurationAccess::GetProperty(rxProperties, u"Size"_ustr) >>= pDescriptor->mnSize;
    PresenterTheme::ConvertToColor(
        PresenterConfigurationAccess::GetProperty(rxProperties, u"Color"_ustr),
        pDescriptor->mnColor);
    PresenterConfigurationAccess::GetProperty(rxProperties, u"Anchor"_ustr) >>= pDescriptor->msAnchor;
    PresenterConfigurationAccess::GetProperty(rxProperties, u"XOffset"_ustr) >>= pDescriptor->mnXOffset;
    PresenterConfigurationAccess::GetProperty(rxProperties, u"YOffset"_ustr) >>= pDescriptor->mnYOffset;

    return pDescriptor;
}

Any ReadContext::GetByName (
    const Reference<container::XNameAccess>& rxNode,
    const OUString& rsName)
{
    OSL_ASSERT(rxNode.is());
    if (rxNode->hasByName(rsName))
        return rxNode->getByName(rsName);
    else
        return Any();
}

std::shared_ptr<PresenterTheme::Theme> ReadContext::ReadTheme (
    PresenterConfigurationAccess& rConfiguration,
    const OUString& rsThemeName)
{
    std::shared_ptr<PresenterTheme::Theme> pTheme;

    OUString sCurrentThemeName (rsThemeName);
    if (sCurrentThemeName.isEmpty())
    {
         // No theme name given.  Look up the CurrentTheme property.
         rConfiguration.GetConfigurationNode(u"Presenter/CurrentTheme"_ustr) >>= sCurrentThemeName;
         if (sCurrentThemeName.isEmpty())
         {
             // Still no name.  Use "DefaultTheme".
             sCurrentThemeName = "DefaultTheme";
         }
    }

    Reference<container::XNameAccess> xThemes (
        rConfiguration.GetConfigurationNode(u"Presenter/Themes"_ustr),
        UNO_QUERY);
    if (xThemes.is())
    {
        // Iterate over all themes and search the one with the given name.
        const Sequence<OUString> aKeys (xThemes->getElementNames());
        for (const OUString& rsKey : aKeys)
        {
            Reference<container::XHierarchicalNameAccess> xTheme (
                xThemes->getByName(rsKey), UNO_QUERY);
            if (xTheme.is())
            {
                OUString sThemeName;
                PresenterConfigurationAccess::GetConfigurationNode(xTheme, u"ThemeName"_ustr)
                    >>= sThemeName;
                if (sThemeName == sCurrentThemeName)
                {
                    pTheme = std::make_shared<PresenterTheme::Theme>(xTheme,rsKey);
                    break;
                }
            }
        }
    }

    if (pTheme != nullptr)
    {
        pTheme->Read(rConfiguration, *this);
    }

    return pTheme;
}

BorderSize ReadContext::ReadBorderSize (const Reference<container::XNameAccess>& rxNode)
{
    BorderSize aBorderSize;

    if (rxNode.is())
    {
        GetByName(rxNode, u"Left"_ustr) >>= aBorderSize.mnLeft;
        GetByName(rxNode, u"Top"_ustr) >>= aBorderSize.mnTop;
        GetByName(rxNode, u"Right"_ustr) >>= aBorderSize.mnRight;
        GetByName(rxNode, u"Bottom"_ustr) >>= aBorderSize.mnBottom;
    }

    return aBorderSize;
}

//===== PaneStyleContainer ====================================================

void PaneStyleContainer::Read (
    const ReadContext& rReadContext,
    const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
{
    Reference<container::XNameAccess> xPaneStyleList (
        PresenterConfigurationAccess::GetConfigurationNode(
            rxThemeRoot,
            u"PaneStyles"_ustr),
        UNO_QUERY);
    if (!xPaneStyleList.is())
        return;

    ::std::vector<OUString> aProperties;
    aProperties.reserve(6);
    aProperties.emplace_back("StyleName");
    aProperties.emplace_back("ParentStyle");
    aProperties.emplace_back("TitleFont");
    aProperties.emplace_back("InnerBorderSize");
    aProperties.emplace_back("OuterBorderSize");
    aProperties.emplace_back("BorderBitmapList");
    PresenterConfigurationAccess::ForAll(
        xPaneStyleList,
        aProperties,
        [this, &rReadContext] (std::vector<uno::Any> const& rValues)
        {
            return this->ProcessPaneStyle(rReadContext, rValues);
        });
}

void PaneStyleContainer::ProcessPaneStyle(
    ReadContext const & rReadContext,
    const ::std::vector<Any>& rValues)
{
    if (rValues.size() != 6)
        return;

    auto pStyle = std::make_shared<PaneStyle>();

    rValues[0] >>= pStyle->msStyleName;

    OUString sParentStyleName;
    if (rValues[1] >>= sParentStyleName)
    {
        // Find parent style.
        auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
            [&sParentStyleName](const SharedPaneStyle& rxStyle) { return rxStyle->msStyleName == sParentStyleName; });
        if (iStyle != mStyles.end())
            pStyle->mpParentStyle = *iStyle;
    }

    Reference<container::XHierarchicalNameAccess> xFontNode (rValues[2], UNO_QUERY);
    pStyle->mpFont = ReadContext::ReadFont(
        xFontNode, PresenterTheme::SharedFontDescriptor());

    Reference<container::XNameAccess> xInnerBorderSizeNode (rValues[3], UNO_QUERY);
    pStyle->maInnerBorderSize = ReadContext::ReadBorderSize(xInnerBorderSizeNode);
    Reference<container::XNameAccess> xOuterBorderSizeNode (rValues[4], UNO_QUERY);
    pStyle->maOuterBorderSize = ReadContext::ReadBorderSize(xOuterBorderSizeNode);

    if (pStyle->mpParentStyle != nullptr)
    {
        pStyle->maInnerBorderSize.Merge(pStyle->mpParentStyle->maInnerBorderSize);
        pStyle->maOuterBorderSize.Merge(pStyle->mpParentStyle->maOuterBorderSize);
    }

    if (rReadContext.mxCanvas.is())
    {
        Reference<container::XNameAccess> xBitmapsNode (rValues[5], UNO_QUERY);
        pStyle->mpBitmaps = std::make_shared<PresenterBitmapContainer>(
            xBitmapsNode,
            pStyle->mpParentStyle != nullptr ? pStyle->mpParentStyle->mpBitmaps
                                             : std::shared_ptr<PresenterBitmapContainer>(),
            rReadContext.mxCanvas);
    }

    mStyles.push_back(std::move(pStyle));
}

SharedPaneStyle PaneStyleContainer::GetPaneStyle (const OUString& rsStyleName) const
{
    auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
        [&rsStyleName](const SharedPaneStyle& rxStyle) { return rxStyle->msStyleName == rsStyleName; });
    if (iStyle != mStyles.end())
        return *iStyle;
    return SharedPaneStyle();
}

//===== PaneStyle =============================================================

PaneStyle::PaneStyle()
{
}

SharedBitmapDescriptor PaneStyle::GetBitmap (const OUString& rsBitmapName) const
{
    if (mpBitmaps != nullptr)
    {
        SharedBitmapDescriptor pBitmap = mpBitmaps->GetBitmap(rsBitmapName);
        if (pBitmap)
            return pBitmap;
    }

    if (mpParentStyle != nullptr)
        return mpParentStyle->GetBitmap(rsBitmapName);
    else
        return SharedBitmapDescriptor();
}

PresenterTheme::SharedFontDescriptor PaneStyle::GetFont() const
{
    if (mpFont)
        return mpFont;
    else if (mpParentStyle != nullptr)
        return mpParentStyle->GetFont();
    else
        return PresenterTheme::SharedFontDescriptor();
}

//===== ViewStyleContainer ====================================================

void ViewStyleContainer::Read (
    const ReadContext& rReadContext,
    const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
{
    Reference<container::XNameAccess> xViewStyleList (
        PresenterConfigurationAccess::GetConfigurationNode(
            rxThemeRoot,
            u"ViewStyles"_ustr),
        UNO_QUERY);
    if (xViewStyleList.is())
    {
        PresenterConfigurationAccess::ForAll(
            xViewStyleList,
            [this, &rReadContext] (OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
            {
                return this->ProcessViewStyle(rReadContext, xProps);
            });
    }
}

void ViewStyleContainer::ProcessViewStyle(
    ReadContext const & rReadContext,
    const Reference<beans::XPropertySet>& rxProperties)
{
    auto pStyle = std::make_shared<ViewStyle>();

    PresenterConfigurationAccess::GetProperty(rxProperties, u"StyleName"_ustr)
        >>= pStyle->msStyleName;

    OUString sParentStyleName;
    if (PresenterConfigurationAccess::GetProperty(rxProperties, u"ParentStyle"_ustr)
        >>= sParentStyleName)
    {
        // Find parent style.
        auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
            [&sParentStyleName](const SharedViewStyle& rxStyle) { return rxStyle->msStyleName == sParentStyleName; });
        if (iStyle != mStyles.end())
        {
            pStyle->mpParentStyle = *iStyle;
            pStyle->mpFont = (*iStyle)->mpFont;
            pStyle->mpBackground = (*iStyle)->mpBackground;
        }
    }

    Reference<container::XHierarchicalNameAccess> xFontNode (
        PresenterConfigurationAccess::GetProperty(rxProperties, u"Font"_ustr), UNO_QUERY);
    PresenterTheme::SharedFontDescriptor pFont (
        ReadContext::ReadFont(xFontNode, PresenterTheme::SharedFontDescriptor()));
    if (pFont)
        pStyle->mpFont = std::move(pFont);

    Reference<container::XHierarchicalNameAccess> xBackgroundNode (
        PresenterConfigurationAccess::GetProperty(rxProperties, u"Background"_ustr),
        UNO_QUERY);
    SharedBitmapDescriptor pBackground (PresenterBitmapContainer::LoadBitmap(
        xBackgroundNode,
        OUString(),
        rReadContext.mxCanvas,
        SharedBitmapDescriptor()));
    if (pBackground && pBackground->GetNormalBitmap().is())
        pStyle->mpBackground = std::move(pBackground);

    mStyles.push_back(std::move(pStyle));
}

SharedViewStyle ViewStyleContainer::GetViewStyle (const OUString& rsStyleName) const
{
    auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
        [&rsStyleName](const SharedViewStyle& rxStyle) { return rxStyle->msStyleName == rsStyleName; });
    if (iStyle != mStyles.end())
        return *iStyle;
    return SharedViewStyle();
}

//===== ViewStyle =============================================================

ViewStyle::ViewStyle()
{
}

SharedBitmapDescriptor ViewStyle::GetBitmap (std::u16string_view rsBitmapName) const
{
    if (rsBitmapName == u"Background")
        return mpBackground;
    else
        return SharedBitmapDescriptor();
}

PresenterTheme::SharedFontDescriptor ViewStyle::GetFont() const
{
    if (mpFont)
        return mpFont;
    else if (mpParentStyle != nullptr)
        return mpParentStyle->GetFont();
    else
        return PresenterTheme::SharedFontDescriptor();
}

//===== StyleAssociationContainer =============================================

void StyleAssociationContainer::Read (
    const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
{
    Reference<container::XNameAccess> xStyleAssociationList (
        PresenterConfigurationAccess::GetConfigurationNode(
            rxThemeRoot,
            u"StyleAssociations"_ustr),
        UNO_QUERY);
    if (!xStyleAssociationList.is())
        return;

    ::std::vector<OUString> aProperties { u"ResourceURL"_ustr, u"StyleName"_ustr };
    PresenterConfigurationAccess::ForAll(
        xStyleAssociationList,
        aProperties,
        [this] (std::vector<uno::Any> const& rValues)
        {
            return this->ProcessStyleAssociation(rValues);
        });
}

OUString StyleAssociationContainer::GetStyleName (const OUString& rsResourceName) const
{
    StyleAssociations::const_iterator iAssociation (maStyleAssociations.find(rsResourceName));
    if (iAssociation != maStyleAssociations.end())
        return iAssociation->second;
    else
        return OUString();
}

void StyleAssociationContainer::ProcessStyleAssociation(
    const ::std::vector<Any>& rValues)
{
    if (rValues.size() != 2)
        return;

    OUString sResourceURL;
    OUString sStyleName;
    if ((rValues[0] >>= sResourceURL)
        && (rValues[1] >>= sStyleName))
    {
        maStyleAssociations[sResourceURL] = sStyleName;
    }
}

} // end of anonymous namespace

} // end of namespace ::sdext::presenter

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