/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sd.hxx"
#include <com/sun/star/animations/XAnimationNode.hpp>
#include <com/sun/star/animations/Event.hpp>
#ifndef _COM_SUN_STAR_ANIMATIONS_XAnimateColor_HPP_
#include <com/sun/star/animations/XAnimateColor.hpp>
#endif
#ifndef _COM_SUN_STAR_ANIMATIONS_XAnimateSet_HPP_
#include <com/sun/star/animations/XAnimateSet.hpp>
#endif
#include <com/sun/star/animations/XCommand.hpp>
#ifndef _COM_SUN_STAR_ANIMATIONS_XAnimateMotion_HPP_
#include <com/sun/star/animations/XAnimateMotion.hpp>
#endif
#ifndef _COM_SUN_STAR_ANIMATIONS_XAnimateTransform_HPP_
#include <com/sun/star/animations/XAnimateTransform.hpp>
#endif
#ifndef _COM_SUN_STAR_ANIMATIONS_XTransitionFilter_HPP_
#include <com/sun/star/animations/XTransitionFilter.hpp>
#endif
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/animations/XAudio.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/ValuePair.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/util/XCloneable.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/beans/NamedValue.hpp>

#include <map>

#include "comphelper/anytostring.hxx"
#include "cppuhelper/exc_hlp.hxx"
#include "rtl/ref.hxx"
#include <animations/animationnodehelper.hxx>

// header for class SdrObjListIter
#include <svx/svditer.hxx>

#include "sdpage.hxx"

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::container;

using ::rtl::OUString;
using ::rtl::OString;
using ::com::sun::star::drawing::XShape;
using ::com::sun::star::beans::NamedValue;

namespace sd
{
    class CustomAnimationClonerImpl
    {
	public:
        CustomAnimationClonerImpl();
		Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource = 0, const SdPage* pTarget = 0 );

    private:
		void transformNode( const Reference< XAnimationNode >& xNode );
		Any transformValue( const Any& rValue );

        Reference< XShape > getClonedShape( const Reference< XShape >& xSource ) const;
        Reference< XAnimationNode > getClonedNode( const Reference< XAnimationNode >& xSource ) const;

        mutable ::std::map< Reference< XShape >, Reference< XShape > > maShapeMap;
		std::vector< Reference< XAnimationNode > > maSourceNodeVector;
		std::vector< Reference< XAnimationNode > > maCloneNodeVector;
    };

	CustomAnimationClonerImpl::CustomAnimationClonerImpl()
	{
	}

	Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource, const SdPage* pTarget )
	{
		CustomAnimationClonerImpl aCloner;
		return aCloner.Clone( xSourceNode, pSource, pTarget );
	}

	Reference< XAnimationNode > CustomAnimationClonerImpl::Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSourcePage, const SdPage* pTargetPage )
	{
		try
		{
			// clone animation hierarchie
			Reference< ::com::sun::star::util::XCloneable > xClonable( xSourceNode, UNO_QUERY_THROW );
			Reference< XAnimationNode > xCloneNode( xClonable->createClone(), UNO_QUERY_THROW );

			// create a dictionary to map source to cloned shapes
			if( pSourcePage && pTargetPage )
			{
				SdrObjListIter aSourceIter( *pSourcePage, IM_DEEPWITHGROUPS );
				SdrObjListIter aTargetIter( *pTargetPage, IM_DEEPWITHGROUPS );

				while( aSourceIter.IsMore() && aTargetIter.IsMore() )
				{
					SdrObject* pSource = aSourceIter.Next();
					SdrObject* pTarget = aTargetIter.Next();

					if( pSource && pTarget)
					{
						Reference< XShape > xSource( pSource->getUnoShape(), UNO_QUERY );
						Reference< XShape > xTarget( pTarget->getUnoShape(), UNO_QUERY );
						if( xSource.is() && xTarget.is() )
						{
							maShapeMap[xSource] = xTarget;
						}
					}
				}
			}

			// create a dictionary to map source to cloned nodes
			::anim::create_deep_vector( xSourceNode, maSourceNodeVector );
			::anim::create_deep_vector( xCloneNode, maCloneNodeVector );

			transformNode( xCloneNode );

			return xCloneNode;
		}
		catch( Exception& e )
		{
			(void)e;
			DBG_ERROR(
				(OString("sd::CustomAnimationClonerImpl::Clone(), "
						"exception caught: ") +
				rtl::OUStringToOString(
					comphelper::anyToString( cppu::getCaughtException() ),
					RTL_TEXTENCODING_UTF8 )).getStr() );

			Reference< XAnimationNode > xEmpty;
			return xEmpty;
		}
	}

	void CustomAnimationClonerImpl::transformNode( const Reference< XAnimationNode >& xNode )
	{
		try
		{
			xNode->setBegin( transformValue( xNode->getBegin() ) );
			xNode->setEnd( transformValue( xNode->getEnd() ) );

			sal_Int16 nNodeType( xNode->getType() );
			switch( nNodeType )
			{
			case AnimationNodeType::ITERATE:
			{
				Reference< XIterateContainer > xIter( xNode, UNO_QUERY_THROW );
				xIter->setTarget( transformValue( xIter->getTarget() ) );
			}
			// its intended that here is no break!
			case AnimationNodeType::PAR:
			case AnimationNodeType::SEQ:
			{
				Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
				Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
				while( xEnumeration->hasMoreElements() )
				{
					Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
					transformNode( xChildNode );
				}
			}
			break;

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

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

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

			Sequence< NamedValue > aUserData( xNode->getUserData() );
			if( aUserData.hasElements() )
			{
				NamedValue* pValue = aUserData.getArray();
				const sal_Int32 nLength = aUserData.getLength();
				sal_Int32 nElement;
				for( nElement = 0; nElement < nLength; nElement++, pValue++ )
				{
					pValue->Value = transformValue( pValue->Value );
				}

				xNode->setUserData( aUserData );
			}
		}
		catch( Exception& e )
		{
			(void)e;
			DBG_ERROR(
				(OString("sd::CustomAnimationClonerImpl::transformNode(), "
						"exception caught: ") +
				rtl::OUStringToOString(
					comphelper::anyToString( cppu::getCaughtException() ),
					RTL_TEXTENCODING_UTF8 )).getStr() );
		}
	}

	Any CustomAnimationClonerImpl::transformValue( const Any& rValue )
	{
		if( rValue.hasValue() ) try
		{
			if( rValue.getValueType() == ::getCppuType((const ValuePair*)0) )
			{
				ValuePair aValuePair;
				rValue >>= aValuePair;

				aValuePair.First = transformValue( aValuePair.First );
				aValuePair.Second = transformValue( aValuePair.Second );

				return makeAny( aValuePair );
			}
			else if( rValue.getValueType() == ::getCppuType((Sequence<Any>*)0) )
			{
				Sequence<Any> aSequence;
				rValue >>= aSequence;

				const sal_Int32 nLength = aSequence.getLength();
				sal_Int32 nElement;
				Any* pAny = aSequence.getArray();

				for( nElement = 0; nElement < nLength; nElement++, pAny++ )
					*pAny = transformValue( *pAny );

				return makeAny( aSequence );
			}
			else if( rValue.getValueTypeClass() == TypeClass_INTERFACE )
			{
				Reference< XShape > xShape;
				rValue >>= xShape;
				if( xShape.is() )
				{
					return makeAny( getClonedShape( xShape ) );
				}
				else
				{
					Reference< XAnimationNode > xNode;
					rValue >>= xNode;
					if( xNode.is() )
						return makeAny( getClonedNode( xNode ) );
				}
			}
			else if( rValue.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
			{
				ParagraphTarget aParaTarget;
				rValue >>= aParaTarget;

				aParaTarget.Shape = getClonedShape( aParaTarget.Shape );

				return makeAny( aParaTarget );
			}
			else if( rValue.getValueType() == ::getCppuType((const Event*)0) )
			{
				Event aEvent;
				rValue >>= aEvent;

				aEvent.Source = transformValue( aEvent.Source );

				return makeAny( aEvent );
			}
		}
		catch( Exception& e )
		{
			(void)e;
			DBG_ERROR(
				(OString("sd::CustomAnimationClonerImpl::transformValue(), "
						"exception caught: ") +
				rtl::OUStringToOString(
					comphelper::anyToString( cppu::getCaughtException() ),
					RTL_TEXTENCODING_UTF8 )).getStr() );
		}

		return rValue;
	}

    Reference< XShape > CustomAnimationClonerImpl::getClonedShape( const Reference< XShape >& xSource ) const
    {
        if( xSource.is() )
		{
			if( maShapeMap.find(xSource) != maShapeMap.end() )
			{
				return maShapeMap[xSource];
			}

			DBG_ASSERT( maShapeMap.empty(), "sd::CustomAnimationClonerImpl::getClonedShape() failed!" );
		}
        return xSource;
    }

	Reference< XAnimationNode > CustomAnimationClonerImpl::getClonedNode( const Reference< XAnimationNode >& xSource ) const
	{
		sal_Int32 nNode, nNodeCount = maSourceNodeVector.size();

		for( nNode = 0; nNode < nNodeCount; nNode++ )
		{
			if( maSourceNodeVector[nNode] == xSource )
				return maCloneNodeVector[nNode];
		}

		DBG_ERROR( "sd::CustomAnimationClonerImpl::getClonedNode() failed!" );
		return xSource;
	}
}