/* -*- 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 <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XCommand.hpp>
#include <com/sun/star/animations/Timing.hpp>
#include <com/sun/star/animations/Event.hpp>
#include <com/sun/star/animations/XAnimateMotion.hpp>
#include <com/sun/star/animations/XAnimatePhysics.hpp>
#include <com/sun/star/animations/XAnimateTransform.hpp>
#include <com/sun/star/animations/XTransitionFilter.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/animations/XAudio.hpp>
#include <com/sun/star/animations/AnimationColorSpace.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/AnimationRestart.hpp>
#include <com/sun/star/animations/EventTrigger.hpp>
#include <com/sun/star/animations/AnimationFill.hpp>
#include <com/sun/star/animations/AnimationEndSync.hpp>
#include <com/sun/star/animations/AnimationCalcMode.hpp>
#include <com/sun/star/animations/AnimationAdditiveMode.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/animations/TransitionType.hpp>
#include <com/sun/star/animations/TransitionSubType.hpp>
#include <com/sun/star/animations/ValuePair.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/presentation/TextAnimationType.hpp>
#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <o3tl/any.hxx>
#include <sax/tools/converter.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>

#include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
#include "sdpropls.hxx"
#include <xmlsdtypes.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmlement.hxx>
#include <xmloff/xmlprhdl.hxx>

#include <animations.hxx>
#include <xmloff/animationexport.hxx>

using namespace css;
using namespace ::cppu;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::beans;
using namespace ::xmloff::token;

using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::RuntimeException;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::beans::NamedValue;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XEnumeration;

namespace xmloff
{

const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Fill[] =
{
    { XML_DEFAULT,      AnimationFill::DEFAULT },
    { XML_REMOVE,       AnimationFill::REMOVE },
    { XML_FREEZE,       AnimationFill::FREEZE },
    { XML_HOLD,         AnimationFill::HOLD },
    { XML_TRANSITION,   AnimationFill::TRANSITION },
    { XML_AUTO,         AnimationFill::AUTO },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_FillDefault[] =
{
    { XML_INHERIT,      AnimationFill::INHERIT },
    { XML_REMOVE,       AnimationFill::REMOVE },
    { XML_FREEZE,       AnimationFill::FREEZE },
    { XML_HOLD,         AnimationFill::HOLD },
    { XML_TRANSITION,   AnimationFill::TRANSITION },
    { XML_AUTO,         AnimationFill::AUTO },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Restart[] =
{
    { XML_DEFAULT,      AnimationRestart::DEFAULT },
    { XML_ALWAYS,       AnimationRestart::ALWAYS },
    { XML_WHENNOTACTIVE,AnimationRestart::WHEN_NOT_ACTIVE },
    { XML_NEVER,        AnimationRestart::NEVER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_RestartDefault[] =
{
    { XML_INHERIT,      AnimationRestart::INHERIT },
    { XML_ALWAYS,       AnimationRestart::ALWAYS },
    { XML_WHENNOTACTIVE,AnimationRestart::WHEN_NOT_ACTIVE },
    { XML_NEVER,        AnimationRestart::NEVER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Endsync[] =
{
    { XML_FIRST,        AnimationEndSync::FIRST },
    { XML_LAST,         AnimationEndSync::LAST },
    { XML_ALL,          AnimationEndSync::ALL },
    { XML_MEDIA,        AnimationEndSync::MEDIA },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_CalcMode[] =
{
    { XML_DISCRETE,     AnimationCalcMode::DISCRETE },
    { XML_LINEAR,       AnimationCalcMode::LINEAR },
    { XML_PACED,        AnimationCalcMode::PACED },
    { XML_SPLINE,       AnimationCalcMode::SPLINE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_AdditiveMode[] =
{
    { XML_BASE,         AnimationAdditiveMode::BASE },
    { XML_SUM,          AnimationAdditiveMode::SUM },
    { XML_REPLACE,      AnimationAdditiveMode::REPLACE },
    { XML_MULTIPLY,     AnimationAdditiveMode::MULTIPLY },
    { XML_NONE,         AnimationAdditiveMode::NONE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransformType[] =
{
    { XML_TRANSLATE,    AnimationTransformType::TRANSLATE },
    { XML_SCALE,        AnimationTransformType::SCALE },
    { XML_ROTATE,       AnimationTransformType::ROTATE },
    { XML_SKEWX,        AnimationTransformType::SKEWX },
    { XML_SKEWY,        AnimationTransformType::SKEWY },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransitionType[] =
{
    { XML_BARWIPE,          TransitionType::BARWIPE },
    { XML_BOXWIPE,          TransitionType::BOXWIPE },
    { XML_FOURBOXWIPE,      TransitionType::FOURBOXWIPE },
    { XML_BARNDOORWIPE,     TransitionType::BARNDOORWIPE },
    { XML_DIAGONALWIPE,     TransitionType::DIAGONALWIPE },
    { XML_BOWTIEWIPE,       TransitionType::BOWTIEWIPE },
    { XML_MISCDIAGONALWIPE, TransitionType::MISCDIAGONALWIPE },
    { XML_VEEWIPE,          TransitionType::VEEWIPE },
    { XML_BARNVEEWIPE,      TransitionType::BARNVEEWIPE },
    { XML_ZIGZAGWIPE,       TransitionType::ZIGZAGWIPE },
    { XML_BARNZIGZAGWIPE,   TransitionType::BARNZIGZAGWIPE },
    { XML_IRISWIPE,         TransitionType::IRISWIPE },
    { XML_TRIANGLEWIPE,     TransitionType::TRIANGLEWIPE },
    { XML_ARROWHEADWIPE,    TransitionType::ARROWHEADWIPE },
    { XML_PENTAGONWIPE,     TransitionType::PENTAGONWIPE },
    { XML_HEXAGONWIPE,      TransitionType::HEXAGONWIPE },
    { XML_ELLIPSEWIPE,      TransitionType::ELLIPSEWIPE },
    { XML_EYEWIPE,          TransitionType::EYEWIPE },
    { XML_ROUNDRECTWIPE,    TransitionType::ROUNDRECTWIPE },
    { XML_STARWIPE,         TransitionType::STARWIPE },
    { XML_MISCSHAPEWIPE,    TransitionType::MISCSHAPEWIPE },
    { XML_CLOCKWIPE,        TransitionType::CLOCKWIPE },
    { XML_PINWHEELWIPE,     TransitionType::PINWHEELWIPE },
    { XML_SINGLESWEEPWIPE,  TransitionType::SINGLESWEEPWIPE },
    { XML_FANWIPE,          TransitionType::FANWIPE },
    { XML_DOUBLEFANWIPE,    TransitionType::DOUBLEFANWIPE },
    { XML_DOUBLESWEEPWIPE,  TransitionType::DOUBLESWEEPWIPE },
    { XML_SALOONDOORWIPE,   TransitionType::SALOONDOORWIPE },
    { XML_WINDSHIELDWIPE,   TransitionType::WINDSHIELDWIPE },
    { XML_SNAKEWIPE,        TransitionType::SNAKEWIPE },
    { XML_SPIRALWIPE,       TransitionType::SPIRALWIPE },
    { XML_PARALLELSNAKESWIPE,TransitionType::PARALLELSNAKESWIPE },
    { XML_BOXSNAKESWIPE,    TransitionType::BOXSNAKESWIPE },
    { XML_WATERFALLWIPE,    TransitionType::WATERFALLWIPE },
    { XML_PUSHWIPE,         TransitionType::PUSHWIPE },
    { XML_SLIDEWIPE,        TransitionType::SLIDEWIPE },
    { XML_FADE,             TransitionType::FADE },
    { XML_RANDOMBARWIPE,    TransitionType::RANDOMBARWIPE },
    { XML_CHECKERBOARDWIPE, TransitionType::CHECKERBOARDWIPE },
    { XML_DISSOLVE,         TransitionType::DISSOLVE },
    { XML_BLINDSWIPE,       TransitionType::BLINDSWIPE },
    { XML_RANDOM,           TransitionType::RANDOM },
    { XML_ZOOM,             TransitionType::ZOOM },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_TransitionSubType[] =
{
    { XML_DEFAULT,              TransitionSubType::DEFAULT },
    { XML_LEFTTORIGHT,          TransitionSubType::LEFTTORIGHT },
    { XML_TOPTOBOTTOM,          TransitionSubType::TOPTOBOTTOM },
    { XML_TOPLEFT,              TransitionSubType::TOPLEFT },
    { XML_TOPRIGHT,             TransitionSubType::TOPRIGHT },
    { XML_BOTTOMRIGHT,          TransitionSubType::BOTTOMRIGHT },
    { XML_BOTTOMLEFT,           TransitionSubType::BOTTOMLEFT },
    { XML_TOPCENTER,            TransitionSubType::TOPCENTER },
    { XML_RIGHTCENTER,          TransitionSubType::RIGHTCENTER },
    { XML_BOTTOMCENTER,         TransitionSubType::BOTTOMCENTER },
    { XML_LEFTCENTER,           TransitionSubType::LEFTCENTER },
    { XML_CORNERSIN,            TransitionSubType::CORNERSIN },
    { XML_CORNERSOUT,           TransitionSubType::CORNERSOUT },
    { XML_VERTICAL,             TransitionSubType::VERTICAL },
    { XML_HORIZONTAL,           TransitionSubType::HORIZONTAL },
    { XML_DIAGONALBOTTOMLEFT,   TransitionSubType::DIAGONALBOTTOMLEFT },
    { XML_DIAGONALTOPLEFT,      TransitionSubType::DIAGONALTOPLEFT },
    { XML_DOUBLEBARNDOOR,       TransitionSubType::DOUBLEBARNDOOR },
    { XML_DOUBLEDIAMOND,        TransitionSubType::DOUBLEDIAMOND },
    { XML_DOWN,                 TransitionSubType::DOWN },
    { XML_LEFT,                 TransitionSubType::LEFT },
    { XML_UP,                   TransitionSubType::UP },
    { XML_RIGHT,                TransitionSubType::RIGHT },
    { XML_RECTANGLE,            TransitionSubType::RECTANGLE },
    { XML_DIAMOND,              TransitionSubType::DIAMOND },
    { XML_CIRCLE,               TransitionSubType::CIRCLE },
    { XML_FOURPOINT,            TransitionSubType::FOURPOINT },
    { XML_FIVEPOINT,            TransitionSubType::FIVEPOINT },
    { XML_SIXPOINT,             TransitionSubType::SIXPOINT },
    { XML_HEART,                TransitionSubType::HEART },
    { XML_KEYHOLE,              TransitionSubType::KEYHOLE },
    { XML_CLOCKWISETWELVE,      TransitionSubType::CLOCKWISETWELVE },
    { XML_CLOCKWISETHREE,       TransitionSubType::CLOCKWISETHREE },
    { XML_CLOCKWISESIX,         TransitionSubType::CLOCKWISESIX },
    { XML_CLOCKWISENINE,        TransitionSubType::CLOCKWISENINE },
    { XML_TWOBLADEVERTICAL,     TransitionSubType::TWOBLADEVERTICAL },
    { XML_TWOBLADEHORIZONTAL,   TransitionSubType::TWOBLADEHORIZONTAL },
    { XML_FOURBLADE,            TransitionSubType::FOURBLADE },
    { XML_CLOCKWISETOP,         TransitionSubType::CLOCKWISETOP },
    { XML_CLOCKWISERIGHT,       TransitionSubType::CLOCKWISERIGHT },
    { XML_CLOCKWISEBOTTOM,      TransitionSubType::CLOCKWISEBOTTOM },
    { XML_CLOCKWISELEFT,        TransitionSubType::CLOCKWISELEFT },
    { XML_CLOCKWISETOPLEFT,     TransitionSubType::CLOCKWISETOPLEFT },
    { XML_COUNTERCLOCKWISEBOTTOMLEFT,TransitionSubType::COUNTERCLOCKWISEBOTTOMLEFT },
    { XML_CLOCKWISEBOTTOMRIGHT, TransitionSubType::CLOCKWISEBOTTOMRIGHT },
    { XML_COUNTERCLOCKWISETOPRIGHT,TransitionSubType::COUNTERCLOCKWISETOPRIGHT },
    { XML_CENTERTOP,            TransitionSubType::CENTERTOP },
    { XML_CENTERRIGHT,          TransitionSubType::CENTERRIGHT },
    { XML_TOP,                  TransitionSubType::TOP },
    { XML_BOTTOM,               TransitionSubType::BOTTOM },
    { XML_FANOUTVERTICAL,       TransitionSubType::FANOUTVERTICAL },
    { XML_FANOUTHORIZONTAL,     TransitionSubType::FANOUTHORIZONTAL },
    { XML_FANINVERTICAL,        TransitionSubType::FANINVERTICAL },
    { XML_FANINHORIZONTAL,      TransitionSubType::FANINHORIZONTAL },
    { XML_PARALLELVERTICAL,     TransitionSubType::PARALLELVERTICAL },
    { XML_PARALLELDIAGONAL,     TransitionSubType::PARALLELDIAGONAL },
    { XML_OPPOSITEVERTICAL,     TransitionSubType::OPPOSITEVERTICAL },
    { XML_OPPOSITEHORIZONTAL,   TransitionSubType::OPPOSITEHORIZONTAL },
    { XML_PARALLELDIAGONALTOPLEFT,TransitionSubType::PARALLELDIAGONALTOPLEFT },
    { XML_PARALLELDIAGONALBOTTOMLEFT,TransitionSubType::PARALLELDIAGONALBOTTOMLEFT },
    { XML_TOPLEFTHORIZONTAL,    TransitionSubType::TOPLEFTHORIZONTAL },
    { XML_TOPLEFTDIAGONAL,      TransitionSubType::TOPLEFTDIAGONAL },
    { XML_TOPRIGHTDIAGONAL,     TransitionSubType::TOPRIGHTDIAGONAL },
    { XML_BOTTOMRIGHTDIAGONAL,  TransitionSubType::BOTTOMRIGHTDIAGONAL },
    { XML_BOTTOMLEFTDIAGONAL,   TransitionSubType::BOTTOMLEFTDIAGONAL },
    { XML_TOPLEFTCLOCKWISE,     TransitionSubType::TOPLEFTCLOCKWISE },
    { XML_TOPRIGHTCLOCKWISE,    TransitionSubType::TOPRIGHTCLOCKWISE },
    { XML_BOTTOMRIGHTCLOCKWISE, TransitionSubType::BOTTOMRIGHTCLOCKWISE },
    { XML_BOTTOMLEFTCLOCKWISE,  TransitionSubType::BOTTOMLEFTCLOCKWISE },
    { XML_TOPLEFTCOUNTERCLOCKWISE,TransitionSubType::TOPLEFTCOUNTERCLOCKWISE },
    { XML_TOPRIGHTCOUNTERCLOCKWISE,TransitionSubType::TOPRIGHTCOUNTERCLOCKWISE },
    { XML_BOTTOMRIGHTCOUNTERCLOCKWISE,TransitionSubType::BOTTOMRIGHTCOUNTERCLOCKWISE },
    { XML_BOTTOMLEFTCOUNTERCLOCKWISE,TransitionSubType::BOTTOMLEFTCOUNTERCLOCKWISE },
    { XML_VERTICALTOPSAME,      TransitionSubType::VERTICALTOPSAME },
    { XML_VERTICALBOTTOMSAME,   TransitionSubType::VERTICALBOTTOMSAME },
    { XML_VERTICALTOPLEFTOPPOSITE,TransitionSubType::VERTICALTOPLEFTOPPOSITE },
    { XML_VERTICALBOTTOMLEFTOPPOSITE,TransitionSubType::VERTICALBOTTOMLEFTOPPOSITE },
    { XML_HORIZONTALLEFTSAME,   TransitionSubType::HORIZONTALLEFTSAME },
    { XML_HORIZONTALRIGHTSAME,  TransitionSubType::HORIZONTALRIGHTSAME },
    { XML_HORIZONTALTOPLEFTOPPOSITE,TransitionSubType::HORIZONTALTOPLEFTOPPOSITE },
    { XML_HORIZONTALTOPRIGHTOPPOSITE,TransitionSubType::HORIZONTALTOPRIGHTOPPOSITE },
    { XML_DIAGONALBOTTOMLEFTOPPOSITE,TransitionSubType::DIAGONALBOTTOMLEFTOPPOSITE },
    { XML_DIAGONALTOPLEFTOPPOSITE,TransitionSubType::DIAGONALTOPLEFTOPPOSITE },
    { XML_TWOBOXTOP,            TransitionSubType::TWOBOXTOP },
    { XML_TWOBOXBOTTOM,         TransitionSubType::TWOBOXBOTTOM },
    { XML_TWOBOXLEFT,           TransitionSubType::TWOBOXLEFT },
    { XML_TWOBOXRIGHT,          TransitionSubType::TWOBOXRIGHT },
    { XML_FOURBOXVERTICAL,      TransitionSubType::FOURBOXVERTICAL },
    { XML_FOURBOXHORIZONTAL,    TransitionSubType::FOURBOXHORIZONTAL },
    { XML_VERTICALLEFT,         TransitionSubType::VERTICALLEFT },
    { XML_VERTICALRIGHT,        TransitionSubType::VERTICALRIGHT },
    { XML_HORIZONTALLEFT,       TransitionSubType::HORIZONTALLEFT },
    { XML_HORIZONTALRIGHT,      TransitionSubType::HORIZONTALRIGHT },
    { XML_FROMLEFT,             TransitionSubType::FROMLEFT },
    { XML_FROMTOP,              TransitionSubType::FROMTOP },
    { XML_FROMRIGHT,            TransitionSubType::FROMRIGHT },
    { XML_FROMBOTTOM,           TransitionSubType::FROMBOTTOM },
    { XML_CROSSFADE,            TransitionSubType::CROSSFADE },
    { XML_FADETOCOLOR,          TransitionSubType::FADETOCOLOR },
    { XML_FADEFROMCOLOR,        TransitionSubType::FADEFROMCOLOR },
    { XML_FADEOVERCOLOR,        TransitionSubType::FADEOVERCOLOR },
    { XML_THREEBLADE,           TransitionSubType::THREEBLADE },
    { XML_EIGHTBLADE,           TransitionSubType::EIGHTBLADE },
    { XML_ONEBLADE,             TransitionSubType::ONEBLADE },
    { XML_ACROSS,               TransitionSubType::ACROSS },
    { XML_TOPLEFTVERTICAL,      TransitionSubType::TOPLEFTVERTICAL },
    { XML_COMBHORIZONTAL,       TransitionSubType::COMBHORIZONTAL },
    { XML_COMBVERTICAL,         TransitionSubType::COMBVERTICAL },
    { XML_IN,                   TransitionSubType::IN },
    { XML_OUT,                  TransitionSubType::OUT },
    { XML_ROTATEIN,             TransitionSubType::ROTATEIN },
    { XML_ROTATEOUT,            TransitionSubType::ROTATEOUT },
    { XML_FROMTOPLEFT,          TransitionSubType::FROMTOPLEFT },
    { XML_FROMTOPRIGHT,         TransitionSubType::FROMTOPRIGHT },
    { XML_FROMBOTTOMLEFT,       TransitionSubType::FROMBOTTOMLEFT },
    { XML_FROMBOTTOMRIGHT,      TransitionSubType::FROMBOTTOMRIGHT },

    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EventTrigger[] =
{
    { XML_ONBEGIN,          EventTrigger::ON_BEGIN },
    { XML_ONEND,            EventTrigger::ON_END },
    { XML_BEGIN,            EventTrigger::BEGIN_EVENT },
    { XML_END,              EventTrigger::END_EVENT },
    { XML_CLICK,            EventTrigger::ON_CLICK },
    { XML_DOUBLECLICK,      EventTrigger::ON_DBL_CLICK },
    { XML_MOUSEOVER,        EventTrigger::ON_MOUSE_ENTER },
    { XML_MOUSEOUT,         EventTrigger::ON_MOUSE_LEAVE },
    { XML_NEXT,             EventTrigger::ON_NEXT },
    { XML_PREVIOUS,         EventTrigger::ON_PREV },
    { XML_STOP_AUDIO,       EventTrigger::ON_STOP_AUDIO },
    { XML_REPEAT,           EventTrigger::REPEAT },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EffectPresetClass[] =
{
    { XML_CUSTOM,       EffectPresetClass::CUSTOM },
    { XML_ENTRANCE,     EffectPresetClass::ENTRANCE },
    { XML_EXIT,         EffectPresetClass::EXIT },
    { XML_EMPHASIS,     EffectPresetClass::EMPHASIS },
    { XML_MOTION_PATH,  EffectPresetClass::MOTIONPATH },
    { XML_OLE_ACTION,   EffectPresetClass::OLEACTION },
    { XML_MEDIA_CALL,   EffectPresetClass::MEDIACALL },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_EffectNodeType[] =
{
    { XML_DEFAULT,                  EffectNodeType::DEFAULT },
    { XML_ON_CLICK,                 EffectNodeType::ON_CLICK },
    { XML_WITH_PREVIOUS,            EffectNodeType::WITH_PREVIOUS },
    { XML_AFTER_PREVIOUS,           EffectNodeType::AFTER_PREVIOUS },
    { XML_MAIN_SEQUENCE,            EffectNodeType::MAIN_SEQUENCE },
    { XML_TIMING_ROOT,              EffectNodeType::TIMING_ROOT },
    { XML_INTERACTIVE_SEQUENCE,     EffectNodeType::INTERACTIVE_SEQUENCE },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_SubItem[] =
{
    { XML_WHOLE,                    ShapeAnimationSubType::AS_WHOLE },
    { XML_BACKGROUND,               ShapeAnimationSubType::ONLY_BACKGROUND },
    { XML_TEXT,                     ShapeAnimationSubType::ONLY_TEXT },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_IterateType[] =
{
    { XML_BY_PARAGRAPH,             TextAnimationType::BY_PARAGRAPH },
    { XML_BY_WORD,                  TextAnimationType::BY_WORD },
    { XML_BY_LETTER,                TextAnimationType::BY_LETTER },
    { XML_TOKEN_INVALID, 0 }
};
const SvXMLEnumMapEntry<sal_Int16> aAnimations_EnumMap_Command[] =
{
    { XML_CUSTOM,                   EffectCommands::CUSTOM },
    { XML_VERB,                     EffectCommands::VERB },
    { XML_PLAY,                     EffectCommands::PLAY },
    { XML_TOGGLE_PAUSE,             EffectCommands::TOGGLEPAUSE },
    { XML_STOP,                     EffectCommands::STOP },
    { XML_STOP_AUDIO,               EffectCommands::STOPAUDIO },
    { XML_TOKEN_INVALID, 0 }
};

const struct ImplAttributeNameConversion* getAnimationAttributeNamesConversionList()
{
    static constexpr struct ImplAttributeNameConversion gImplConversionList[]
    {
        { XML_X,                        u"X"_ustr },
        { XML_Y,                        u"Y"_ustr },
        { XML_WIDTH,                    u"Width"_ustr },
        { XML_HEIGHT,                   u"Height"_ustr },
        { XML_ROTATE,                   u"Rotate"_ustr },
        { XML_SKEWX,                    u"SkewX"_ustr },
        { XML_FILL_COLOR,               u"FillColor"_ustr },
        { XML_FILL,                     u"FillStyle"_ustr },
        { XML_STROKE_COLOR,             u"LineColor"_ustr },
        { XML_STROKE,                   u"LineStyle"_ustr },
        { XML_COLOR,                    u"CharColor"_ustr },
        { XML_TEXT_ROTATION_ANGLE,      u"CharRotation"_ustr },
        { XML_FONT_WEIGHT,              u"CharWeight"_ustr },
        { XML_TEXT_UNDERLINE,           u"CharUnderline"_ustr },
        { XML_FONT_FAMILY,              u"CharFontName"_ustr },
        { XML_FONT_SIZE,                u"CharHeight"_ustr },
        { XML_FONT_STYLE,               u"CharPosture"_ustr },
        { XML_VISIBILITY,               u"Visibility"_ustr },
        { XML_OPACITY,                  u"Opacity"_ustr },
        { XML_DIM,                      u"DimColor"_ustr },
        { XML_TOKEN_INVALID,            u""_ustr }
    };

    return gImplConversionList;
}


class AnimationsExporterImpl
{
public:
    AnimationsExporterImpl( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps );

    void prepareNode( const Reference< XAnimationNode >& xNode );
    void exportNode( const Reference< XAnimationNode >& xNode );

    void exportContainer( const Reference< XTimeContainer >& xNode, sal_Int16 nContainerNodeType );
    void exportAnimate( const Reference< XAnimate >& xNode );
    void exportAudio( const Reference< XAudio >& xAudio );
    void exportCommand( const Reference< XCommand >& xCommand );

    static Reference< XInterface > getParagraphTarget( const ParagraphTarget& pTarget );

    static void convertPath( OUStringBuffer& sTmp, const Any& rPath );
    bool convertValue( XMLTokenEnum eAttributeName, OUStringBuffer& sTmp, const Any& rValue ) const;
    void convertTiming( OUStringBuffer& sTmp, const Any& rTiming ) const;
    void convertTarget( OUStringBuffer& sTmp, const Any& rTarget ) const;

    void prepareValue( const Any& rValue );

    void exportTransitionNode();
    void prepareTransitionNode();

    bool mbHasTransition;
private:
    rtl::Reference<SvXMLExport> mxExport;
    Reference< XPropertySet > mxPageProps;
    rtl::Reference<XMLSdPropHdlFactory> mxSdPropHdlFactory;
};

AnimationsExporterImpl::AnimationsExporterImpl( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps )
: mbHasTransition(false)
, mxExport( &rExport )
, mxPageProps( xPageProps )
, mxSdPropHdlFactory(new XMLSdPropHdlFactory( rExport.GetModel(), rExport ))
{
}


/** split a uri hierarchy into first segment and rest */
static bool splitPath(OUString const & i_rPath,
    OUString & o_rDir, OUString& o_rRest)
{
    const sal_Int32 idx(i_rPath.indexOf(u'/'));
    if (idx < 0 || idx >= i_rPath.getLength()) {
        o_rDir = OUString();
        o_rRest = i_rPath;
        return true;
    } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
        // input must not start or end with '/'
        return false;
    } else {
        o_rDir  = i_rPath.copy(0, idx);
        o_rRest = i_rPath.copy(idx+1);
        return true;
    }
}

static void lcl_CopyStream(
        uno::Reference<embed::XStorage> const& xSource,
        uno::Reference<embed::XStorage> const& xTarget,
         OUString const& rPath)
{
    OUString dir;
    OUString rest;
    if (!splitPath(rPath, dir, rest))
        throw uno::RuntimeException();

    if (dir.getLength() == 0)
        xSource->copyElementTo(rPath, xTarget, rPath);
    else
    {
        uno::Reference<embed::XStorage> const xSubSource(
            xSource->openStorageElement(dir, embed::ElementModes::READ));
        uno::Reference<embed::XStorage> const xSubTarget(
            xTarget->openStorageElement(dir, embed::ElementModes::WRITE));
        lcl_CopyStream(xSubSource, xSubTarget, rest);
    }
    uno::Reference<embed::XTransactedObject> const xTransaction(xTarget, uno::UNO_QUERY);
    if (xTransaction.is())
        xTransaction->commit();
}

char const s_PkgScheme[] = "vnd.sun.star.Package:";

static OUString lcl_StoreMediaAndGetURL(SvXMLExport & rExport, OUString const& rURL)
{
    if (rURL.startsWithIgnoreAsciiCase(s_PkgScheme))
    {
        try // video is embedded
        {
            // copy the media stream from document storage to target storage
            // (not sure if this is the best way to store these?)
            uno::Reference<document::XStorageBasedDocument> const xSBD(
                    rExport.GetModel(), uno::UNO_QUERY_THROW);
            uno::Reference<embed::XStorage> const xSource(
                    xSBD->getDocumentStorage(), uno::UNO_SET_THROW);
            uno::Reference<embed::XStorage> const xTarget(
                    rExport.GetTargetStorage(), uno::UNO_SET_THROW);

            OUString urlPath = rURL.copy(SAL_N_ELEMENTS(s_PkgScheme)-1);

            lcl_CopyStream(xSource, xTarget, urlPath);

            return urlPath;
        }
        catch (uno::Exception const&)
        {
            TOOLS_INFO_EXCEPTION("xmloff", "exception while storing embedded media");
        }
        return OUString();
    }
    else
    {
        return rExport.GetRelativeReference(rURL); // linked
    }
}

void AnimationsExporterImpl::exportTransitionNode()
{
    if( !(mbHasTransition && mxPageProps.is()) )
        return;

    sal_Int16 nTransition = 0;
    mxPageProps->getPropertyValue(u"TransitionType"_ustr) >>= nTransition;

    Any aSound( mxPageProps->getPropertyValue(u"Sound"_ustr) );
    OUString sSoundURL;
    aSound >>= sSoundURL;
    bool bStopSound = false;
    if( !(aSound >>= bStopSound) )
        bStopSound = false;


    OUStringBuffer sTmp;
    if( !((nTransition != 0) || !sSoundURL.isEmpty() || bStopSound) )
        return;

    Reference< XInterface > xSource( mxPageProps );
    Event aEvent;
    aEvent.Source <<= xSource;
    aEvent.Trigger = EventTrigger::BEGIN_EVENT;
    aEvent.Repeat = 0;

    convertTiming( sTmp, Any( aEvent ) );
    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BEGIN, sTmp.makeStringAndClear() );

    SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_PAR, true, true );

    if( nTransition != 0 )
    {
        sal_Int16 nSubtype = 0;
        bool bDirection = false;
        sal_Int32 nFadeColor = 0;
        double fDuration = 0.0;
        mxPageProps->getPropertyValue(u"TransitionSubtype"_ustr) >>= nSubtype;
        mxPageProps->getPropertyValue(u"TransitionDirection"_ustr) >>= bDirection;
        mxPageProps->getPropertyValue(u"TransitionFadeColor"_ustr) >>= nFadeColor;
        mxPageProps->getPropertyValue(u"TransitionDuration"_ustr) >>= fDuration;

        ::sax::Converter::convertDouble( sTmp, fDuration );
        sTmp.append( 's');
        mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, sTmp.makeStringAndClear() );

        SvXMLUnitConverter::convertEnum( sTmp, nTransition, aAnimations_EnumMap_TransitionType );
        mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TYPE, sTmp.makeStringAndClear() );

        if( nSubtype != TransitionSubType::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nSubtype, aAnimations_EnumMap_TransitionSubType );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_SUBTYPE, sTmp.makeStringAndClear() );
        }

        if( !bDirection )
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DIRECTION, XML_REVERSE );

        if( (nTransition == TransitionType::FADE)
                && ((nSubtype == TransitionSubType::FADETOCOLOR) || (nSubtype == TransitionSubType::FADEFROMCOLOR)
                    || (nSubtype == TransitionSubType::FADEOVERCOLOR)))
        {
            ::sax::Converter::convertColor( sTmp, nFadeColor );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FADECOLOR, sTmp.makeStringAndClear() );
        }
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_TRANSITIONFILTER, true, true );
    }

    if( bStopSound )
    {
        mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COMMAND, XML_STOP_AUDIO );
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_COMMAND, true, true );
    }
    else if( !sSoundURL.isEmpty())
    {
        sSoundURL = lcl_StoreMediaAndGetURL(*mxExport, sSoundURL);
        mxExport->AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sSoundURL );

        bool bLoopSound = false;
        mxPageProps->getPropertyValue(u"LoopSound"_ustr) >>= bLoopSound;

        if( bLoopSound )
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, XML_INDEFINITE );
        SvXMLElementExport aElement2( *mxExport, XML_NAMESPACE_ANIMATION, XML_AUDIO, true, true );
    }
}

void AnimationsExporterImpl::prepareTransitionNode()
{
    if( !mxPageProps.is() )
        return;

    try
    {
        sal_Int16 nTransition = 0;
        mxPageProps->getPropertyValue(u"TransitionType"_ustr) >>= nTransition;

        bool bStopSound = false;
        OUString sSoundURL;

        if( nTransition == 0 )
        {
            Any aSound( mxPageProps->getPropertyValue(u"Sound"_ustr) );
            aSound >>= sSoundURL;

            if( !(aSound >>= bStopSound) )
                bStopSound = false;
        }

        if( (nTransition != 0) || !sSoundURL.isEmpty() || bStopSound )
        {
            mbHasTransition = true;
            Reference< XInterface > xInt( mxPageProps );
            mxExport->getInterfaceToIdentifierMapper().registerReference( xInt );
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporterImpl::prepareNode( const Reference< XAnimationNode >& xNode )
{
    try
    {
        prepareValue( xNode->getBegin() );
        prepareValue( xNode->getEnd() );

        sal_Int16 nNodeType = xNode->getType();
        switch( nNodeType )
        {
        case AnimationNodeType::ITERATE:
        {
            Reference< XIterateContainer > xIter( xNode, UNO_QUERY_THROW );
            prepareValue( xIter->getTarget() );
            [[fallthrough]];
        }
        case AnimationNodeType::PAR:
        case AnimationNodeType::SEQ:
        {
            Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
            Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
            while( xEnumeration->hasMoreElements() )
            {
                Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
                prepareNode( xChildNode );
            }
        }
        break;

        case AnimationNodeType::ANIMATE:
        case AnimationNodeType::SET:
        case AnimationNodeType::ANIMATEMOTION:
        case AnimationNodeType::ANIMATEPHYSICS:
        case AnimationNodeType::ANIMATECOLOR:
        case AnimationNodeType::ANIMATETRANSFORM:
        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
            prepareValue( xAnimate->getTarget() );
        }
        break;

        case AnimationNodeType::COMMAND:
        {
            Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW );
            prepareValue( xCommand->getTarget() );
        }
        break;

        case AnimationNodeType::AUDIO:
        {
            Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW );
            prepareValue( xAudio->getSource() );
        }
        break;
        }

        const Sequence< NamedValue > aUserData( xNode->getUserData() );
        for( const auto& rValue : aUserData )
        {
            if( IsXMLToken( rValue.Name, XML_MASTER_ELEMENT ) )
            {
                Reference< XInterface > xMaster;
                rValue.Value >>= xMaster;
                if( xMaster.is() )
                    mxExport->getInterfaceToIdentifierMapper().registerReference( xMaster );
            }
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporterImpl::exportNode( const Reference< XAnimationNode >& xNode )
{
    try
    {
        OUStringBuffer sTmp;

        const OUString& rExportIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier( xNode );
        if( !rExportIdentifier.isEmpty() )
        {
            mxExport->AddAttributeIdLegacy(
                XML_NAMESPACE_ANIMATION, rExportIdentifier);
        }

        Any aTemp( xNode->getBegin() );
        if( aTemp.hasValue() )
        {
            convertTiming( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BEGIN, sTmp.makeStringAndClear() );
        }

        double fTemp = 0;
        sal_Int16 nTemp;

        aTemp = xNode->getDuration();
        if( aTemp.hasValue() )
        {
            if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                sTmp.append( 's');
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, sTmp.makeStringAndClear() );
            }
            else
            {
                Timing eTiming;
                if( aTemp >>= eTiming )
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DUR, eTiming == Timing_INDEFINITE ? XML_INDEFINITE : XML_MEDIA );
            }
        }

        aTemp = xNode->getEnd();
        if( aTemp.hasValue() )
        {
            convertTiming( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_END, sTmp.makeStringAndClear() );
        }

        nTemp = xNode->getFill();
        if( nTemp != AnimationFill::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Fill );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FILL, sTmp.makeStringAndClear() );
        }

        nTemp = xNode->getFillDefault();
        if( nTemp != AnimationFill::INHERIT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_FillDefault );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FILLDEFAULT, sTmp.makeStringAndClear() );
        }

        nTemp = xNode->getRestart();
        if( nTemp != AnimationRestart::DEFAULT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Restart );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_RESTART, sTmp.makeStringAndClear() );
        }

        nTemp = xNode->getRestartDefault();
        if( nTemp != AnimationRestart::INHERIT )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_RestartDefault );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_RESTARTDEFAULT, sTmp.makeStringAndClear() );
        }

        fTemp = xNode->getAcceleration();
        if( fTemp != 0.0 )
        {
            ::sax::Converter::convertDouble( sTmp, fTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ACCELERATE, sTmp.makeStringAndClear() );
        }

        fTemp = xNode->getDecelerate();
        if( fTemp != 0.0 )
        {
            ::sax::Converter::convertDouble( sTmp, fTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DECELERATE, sTmp.makeStringAndClear() );
        }

        bool bTemp = xNode->getAutoReverse();
        if( bTemp )
        {
            ::sax::Converter::convertBool( sTmp, bTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_AUTOREVERSE, sTmp.makeStringAndClear() );
        }

        aTemp = xNode->getRepeatCount();
        if( aTemp.hasValue() )
        {
            Timing eTiming;
            if( (aTemp >>= eTiming ) && (eTiming == Timing_INDEFINITE ) )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, XML_INDEFINITE );
            else if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATCOUNT, sTmp.makeStringAndClear() );
            }
        }

        aTemp = xNode->getRepeatDuration();
        if( aTemp.hasValue() )
        {
            Timing eTiming;
            if( ( aTemp >>= eTiming ) && (eTiming == Timing_INDEFINITE) )
            {
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATDUR, XML_INDEFINITE );
            }
            else if( aTemp >>= fTemp )
            {
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_REPEATDUR, sTmp.makeStringAndClear() );
            }
        }

        aTemp = xNode->getEndSync();
        if( aTemp.hasValue() && (aTemp >>= nTemp) )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_Endsync );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ENDSYNC, sTmp.makeStringAndClear() );
        }

        sal_Int16 nContainerNodeType = EffectNodeType::DEFAULT;
        OUString aPresetId;
        const Sequence< NamedValue > aUserData( xNode->getUserData() );
        for( const auto& rValue : aUserData )
        {
            if( IsXMLToken( rValue.Name, XML_NODE_TYPE ) )
            {
                if( (rValue.Value >>= nContainerNodeType) && (nContainerNodeType != EffectNodeType::DEFAULT) )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nContainerNodeType, aAnimations_EnumMap_EffectNodeType );
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_NODE_TYPE, sTmp.makeStringAndClear() );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_ID ) )
            {
                if( rValue.Value >>= aPresetId )
                {
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_ID, aPresetId );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_SUB_TYPE ) )
            {
                OUString aPresetSubType;
                if( rValue.Value >>= aPresetSubType )
                {
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_SUB_TYPE, aPresetSubType );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_PRESET_CLASS ) )
            {
                sal_Int16 nEffectPresetClass = sal_uInt16();
                if( rValue.Value >>= nEffectPresetClass )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nEffectPresetClass, aAnimations_EnumMap_EffectPresetClass );
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_PRESET_CLASS, sTmp.makeStringAndClear() );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_MASTER_ELEMENT ) )
            {
                Reference< XInterface > xMaster;
                rValue.Value >>= xMaster;
                if( xMaster.is() )
                {
                    const OUString& rIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier(xMaster);
                    if( !rIdentifier.isEmpty() )
                        mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_MASTER_ELEMENT, rIdentifier );
                }
            }
            else if( IsXMLToken( rValue.Name, XML_GROUP_ID ) )
            {
                sal_Int32 nGroupId = 0;
                if( rValue.Value >>= nGroupId )
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, XML_GROUP_ID, OUString::number( nGroupId ) );
            }
            else
            {
                OUString aTmp;
                if( rValue.Value >>= aTmp )
                    mxExport->AddAttribute( XML_NAMESPACE_PRESENTATION, rValue.Name, aTmp );
            }
        }

        nTemp = xNode->getType();
        switch( nTemp )
        {
        case AnimationNodeType::PAR:
        case AnimationNodeType::SEQ:
        case AnimationNodeType::ITERATE:
        {
            Reference< XTimeContainer > xContainer( xNode, UNO_QUERY_THROW );
            exportContainer( xContainer, nContainerNodeType );
        }
        break;

        case AnimationNodeType::ANIMATE:
        case AnimationNodeType::SET:
        case AnimationNodeType::ANIMATEMOTION:
        case AnimationNodeType::ANIMATEPHYSICS:
        case AnimationNodeType::ANIMATECOLOR:
        case AnimationNodeType::ANIMATETRANSFORM:
        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
            exportAnimate( xAnimate );
        }
        break;
        case AnimationNodeType::AUDIO:
        {
            Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW );
            exportAudio( xAudio );
        }
        break;
        case AnimationNodeType::COMMAND:
        {
            Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW );
            exportCommand( xCommand );
        }
        break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::exportNode(), invalid AnimationNodeType!" );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }

    // if something goes wrong, it's always a good idea to clear the attribute list
    mxExport->ClearAttrList();
}

void AnimationsExporterImpl::exportContainer( const Reference< XTimeContainer >& xContainer, sal_Int16 nContainerNodeType )
{
    try
    {
        const sal_Int32 nNodeType = xContainer->getType();

        if( nNodeType == AnimationNodeType::ITERATE )
        {
            OUStringBuffer sTmp;
            Reference< XIterateContainer > xIter( xContainer, UNO_QUERY_THROW );

            Any aTemp( xIter->getTarget() );
            if( aTemp.hasValue() )
            {
                convertTarget( sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
            }

            sal_Int16 nTemp = xIter->getSubItem();
            if( nTemp )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_SubItem );
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_SUB_ITEM, sTmp.makeStringAndClear() );
            }

            nTemp = xIter->getIterateType();
            if( nTemp )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_IterateType );
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_ITERATE_TYPE, sTmp.makeStringAndClear() );
            }

            double fTemp = xIter->getIterateInterval();
            if( fTemp )
            {
                OUStringBuffer buf;
                ::sax::Converter::convertDuration(buf, fTemp / (24*60*60));
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION,
                        XML_ITERATE_INTERVAL, buf.makeStringAndClear());
            }
        }

        XMLTokenEnum eElementToken;
        switch( nNodeType )
        {
        case AnimationNodeType::PAR:    eElementToken = XML_PAR; break;
        case AnimationNodeType::SEQ:    eElementToken = XML_SEQ; break;
        case AnimationNodeType::ITERATE:eElementToken = XML_ITERATE; break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::exportContainer(), invalid TimeContainerType!" );
            return;
        }
        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, eElementToken, true, true );

        if( nContainerNodeType == EffectNodeType::TIMING_ROOT )
            exportTransitionNode();

        Reference< XEnumerationAccess > xEnumerationAccess( xContainer, UNO_QUERY_THROW );
        Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
        while( xEnumeration->hasMoreElements() )
        {
            Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
            exportNode( xChildNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporterImpl::exportAnimate( const Reference< XAnimate >& xAnimate )
{
    try
    {
        const sal_Int16 nNodeType = xAnimate->getType();

        OUStringBuffer sTmp;
        sal_Int16 nTemp;
        bool bTemp;

        Any aTemp( xAnimate->getTarget() );
        if( aTemp.hasValue() )
        {
            convertTarget( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
        }

        nTemp = xAnimate->getSubItem();
        if( nTemp )
        {
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_SubItem );
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_SUB_ITEM, sTmp.makeStringAndClear() );
        }

        XMLTokenEnum eAttributeName = XML_TOKEN_INVALID;

        if( nNodeType == AnimationNodeType::TRANSITIONFILTER )
        {
            eAttributeName = XML_TRANSITIONFILTER;
        }
        else if( nNodeType == AnimationNodeType::ANIMATETRANSFORM )
        {
            eAttributeName = XML_ANIMATETRANSFORM;
        }
        else if( nNodeType == AnimationNodeType::ANIMATEMOTION )
        {
            eAttributeName = XML_ANIMATEMOTION;
        }
        else if( nNodeType == AnimationNodeType::ANIMATEPHYSICS )
        {
            eAttributeName = XML_ANIMATEPHYSICS;
        }
        else
        {
            OUString sTemp( xAnimate->getAttributeName() );
            if( !sTemp.isEmpty() )
            {
                const struct ImplAttributeNameConversion* p = getAnimationAttributeNamesConversionList();
                while( !p->maAPIName.isEmpty() )
                {
                    if( sTemp == p->maAPIName )
                    {
                        sTemp = GetXMLToken( p->meXMLToken );
                        eAttributeName = p->meXMLToken;
                        break;
                    }

                    p++;
                }

                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, sTemp );
            }
            else
            {
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, u"invalid"_ustr );
            }
        }

        bool bExportedValues = false;
        Sequence< Any > aValues( xAnimate->getValues() );
        if( aValues.hasElements() )
        {
            aTemp <<= aValues;
            bExportedValues = convertValue(eAttributeName, sTmp, aTemp);
            SAL_WARN_IF(!bExportedValues, "xmloff", "exportAnimate(), unable to export smil:values");
            if (bExportedValues)
                mxExport->AddAttribute(XML_NAMESPACE_SMIL, XML_VALUES, sTmp.makeStringAndClear());
        }
        else
        {
            aTemp = xAnimate->getFrom();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FROM, sTmp.makeStringAndClear() );
            }

            aTemp = xAnimate->getBy();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_BY, sTmp.makeStringAndClear() );
            }

            aTemp = xAnimate->getTo();
            if( aTemp.hasValue() )
            {
                convertValue( eAttributeName, sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TO, sTmp.makeStringAndClear() );
            }
        }

        if(nNodeType != AnimationNodeType::SET)
        {
            const Sequence< double > aKeyTimes( xAnimate->getKeyTimes() );
            SAL_WARN_IF(aKeyTimes.hasElements() && !bExportedValues, "xmloff", "exportAnimate() no smil:values exported so skipping smil:keyTimes");
            if (aKeyTimes.hasElements() && bExportedValues)
            {
                for( const auto& rKeyTime : aKeyTimes )
                {
                    if( !sTmp.isEmpty() )
                        sTmp.append( ';' );

                    sTmp.append( rKeyTime );
                }
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_KEYTIMES, sTmp.makeStringAndClear() );
            }

            OUString sTemp( xAnimate->getFormula() );
            if( !sTemp.isEmpty() )
                mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_FORMULA, sTemp );

            if( (nNodeType != AnimationNodeType::TRANSITIONFILTER) &&
                (nNodeType != AnimationNodeType::AUDIO ) )
            {
                // calcMode  = "discrete | linear | paced | spline"
                nTemp = xAnimate->getCalcMode();
                if( ((nNodeType == AnimationNodeType::ANIMATEMOTION ) && (nTemp != AnimationCalcMode::PACED)) ||
                    ((nNodeType != AnimationNodeType::ANIMATEMOTION ) && (nTemp != AnimationCalcMode::LINEAR)) )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_CalcMode );
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_CALCMODE, sTmp.makeStringAndClear() );
                }

                bTemp = xAnimate->getAccumulate();
                if( bTemp )
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ACCUMULATE, XML_SUM );

                nTemp = xAnimate->getAdditive();
                if( nTemp != AnimationAdditiveMode::REPLACE )
                {
                    SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_AdditiveMode );
                    mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ADDITIVE, sTmp.makeStringAndClear() );
                }
            }

            const Sequence< TimeFilterPair > aTimeFilter( xAnimate->getTimeFilter() );
            if( aTimeFilter.hasElements() )
            {
                for( const auto& rPair : aTimeFilter )
                {
                    if( !sTmp.isEmpty() )
                        sTmp.append( ';' );

                    sTmp.append(OUString::number(rPair.Time) + "," + OUString::number(rPair.Progress));
                }

                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_KEYSPLINES, sTmp.makeStringAndClear() );
            }
        }

        XMLTokenEnum eElementToken = XML_ANIMATE;

        switch( nNodeType )
        {
        case AnimationNodeType::ANIMATE:
            eElementToken = XML_ANIMATE;
            break;

        case AnimationNodeType::SET:
            eElementToken = XML_SET;
            break;

        case AnimationNodeType::ANIMATEMOTION:
        {
            eElementToken = XML_ANIMATEMOTION;

            Reference< XAnimateMotion > xAnimateMotion( xAnimate, UNO_QUERY_THROW );

            aTemp = xAnimateMotion->getPath();
            if( aTemp.hasValue() )
            {
                convertPath( sTmp, aTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SVG, XML_PATH, sTmp.makeStringAndClear() );
            }

            // TODO: origin = ( parent | layout )
            aTemp = xAnimateMotion->getOrigin();
        }
        break;

        case AnimationNodeType::ANIMATEPHYSICS:
        {
            eElementToken = XML_ANIMATEPHYSICS;
            double fTemp = 0;

            Reference< XAnimatePhysics > xAnimatePhysics( xAnimate, UNO_QUERY_THROW );
            aTemp = xAnimatePhysics->getStartVelocityX();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_START_VELOCITY_X, sTmp.makeStringAndClear() );
            }

            aTemp = xAnimatePhysics->getStartVelocityY();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_START_VELOCITY_Y, sTmp.makeStringAndClear() );
            }

            aTemp = xAnimatePhysics->getDensity();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_DENSITY, sTmp.makeStringAndClear() );
            }

            aTemp = xAnimatePhysics->getBounciness();
            if( aTemp.hasValue() )
            {
                aTemp >>= fTemp;
                ::sax::Converter::convertDouble( sTmp, fTemp );
                mxExport->AddAttribute( XML_NAMESPACE_LO_EXT, XML_PHYSICS_ANIMATION_BOUNCINESS, sTmp.makeStringAndClear() );
            }
        }
        break;

        case AnimationNodeType::ANIMATECOLOR:
        {
            eElementToken = XML_ANIMATECOLOR;

            Reference< XAnimateColor > xAnimateColor( xAnimate, UNO_QUERY_THROW );

            nTemp = xAnimateColor->getColorInterpolation();
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COLOR_INTERPOLATION, (nTemp == AnimationColorSpace::RGB) ? XML_RGB : XML_HSL );

            bTemp = xAnimateColor->getDirection();
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COLOR_INTERPOLATION_DIRECTION, bTemp ? XML_CLOCKWISE : XML_COUNTER_CLOCKWISE );
        }
        break;

        case AnimationNodeType::ANIMATETRANSFORM:
        {
            eElementToken = XML_ANIMATETRANSFORM;

            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_ATTRIBUTENAME, XML_TRANSFORM );

            Reference< XAnimateTransform > xTransform( xAnimate, UNO_QUERY_THROW );
            nTemp = xTransform->getTransformType();
            SvXMLUnitConverter::convertEnum( sTmp, nTemp, aAnimations_EnumMap_TransformType );
            mxExport->AddAttribute( XML_NAMESPACE_SVG, XML_TYPE, sTmp.makeStringAndClear() );
        }
        break;

        case AnimationNodeType::TRANSITIONFILTER:
        {
            Reference< XTransitionFilter > xTransitionFilter( xAnimate, UNO_QUERY );
            eElementToken = XML_TRANSITIONFILTER;

            sal_Int16 nTransition = xTransitionFilter->getTransition();
            SvXMLUnitConverter::convertEnum( sTmp, nTransition, aAnimations_EnumMap_TransitionType );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TYPE, sTmp.makeStringAndClear() );

            sal_Int16 nSubtype = xTransitionFilter->getSubtype();
            if( nSubtype != TransitionSubType::DEFAULT )
            {
                SvXMLUnitConverter::convertEnum( sTmp, nSubtype, aAnimations_EnumMap_TransitionSubType );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_SUBTYPE, sTmp.makeStringAndClear() );
            }

            bTemp = xTransitionFilter->getMode();
            if( !bTemp )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_MODE, XML_OUT );

            bTemp = xTransitionFilter->getDirection();
            if( !bTemp )
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_DIRECTION, XML_REVERSE );

            if( (nTransition == TransitionType::FADE) && ((nSubtype == TransitionSubType::FADETOCOLOR) || (nSubtype == TransitionSubType::FADEFROMCOLOR) ))
            {
                nTemp = xTransitionFilter->getFadeColor();
                ::sax::Converter::convertColor( sTmp, nTemp );
                mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_FADECOLOR, sTmp.makeStringAndClear() );
            }
        }
        break;
        }

        if( eElementToken == XML_ANIMATEPHYSICS ) // not a standard should use the extension namespace
        {
            SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_LO_EXT, eElementToken, true, true );
        }
        else
        {
            SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, eElementToken, true, true );
        }

    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporterImpl::exportAudio( const Reference< XAudio >& xAudio )
{
    if( !xAudio.is() )
        return;

    try
    {
        OUString aSourceURL;
        xAudio->getSource() >>= aSourceURL;
        if( !aSourceURL.isEmpty() )
            mxExport->AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, mxExport->GetRelativeReference( aSourceURL ) );

        const double fVolume = xAudio->getVolume();
        if( fVolume != 1.0 )
        {
            OUStringBuffer sTmp;
            ::sax::Converter::convertDouble( sTmp, fVolume );
            mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_AUDIO_LEVEL, sTmp.makeStringAndClear() );
        }

    /* todo?
       sal_Int32 nEndAfterSlide = 0;
        xAudio->getEndAfterSlide() >>= nEndAfterSlide;
        if( nEndAfterSlide != 0 )
            mxExport->AddAttribute( );
    */
        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_AUDIO, true, true );

    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporterImpl::exportCommand( const Reference< XCommand >& xCommand )
{
    if( !xCommand.is() )
        return;

    try
    {
        OUStringBuffer sTmp;
        Any aTemp( xCommand->getTarget() );
        if( aTemp.hasValue() )
        {
            convertTarget( sTmp, aTemp );
            mxExport->AddAttribute( XML_NAMESPACE_SMIL, XML_TARGETELEMENT, sTmp.makeStringAndClear() );
        }

        sal_Int16 nCommand = xCommand->getCommand();
        SvXMLUnitConverter::convertEnum( sTmp, nCommand, aAnimations_EnumMap_Command );
        mxExport->AddAttribute( XML_NAMESPACE_ANIMATION, XML_COMMAND, sTmp.makeStringAndClear() );

    // todo virtual css::uno::Any SAL_CALL getParameter() throw (css::uno::RuntimeException) = 0;

        SvXMLElementExport aElement( *mxExport, XML_NAMESPACE_ANIMATION, XML_COMMAND, true, true );

    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

Reference< XInterface > AnimationsExporterImpl::getParagraphTarget( const ParagraphTarget& pTarget )
{
    try
    {
        Reference< XEnumerationAccess > xParaEnumAccess( pTarget.Shape, UNO_QUERY_THROW );

        Reference< XEnumeration > xEnumeration( xParaEnumAccess->createEnumeration(), css::uno::UNO_SET_THROW );
        sal_Int32 nParagraph = pTarget.Paragraph;

        while( xEnumeration->hasMoreElements() )
        {
            Reference< XInterface > xRef( xEnumeration->nextElement(), UNO_QUERY );
            if( nParagraph-- == 0 )
                return xRef;
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }

    Reference< XInterface > xRef;
    return xRef;
}

void AnimationsExporterImpl::convertPath( OUStringBuffer& sTmp, const Any& rPath )
{
    OUString aStr;
    rPath >>= aStr;

    sTmp = aStr;
}

bool AnimationsExporterImpl::convertValue( XMLTokenEnum eAttributeName, OUStringBuffer& sTmp, const Any& rValue ) const
{
    if( !rValue.hasValue() )
        return false;

    bool bSuccess = true;
    if( auto pValuePair = o3tl::tryAccess<ValuePair>(rValue) )
    {
        OUStringBuffer sTmp2;
        bSuccess &= convertValue( eAttributeName, sTmp, pValuePair->First );
        sTmp.append( ',' );
        bSuccess &= convertValue( eAttributeName, sTmp2, pValuePair->Second );
        sTmp.append( sTmp2 );
    }
    else if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();

        OUStringBuffer sTmp2;

        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
        {
            if( !sTmp.isEmpty() )
                sTmp.append( ';' );
            bSuccess &= convertValue( eAttributeName, sTmp2, *pAny );
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else
    {
        sal_Int32 nType;

        switch( eAttributeName )
        {
        case XML_X:
        case XML_Y:
        case XML_WIDTH:
        case XML_HEIGHT:
        case XML_ANIMATETRANSFORM:
        case XML_ANIMATEMOTION:
        case XML_ANIMATEPHYSICS:
        {
            if( auto aString = o3tl::tryAccess<OUString>(rValue) )
            {
                sTmp.append( *aString );
            }
            else if( auto x = o3tl::tryAccess<double>(rValue) )
            {
                sTmp.append( *x );
            }
            else
            {
                OSL_FAIL( "xmloff::AnimationsExporterImpl::convertValue(), invalid value type!" );
                bSuccess = false;
            }
            return bSuccess;
        }

        case XML_SKEWX:
        case XML_ROTATE:            nType = XML_TYPE_DOUBLE;                    break;
        case XML_TEXT_ROTATION_ANGLE: nType = XML_TYPE_NUMBER16;                break;
        case XML_FILL_COLOR:
        case XML_STROKE_COLOR:
        case XML_DIM:
        case XML_COLOR:             nType = XML_TYPE_COLOR;                     break;
        case XML_FILL:              nType = XML_SD_TYPE_FILLSTYLE;              break;
        case XML_STROKE:            nType = XML_SD_TYPE_STROKE;                 break;
        case XML_FONT_WEIGHT:       nType = XML_TYPE_TEXT_WEIGHT;               break;
        case XML_FONT_STYLE:        nType = XML_TYPE_TEXT_POSTURE;              break;
        case XML_TEXT_UNDERLINE:    nType = XML_TYPE_TEXT_UNDERLINE_STYLE;      break;
        case XML_FONT_SIZE:         nType = XML_TYPE_DOUBLE_PERCENT;            break;
        case XML_VISIBILITY:        nType = XML_SD_TYPE_PRESPAGE_VISIBILITY;    break;
        case XML_OPACITY:
        case XML_TRANSITIONFILTER:  nType = XML_TYPE_DOUBLE;                    break;
        default:
            OSL_FAIL( "xmloff::AnimationsExporterImpl::convertValue(), invalid AttributeName!" );
            nType = XML_TYPE_STRING;
        }

        //const XMLPropertyHandler* pHandler = static_cast<SdXMLExport*>(&mrExport)->GetSdPropHdlFactory()->GetPropertyHandler( nType );
        const XMLPropertyHandler* pHandler = mxSdPropHdlFactory->GetPropertyHandler( nType );
        if (!pHandler)
            bSuccess = false;
        else
        {
            OUString aString;
            bSuccess = pHandler->exportXML( aString, rValue, mxExport->GetMM100UnitConverter() );
            sTmp.append( aString );
        }
    }

    return bSuccess;
}

void AnimationsExporterImpl::convertTiming( OUStringBuffer& sTmp, const Any& rValue ) const
{
    if( !rValue.hasValue() )
        return;

    if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();

        OUStringBuffer sTmp2;

        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
        {
            if( !sTmp.isEmpty() )
                sTmp.append( ';' );
            convertTiming( sTmp2, *pAny );
            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else if( auto x = o3tl::tryAccess<double>(rValue) )
    {
        sTmp.append( *x );
        sTmp.append( 's');
    }
    else if( auto pTiming = o3tl::tryAccess<Timing>(rValue) )
    {
        sTmp.append( GetXMLToken( (*pTiming == Timing_MEDIA) ? XML_MEDIA : XML_INDEFINITE ) );
    }
    else if( auto pEvent = o3tl::tryAccess<Event>(rValue) )
    {
        OUStringBuffer sTmp2;

        if( pEvent->Trigger != EventTrigger::NONE )
        {
            if( pEvent->Source.hasValue() )
            {
                convertTarget( sTmp, pEvent->Source );
                sTmp.append( '.' );
            }

            SvXMLUnitConverter::convertEnum( sTmp2, pEvent->Trigger, aAnimations_EnumMap_EventTrigger );

            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }

        if( pEvent->Offset.hasValue() )
        {
            convertTiming( sTmp2, pEvent->Offset );

            if( !sTmp.isEmpty() )
                sTmp.append( '+' );

            sTmp.append( sTmp2 );
            sTmp2.setLength(0);
        }
    }
    else
    {
        OSL_FAIL( "xmloff::AnimationsExporterImpl::convertTiming(), invalid value type!" );
    }
}

void AnimationsExporterImpl::convertTarget( OUStringBuffer& sTmp, const Any& rTarget ) const
{
    if( !rTarget.hasValue() )
        return;

    Reference< XInterface > xRef;

    if( !(rTarget >>= xRef) )
    {
        if( auto pt = o3tl::tryAccess<ParagraphTarget>(rTarget) )
        {
            xRef = getParagraphTarget( *pt );
        }
    }

    SAL_WARN_IF( !xRef.is(), "xmloff", "xmloff::AnimationsExporterImpl::convertTarget(), invalid target type!" );
    if( xRef.is() )
    {
        const OUString& rIdentifier = mxExport->getInterfaceToIdentifierMapper().getIdentifier(xRef);
        if( !rIdentifier.isEmpty() )
            sTmp.append( rIdentifier );
    }
}

void AnimationsExporterImpl::prepareValue( const Any& rValue )
{
    if( !rValue.hasValue() )
        return;

    if( auto pValuePair = o3tl::tryAccess<ValuePair>(rValue) )
    {
        prepareValue( pValuePair->First );
        prepareValue( pValuePair->Second );
    }
    else if( auto pSequence = o3tl::tryAccess<Sequence<Any>>(rValue) )
    {
        const sal_Int32 nLength = pSequence->getLength();
        sal_Int32 nElement;
        const Any* pAny = pSequence->getConstArray();

        for( nElement = 0; nElement < nLength; nElement++, pAny++ )
            prepareValue( *pAny );
    }
    else if( rValue.getValueTypeClass() == css::uno::TypeClass_INTERFACE )
    {
        Reference< XInterface> xRef( rValue, UNO_QUERY );
        if( xRef.is() )
            mxExport->getInterfaceToIdentifierMapper().registerReference( xRef );
    }
    else if( auto pt = o3tl::tryAccess<ParagraphTarget>(rValue) )
    {
        Reference< XInterface> xRef( getParagraphTarget( *pt ) );
        if( xRef.is() )
            mxExport->getInterfaceToIdentifierMapper().registerReference( xRef );
    }
    else if( auto pEvent = o3tl::tryAccess<Event>(rValue) )
    {
        prepareValue( pEvent->Source );
    }
}

AnimationsExporter::AnimationsExporter( SvXMLExport& rExport, const Reference< XPropertySet >& xPageProps )
    : mpImpl( new AnimationsExporterImpl( rExport, xPageProps ) )
{
}

AnimationsExporter::~AnimationsExporter()
{
}

void AnimationsExporter::prepare( const Reference< XAnimationNode >& xRootNode )
{
    try
    {
        if( xRootNode.is() )
        {
            mpImpl->prepareTransitionNode();
            mpImpl->prepareNode( xRootNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

void AnimationsExporter::exportAnimations( const Reference< XAnimationNode >& xRootNode )
{
    try
    {
        if( xRootNode.is() )
        {
            bool bHasEffects = mpImpl->mbHasTransition;

            if( !bHasEffects )
            {
                // first check if there are no animations
                Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW );
                Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );
                if( xEnumeration->hasMoreElements() )
                {
                    // first child node may be an empty main sequence, check this
                    Reference< XAnimationNode > xMainNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
                    Reference< XEnumerationAccess > xMainEnumerationAccess( xMainNode, UNO_QUERY_THROW );
                    Reference< XEnumeration > xMainEnumeration( xMainEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW );

                    // only export if the main sequence is not empty or if there are additional
                    // trigger sequences
                    bHasEffects = xMainEnumeration->hasMoreElements() || xEnumeration->hasMoreElements();
                }
            }

            if( bHasEffects )
                mpImpl->exportNode( xRootNode );
        }
    }
    catch (const RuntimeException&)
    {
        TOOLS_WARN_EXCEPTION("xmloff.draw", "");
    }
}

}

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