/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#include "oox/ppt/timenodelistcontext.hxx"

#include "comphelper/anytostring.hxx"
#include "cppuhelper/exc_hlp.hxx"
#include <osl/diagnose.h>
#include <rtl/math.hxx>

#include <com/sun/star/animations/XTimeContainer.hpp>
#include <com/sun/star/animations/XAnimationNode.hpp>
#include <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XAnimateSet.hpp>
#include <com/sun/star/animations/XAnimateTransform.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/animations/AnimationCalcMode.hpp>
#include <com/sun/star/animations/AnimationColorSpace.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/XCommand.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <com/sun/star/beans/NamedValue.hpp>

#include "oox/helper/attributelist.hxx"
#include "oox/core/xmlfilterbase.hxx"
#include "oox/drawingml/drawingmltypes.hxx"
#include "oox/drawingml/colorchoicecontext.hxx"
#include "oox/ppt/slidetransition.hxx"

#include "animvariantcontext.hxx"
#include "commonbehaviorcontext.hxx"
#include "conditioncontext.hxx"
#include "commontimenodecontext.hxx"
#include "timeanimvaluecontext.hxx"
#include "animationtypes.hxx"

using namespace ::oox::core;
using namespace ::oox::drawingml;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::xml::sax;
using namespace ::com::sun::star::awt;
using ::com::sun::star::beans::NamedValue;

using ::rtl::OUString;

namespace oox { namespace ppt {

	struct AnimColor
	{
		AnimColor(sal_Int16 cs, sal_Int32 o, sal_Int32 t, sal_Int32 th )
			: colorSpace( cs ), one( o ), two( t ), three( th )
			{
			}

		Any get()
			{
				sal_Int32 nColor;
				Sequence< double > aHSL( 3 );
				Any aColor;

				switch( colorSpace )
				{
				case AnimationColorSpace::HSL:
					aHSL[ 0 ] = double(one) / 100000;
					aHSL[ 1 ] = double(two) / 100000;
					aHSL[ 2 ] = double(three) / 100000;
					aColor = Any(aHSL);
					break;
				case AnimationColorSpace::RGB:
					nColor = ( ( ( one * 128 ) / 1000 ) & 0xff ) << 16
						| ( ( ( two * 128 ) / 1000 ) & 0xff ) << 8
						| ( ( ( three * 128 ) / 1000 )  & 0xff );
					aColor = Any(nColor);
					break;
				default:
					nColor = 0;
					aColor = Any( nColor );
					break;
				}
				return  aColor;
			}

		sal_Int16 colorSpace;
		sal_Int32 one;
		sal_Int32 two;
		sal_Int32 three;
	};


	/** CT_TLMediaNodeAudio
			CT_TLMediaNodeVideo */
	class MediaNodeContext
		: public TimeNodeContext
	{
	public:
        MediaNodeContext( ContextHandler& rParent, sal_Int32  aElement,
                            const Reference< XFastAttributeList >& xAttribs,
                            const TimeNodePtr & pNode )
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
				, mbIsNarration( false )
				, mbFullScrn( false )
			{
				AttributeList attribs( xAttribs );

				switch( aElement )
				{
				case PPT_TOKEN( audio ):
					mbIsNarration = attribs.getBool( XML_isNarration, false );
					break;
				case PPT_TOKEN( video ):
					mbFullScrn = attribs.getBool( XML_fullScrn, false );
					break;
				default:
					break;
				}
			}

		virtual void SAL_CALL endFastElement( sal_Int32 aElement )
			throw ( SAXException, RuntimeException)
			{
				if( aElement == PPT_TOKEN( audio ) )
				{
					// TODO deal with mbIsNarration
				}
				else if( aElement == PPT_TOKEN( video ) )
				{
					// TODO deal with mbFullScrn
				}
			}

		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}

	private:
		bool mbIsNarration;
		bool mbFullScrn;
	};


	/** CT_TLSetBehavior
	 */
	class SetTimeNodeContext
		: public TimeNodeContext
	{
	public:
        SetTimeNodeContext( ContextHandler& rParent, sal_Int32  aElement,
                            const Reference< XFastAttributeList >& xAttribs,
                            const TimeNodePtr & pNode )
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{

			}

		~SetTimeNodeContext() throw ()
			{
				if( maTo.hasValue() )
				{
					// TODO
					// HACK !!! discard and refactor
					OUString aString;
					if( maTo >>= aString )
					{
						OSL_TRACE( "Magic conversion %s", OUSTRING_TO_CSTR( aString ) );
						maTo = makeAny( aString.equalsAscii( "visible" ) ? sal_True : sal_False );
						if( !maTo.has<sal_Bool>() )
							OSL_TRACE( "conversion failed" );
					}
					mpNode->setTo( maTo );
				}

			}

		virtual void SAL_CALL endFastElement( sal_Int32 /*aElement*/ )
			throw ( SAXException, RuntimeException)
			{
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( to ):
					// CT_TLAnimVariant
                    xRet.set( new AnimVariantContext( *this, aElementToken, maTo ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		Any  maTo;
	};

	/** CT_TLCommandBehavior
	 */
	class CmdTimeNodeContext
		: public TimeNodeContext
	{
	public:
        CmdTimeNodeContext( ContextHandler& rParent, sal_Int32  aElement,
                            const Reference< XFastAttributeList >& xAttribs,
                            const TimeNodePtr & pNode )
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
				, maType(0)
			{
				switch ( aElement )
				{
				case PPT_TOKEN( cmd ):
					msCommand = xAttribs->getOptionalValue( XML_cmd );
					maType = xAttribs->getOptionalValueToken( XML_type, 0 );
					break;
				default:
					break;
				}
			}

		~CmdTimeNodeContext() throw ()
			{
			}

		virtual void SAL_CALL endFastElement( sal_Int32 aElement )
			throw ( SAXException, RuntimeException)
			{
				if( aElement == PPT_TOKEN( cmd ) )
				{
					try {
						// see sd/source/filter/ppt/pptinanimations.cxx
						// in AnimationImporter::importCommandContainer()
						// REFACTOR?
						// a good chunk of this code has been copied verbatim *sigh*
						sal_Int16 nCommand = EffectCommands::CUSTOM;
						NamedValue aParamValue;

						switch( maType )
						{
						case XML_verb:
							aParamValue.Name = OUString(RTL_CONSTASCII_USTRINGPARAM("Verb"));
							// TODO make sure msCommand has what we want
							aParamValue.Value <<= msCommand.toInt32();
							nCommand = EffectCommands::VERB;
							break;
						case XML_evt:
						case XML_call:
							if( msCommand.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "onstopaudio" ) ) )
							{
								nCommand = EffectCommands::STOPAUDIO;
							}
							else if( msCommand.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("play") ) )
							{
								nCommand = EffectCommands::PLAY;
							}
							else if( msCommand.compareToAscii( RTL_CONSTASCII_STRINGPARAM("playFrom") ) == 0 )
							{
								const OUString aMediaTime( msCommand.copy( 9, msCommand.getLength() - 10 ) );
								rtl_math_ConversionStatus eStatus;
								double fMediaTime = ::rtl::math::stringToDouble( aMediaTime, (sal_Unicode)('.'), (sal_Unicode)(','), &eStatus, NULL );
								if( eStatus == rtl_math_ConversionStatus_Ok )
								{
									aParamValue.Name = CREATE_OUSTRING("MediaTime");
									aParamValue.Value <<= fMediaTime;
								}
								nCommand = EffectCommands::PLAY;
							}
							else if( msCommand.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("togglePause") ) )
							{
								nCommand = EffectCommands::TOGGLEPAUSE;
							}
							else if( msCommand.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("stop") ) )
							{
								nCommand = EffectCommands::STOP;
							}
							break;
						}
						mpNode->getNodeProperties()[ NP_COMMAND ] = makeAny((sal_Int16)nCommand);
						if( nCommand == EffectCommands::CUSTOM )
						{
							OSL_TRACE("OOX: CmdTimeNodeContext::endFastElement(), unknown command!");
							aParamValue.Name = CREATE_OUSTRING("UserDefined");
							aParamValue.Value <<= msCommand;
						}
						if( aParamValue.Value.hasValue() )
						{
							Sequence< NamedValue > aParamSeq( &aParamValue, 1 );
							mpNode->getNodeProperties()[ NP_PARAMETER ] = makeAny( aParamSeq );
						}
					}
					catch( RuntimeException& )
					{
						OSL_TRACE( "OOX: Exception in CmdTimeNodeContext::endFastElement()" );
					}
				}
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}

	private:
		OUString msCommand;
		sal_Int32 maType;
	};


	/** CT_TLTimeNodeSequence
	 */
	class SequenceTimeNodeContext
		: public TimeNodeContext
	{
	public:
        SequenceTimeNodeContext( ContextHandler& rParent, sal_Int32  aElement,
                                 const Reference< XFastAttributeList >& xAttribs,
                                 const TimeNodePtr & pNode )
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
				, mnNextAc(0)
				, mnPrevAc(0)
			{
				AttributeList attribs(xAttribs);
				mbConcurrent = attribs.getBool( XML_concurrent, false );
				// ST_TLNextActionType { none, seek }
				mnNextAc = xAttribs->getOptionalValueToken( XML_nextAc, 0 );
				// ST_TLPreviousActionType { none, skipTimed }
				mnPrevAc = xAttribs->getOptionalValueToken( XML_prevAc, 0 );
			}

		~SequenceTimeNodeContext() throw()
			{
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cTn ):
                    xRet.set( new CommonTimeNodeContext( *this, aElementToken, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( nextCondLst ):
                    xRet.set( new CondListContext( *this, aElementToken, xAttribs, mpNode,
												   mpNode->getNextCondition() ) );
					break;
				case PPT_TOKEN( prevCondLst ):
                    xRet.set( new CondListContext( *this, aElementToken, xAttribs, mpNode,
												   mpNode->getPrevCondition() ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		bool mbConcurrent;
		sal_Int32 mnNextAc, mnPrevAc;
	};


	/** CT_TLTimeNodeParallel
	 *  CT_TLTimeNodeExclusive
	 */
	class ParallelExclTimeNodeContext
		: public TimeNodeContext
	{
	public:
        ParallelExclTimeNodeContext( ContextHandler& rParent, sal_Int32  aElement,
                                     const Reference< XFastAttributeList >& xAttribs,
                                     const TimeNodePtr & pNode )
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{
			}

		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cTn ):
                    xRet.set( new CommonTimeNodeContext( *this, aElementToken, xAttribs, mpNode ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}

	protected:

	};


	/** CT_TLAnimateColorBehavior */
	class AnimColorContext
		: public TimeNodeContext
	{
	public:
        AnimColorContext( ContextHandler& rParent, sal_Int32  aElement,
                            const Reference< XFastAttributeList >& xAttribs,
                            const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
				// ST_TLAnimateColorSpace ( XML_rgb, XML_hsl }
			, mnColorSpace( xAttribs->getOptionalValueToken( XML_clrSpc, 0 ) )
				// ST_TLAnimateColorDirection { XML_cw, XML_ccw }
			, mnDir( xAttribs->getOptionalValueToken( XML_dir, 0 ) )
			, mbHasByColor( false )
			, m_byColor( AnimationColorSpace::RGB, 0, 0, 0)
			{
			}
		~AnimColorContext() throw()
			{
			}

		virtual void SAL_CALL endFastElement( sal_Int32 aElement ) throw ( SAXException, RuntimeException)
			{
				//xParentNode
				if( aElement == mnElement )
				{
					NodePropertyMap & pProps(mpNode->getNodeProperties());
					pProps[ NP_DIRECTION ] = makeAny( mnDir == XML_cw );
					pProps[ NP_COLORINTERPOLATION ] = makeAny( mnColorSpace == XML_hsl ? AnimationColorSpace::HSL : AnimationColorSpace::RGB );
                    const GraphicHelper& rGraphicHelper = getFilter().getGraphicHelper();
					if( maToClr.isUsed() )
                        mpNode->setTo( Any( maToClr.getColor( rGraphicHelper ) ) );
					if( maFromClr.isUsed() )
                        mpNode->setFrom( Any( maFromClr.getColor( rGraphicHelper ) ) );
					if( mbHasByColor )
						mpNode->setBy( m_byColor.get() );
				}
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken, const Reference< XFastAttributeList >& xAttribs ) throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( hsl ):
					// CT_TLByHslColorTransform
				{
					if( mbHasByColor )
					{
						m_byColor.colorSpace = AnimationColorSpace::HSL;
						m_byColor.one = xAttribs->getOptionalValue( XML_h ).toInt32( );
						m_byColor.two = xAttribs->getOptionalValue( XML_s ).toInt32( );
						m_byColor.three = xAttribs->getOptionalValue( XML_l ).toInt32( );
					}
					xRet.set(this);
					break;
				}
				case PPT_TOKEN( rgb ):
				{
					if( mbHasByColor )
					{
						// CT_TLByRgbColorTransform
						m_byColor.colorSpace = AnimationColorSpace::RGB;
						m_byColor.one = xAttribs->getOptionalValue( XML_r ).toInt32();
						m_byColor.two = xAttribs->getOptionalValue( XML_g ).toInt32();
						m_byColor.three = xAttribs->getOptionalValue( XML_b ).toInt32();
					}
					xRet.set(this);
					break;
				}
				case PPT_TOKEN( by ):
					// CT_TLByAnimateColorTransform
					mbHasByColor = true;
					xRet.set(this);
					break;
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( to ):
					// CT_Color
                    xRet.set( new ColorContext( *this, maToClr ) );
					break;
				case PPT_TOKEN( from ):
					// CT_Color
                    xRet.set( new ColorContext( *this, maFromClr ) );
					break;

				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}


	private:
		sal_Int32 mnColorSpace;
		sal_Int32 mnDir;
		bool mbHasByColor;
		AnimColor m_byColor;
		oox::drawingml::Color maToClr;
		oox::drawingml::Color maFromClr;
	};


	/** CT_TLAnimateBehavior */
	class AnimContext
		: public TimeNodeContext
	{
	public:
        AnimContext( ContextHandler& rParent, sal_Int32  aElement,
                     const Reference< XFastAttributeList >& xAttribs,
                      const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{
				NodePropertyMap & aProps( pNode->getNodeProperties() );
				sal_Int32 nCalcMode = xAttribs->getOptionalValueToken( XML_calcmode, 0 );
				if(nCalcMode)
				{
					sal_Int16 nEnum = 0;
					switch(nCalcMode)
					{
					case XML_discrete:
						nEnum = AnimationCalcMode::DISCRETE;
						break;
					case XML_lin:
						nEnum = AnimationCalcMode::LINEAR;
						break;
					case XML_fmla:
					default:
						// TODO what value is good ?
						nEnum = AnimationCalcMode::DISCRETE;
						break;
					}
					aProps[ NP_CALCMODE ] = makeAny(nEnum);
				}
				OUString aStr;
				aStr = xAttribs->getOptionalValue( XML_from );
				if( aStr.getLength() )
				{
					pNode->setFrom( makeAny( aStr ) );
				}
				aStr = xAttribs->getOptionalValue( XML_by );
				if( aStr.getLength() )
				{
					pNode->setBy( makeAny( aStr ) );
				}
				aStr = xAttribs->getOptionalValue( XML_to );
				if( aStr.getLength() )
				{
					pNode->setTo( makeAny( aStr ) );
				}
				mnValueType = xAttribs->getOptionalValueToken( XML_valueType, 0 );
			}


		~AnimContext() throw ()
			{
				::std::list< TimeAnimationValue >::iterator iter, end;
				int nKeyTimes = maTavList.size();
				if( nKeyTimes > 0)
				{
					int i;
					Sequence< double > aKeyTimes( nKeyTimes );
					Sequence< Any > aValues( nKeyTimes );

					NodePropertyMap & aProps( mpNode->getNodeProperties() );
					end = maTavList.end();
					for(iter = maTavList.begin(), i=0; iter != end; iter++,i++)
					{
						// TODO what to do if it is Timing_INFINITE ?
						Any aTime = GetTimeAnimateValueTime( iter->msTime );
						aTime >>= aKeyTimes[i];
						aValues[i] = iter->maValue;

						OUString aTest;
						iter->maValue >>= aTest;
						if( aTest.getLength() != 0 )
						{
							aValues[i] = iter->maValue;
						}
						else
						{
							aProps[ NP_FORMULA ] <<= iter->msFormula;
						}
					}
					aProps[ NP_VALUES ] <<= aValues;
					aProps[ NP_KEYTIMES ] <<= aKeyTimes;
				}
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken, const Reference< XFastAttributeList >& xAttribs ) throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( tavLst ):
                    xRet.set( new TimeAnimValueListContext ( *this, xAttribs, maTavList ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		sal_Int32              mnValueType;
		TimeAnimationValueList maTavList;
	};


	/** CT_TLAnimateScaleBehavior */
	class AnimScaleContext
		: public TimeNodeContext
	{
	public:
        AnimScaleContext( ContextHandler& rParent, sal_Int32  aElement,
                            const Reference< XFastAttributeList >& xAttribs,
                            const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
				, mbZoomContents( false )
			{
				AttributeList attribs( xAttribs );
				// TODO what to do with mbZoomContents
				mbZoomContents = attribs.getBool( XML_zoomContents, false );
				pNode->getNodeProperties()[ NP_TRANSFORMTYPE ]
					= makeAny((sal_Int16)AnimationTransformType::SCALE);
			}

		~AnimScaleContext( ) throw( )
			{
			}

		virtual void SAL_CALL endFastElement( sal_Int32 aElement ) throw ( SAXException, RuntimeException)
			{
				if( aElement == mnElement )
				{
					if( maTo.hasValue() )
					{
						mpNode->setTo( maTo );
					}
					if( maBy.hasValue() )
					{
						mpNode->setBy( maBy );
					}
					if( maFrom.hasValue() )
					{
						mpNode->setFrom( maFrom );
					}
				}
			}

		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( to ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					maTo <<= p.X;
					maTo <<= p.Y;
					break;
				}
				case PPT_TOKEN( from ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					maFrom <<= p.X;
					maFrom <<= p.Y;
					break;
				}
				case PPT_TOKEN( by ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					maBy <<= p.X;
					maBy <<= p.Y;
					break;
				}
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		Any maBy;
		Any maFrom;
		Any maTo;
		bool mbZoomContents;
	};


	/** CT_TLAnimateRotationBehavior */
	class AnimRotContext
		: public TimeNodeContext
	{
	public:
        AnimRotContext( ContextHandler& rParent, sal_Int32  aElement,
                        const Reference< XFastAttributeList >& xAttribs,
                         const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{
				AttributeList attribs( xAttribs );

				pNode->getNodeProperties()[ NP_TRANSFORMTYPE ]
					= makeAny((sal_Int16)AnimationTransformType::ROTATE);
				// TODO make sure the units are OK
				if(attribs.hasAttribute( XML_by ) )
				{
					sal_Int32 nBy = attribs.getInteger( XML_by, 0 );
					pNode->setBy( makeAny( (double)nBy ) );
				}
				if(attribs.hasAttribute( XML_from ) )
				{
					sal_Int32 nFrom = attribs.getInteger( XML_from, 0 );
					pNode->setFrom( makeAny( nFrom ) );
				}
				if(attribs.hasAttribute( XML_to ) )
				{
					sal_Int32 nTo = attribs.getInteger( XML_to, 0 );
					pNode->setTo( makeAny( nTo ) );
				}
			}

		~AnimRotContext( ) throw( )
			{
			}

		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken, const Reference< XFastAttributeList >& xAttribs ) throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	};



	/** CT_TLAnimateMotionBehavior */
	class AnimMotionContext
		: public TimeNodeContext
	{
	public:
        AnimMotionContext( ContextHandler& rParent, sal_Int32  aElement,
                         const Reference< XFastAttributeList >& xAttribs,
                          const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{
				pNode->getNodeProperties()[ NP_TRANSFORMTYPE ]
					= makeAny((sal_Int16)AnimationTransformType::TRANSLATE);

				AttributeList attribs( xAttribs );
				// ST_TLAnimateMotionBehaviorOrigin { parent, layour }
				sal_Int32 nOrigin = xAttribs->getOptionalValueToken( XML_origin, 0 );
				if( nOrigin != 0 )
				{
					switch(nOrigin)
					{
					case XML_layout:
					case XML_parent:
						break;
					}
					// TODO
				}

				OUString aStr = xAttribs->getOptionalValue( XML_path );
				aStr = aStr.replace( 'E', ' ' );
				aStr = aStr.trim();
				pNode->getNodeProperties()[ NP_PATH ] = makeAny(aStr);

				// ST_TLAnimateMotionPathEditMode{ fixed, relative }
				mnPathEditMode = xAttribs->getOptionalValueToken( XML_pathEditMode, 0 );
				msPtsTypes = xAttribs->getOptionalValue( XML_ptsTypes );
				mnAngle = attribs.getInteger( XML_rAng, 0 );
				// TODO make sure the units are right. Likely not.
			}

		~AnimMotionContext( ) throw()
			{
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken,
																																							const Reference< XFastAttributeList >& xAttribs )
			throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( to ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					Any rAny;
					rAny <<= p.X;
					rAny <<= p.Y;
					mpNode->setTo( rAny );
					break;
				}
				case PPT_TOKEN( from ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					Any rAny;
					rAny <<= p.X;
					rAny <<= p.Y;
					mpNode->setFrom( rAny );
					break;
				}
				case PPT_TOKEN( by ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					Any rAny;
					rAny <<= p.X;
					rAny <<= p.Y;
					mpNode->setBy( rAny );
					break;
				}
				case PPT_TOKEN( rCtr ):
				{
					// CT_TLPoint
					Point p = GetPointPercent( xAttribs );
					// TODO push
					break;
				}
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		OUString msPtsTypes;
		sal_Int32 mnPathEditMode;
		sal_Int32 mnAngle;
	};


	/** CT_TLAnimateEffectBehavior */
	class AnimEffectContext
		: public TimeNodeContext
	{
	public:
        AnimEffectContext( ContextHandler& rParent, sal_Int32  aElement,
                             const Reference< XFastAttributeList >& xAttribs,
                             const TimeNodePtr & pNode ) throw()
            : TimeNodeContext( rParent, aElement, xAttribs, pNode )
			{
				sal_Int32 nDir = xAttribs->getOptionalValueToken( XML_transition, 0 );
				OUString sFilter = xAttribs->getOptionalValue( XML_filter );
				// TODO
//				OUString sPrList = xAttribs->getOptionalValue( XML_prLst );

				if( sFilter.getLength() )
				{
					SlideTransition aFilter( sFilter );
					aFilter.setMode( nDir == XML_out ? false : true );
					pNode->setTransitionFilter( aFilter );
				}
			}


		~AnimEffectContext( ) throw()
			{
			}


		virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( ::sal_Int32 aElementToken, const Reference< XFastAttributeList >& xAttribs ) throw ( SAXException, RuntimeException )
			{
				Reference< XFastContextHandler > xRet;

				switch ( aElementToken )
				{
				case PPT_TOKEN( cBhvr ):
                    xRet.set( new CommonBehaviorContext ( *this, xAttribs, mpNode ) );
					break;
				case PPT_TOKEN( progress ):
                    xRet.set( new AnimVariantContext( *this, aElementToken, maProgress ) );
					// TODO handle it.
					break;
				default:
					break;
				}

				if( !xRet.is() )
					xRet.set( this );

				return xRet;
			}
	private:
		Any maProgress;
		OUString msFilter;
		OUString msPrList;
	};



    TimeNodeContext * TimeNodeContext::makeContext(
            ContextHandler& rParent, sal_Int32  aElement,
            const Reference< XFastAttributeList >& xAttribs,
            const TimeNodePtr & pNode )
	{
		TimeNodeContext *pCtx = NULL;
		switch( aElement )
		{
		case PPT_TOKEN( animClr ):
            pCtx = new AnimColorContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( par ):
            pCtx = new ParallelExclTimeNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( seq ):
            pCtx = new SequenceTimeNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( excl ):
            pCtx = new ParallelExclTimeNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( anim ):
            pCtx = new AnimContext ( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( animEffect ):
            pCtx = new AnimEffectContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( animMotion ):
            pCtx = new AnimMotionContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( animRot ):
            pCtx = new AnimRotContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( animScale ):
            pCtx = new AnimScaleContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( cmd ):
            pCtx = new CmdTimeNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( set ):
            pCtx = new SetTimeNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		case PPT_TOKEN( audio ):
		case PPT_TOKEN( video ):
            pCtx = new MediaNodeContext( rParent, aElement, xAttribs, pNode );
			break;
		default:
			break;
		}
		return pCtx;
	}


    TimeNodeContext::TimeNodeContext( ContextHandler& rParent, sal_Int32 aElement,
            const Reference< XFastAttributeList >& /*xAttribs*/,
            const TimeNodePtr & pNode ) throw()
        : ContextHandler( rParent )
		, mnElement( aElement )
		, mpNode( pNode )
	{
	}


	TimeNodeContext::~TimeNodeContext( ) throw()
	{

	}


    TimeNodeListContext::TimeNodeListContext( ContextHandler& rParent, TimeNodePtrList & aList )
		throw()
        : ContextHandler( rParent )
			, maList( aList )
	{
	}


	TimeNodeListContext::~TimeNodeListContext( ) throw()
	{
	}


	Reference< XFastContextHandler > SAL_CALL TimeNodeListContext::createFastChildContext( ::sal_Int32 aElementToken, const Reference< XFastAttributeList >& xAttribs ) throw (SAXException, RuntimeException)
	{
		Reference< XFastContextHandler > xRet;

		sal_Int16 nNodeType;

		switch( aElementToken )
		{
		case PPT_TOKEN( par ):
			nNodeType = AnimationNodeType::PAR;
			break;
		case PPT_TOKEN( seq ):
			nNodeType = AnimationNodeType::SEQ;
			break;
		case PPT_TOKEN( excl ):
			// TODO pick the right type. We choose parallel for now as
			// there does not seem to be an "Exclusive"
			nNodeType = AnimationNodeType::PAR;
			break;
		case PPT_TOKEN( anim ):
			nNodeType = AnimationNodeType::ANIMATE;
			break;
		case PPT_TOKEN( animClr ):
			nNodeType = AnimationNodeType::ANIMATECOLOR;
			break;
		case PPT_TOKEN( animEffect ):
			nNodeType = AnimationNodeType::TRANSITIONFILTER;
			break;
		case PPT_TOKEN( animMotion ):
			nNodeType = AnimationNodeType::ANIMATEMOTION;
			break;
		case PPT_TOKEN( animRot ):
		case PPT_TOKEN( animScale ):
			nNodeType = AnimationNodeType::ANIMATETRANSFORM;
			break;
		case PPT_TOKEN( cmd ):
			nNodeType = AnimationNodeType::COMMAND;
			break;
		case PPT_TOKEN( set ):
			nNodeType = AnimationNodeType::SET;
			break;
		case PPT_TOKEN( audio ):
			nNodeType = AnimationNodeType::AUDIO;
			break;
		case PPT_TOKEN( video ):
			nNodeType = AnimationNodeType::AUDIO;
			OSL_TRACE( "OOX: video requested, gave Audio instead" );
			break;

		default:
			nNodeType = AnimationNodeType::CUSTOM;
			OSL_TRACE( "OOX: uhandled token %x", aElementToken );
			break;
		}

		TimeNodePtr pNode(new TimeNode(nNodeType));
		maList.push_back( pNode );
        ContextHandler * pContext = TimeNodeContext::makeContext( *this, aElementToken, xAttribs, pNode );
        xRet.set( pContext ? pContext : this );

		return xRet;
	}


} }