#pragma once
#include "mof/stream/Manipulator.hpp"
#include <map>
#include <stdexcept>
#include <boost/function/function2.hpp>
#include "mof/Interpolation.hpp"

namespace mof{

    template<typename T >
    class KeyFrameAnimation : public Manipulator< T >
    {
	public:
		typedef std::shared_ptr<KeyFrameAnimation<T>> Handler;
	    typedef std::pair< FrameNumber , T > KeyFrame;
	    typedef typename std::map< FrameNumber , T> KeyMap;

        virtual ~KeyFrameAnimation(){}

	    virtual T value( mof::FrameNumber frame) const
        {
		    return m_interpolator(m_map , frame);
    	}

	    mof::FrameNumber getFinalKeyFrameNumber()
        {
		    return m_maxKeyFrameNumber;
	    }

    private:
	    KeyMap m_map;
	    FrameNumber m_maxKeyFrameNumber;
	    boost::function2<T , const KeyMap& , mof::FrameNumber> m_interpolator;

        KeyFrameAnimation
        (
		    const KeyFrame& front ,
            const KeyFrame& back , 
		    const boost::function2< T , const KeyMap& , FrameNumber > & interpolator
		)
        : m_interpolator(interpolator)
	    {
		    int length = &back - &front + 1;
		    if(length < 1)throw std::invalid_argument("length < 1");
		    m_maxKeyFrameNumber = 0;
		    for(int i = 0 ; i < length ; i++)
            {
			    if((&front)[i].first > m_maxKeyFrameNumber)m_maxKeyFrameNumber = (&front)[i].first;
			    m_map.insert( std::make_pair((&front)[i].first , (&front)[i].second ) );
	    	}
    	}

	    KeyFrameAnimation
        (
		    const KeyFrame& front , const KeyFrame& back 
		)
        : m_interpolator(&mof::linerInterpolate<T>)
	    {
		    int length = &back - &front + 1;
		    if(length < 1)throw std::invalid_argument("length < 1");
		    m_maxKeyFrameNumber = 0;
		    for(int i = 0 ; i < length ; i++)
            {
			    if((&front)[i].first > m_maxKeyFrameNumber)m_maxKeyFrameNumber = (&front)[i].first;
			    m_map.insert( std::make_pair((&front)[i].first , (&front)[i].second ) );
		    }
	    }

        template< typename T > friend
	    typename KeyFrameAnimation< T >::KeyFrame 
	    makeKeyFrame( mof::FrameNumber frame , const T & obj );

        template< typename T > friend
        typename KeyFrameAnimation< T >::Handler 
        makeKeyFrameAnimationHandler
        (
	        const std::pair< FrameNumber , T >& front ,
            const std::pair< FrameNumber , T >& back  ,
		    const boost::function2< T , const std::map< FrameNumber , T >& , FrameNumber> & interpolator
        );

        template< typename T > friend
        typename KeyFrameAnimation< T >::Handler 
        makeKeyFrameAnimationHandler
        (
	        const std::pair< FrameNumber , T >& front ,
            const std::pair< FrameNumber , T >& back  
        );

    };
//{{{ ヘルパ関数
	template< typename T >
	typename KeyFrameAnimation< T >::KeyFrame 
	makeKeyFrame( mof::FrameNumber frame , const T & obj )
    {
		return std::make_pair< FrameNumber , T>(frame , obj);
	}

    template< typename T >
    typename KeyFrameAnimation< T >::Handler 
    makeKeyFrameAnimationHandler
    (
	    const std::pair< FrameNumber , T >& front ,
        const std::pair< FrameNumber , T >& back  ,
		const boost::function2< T , const std::map< FrameNumber , T >& , FrameNumber> & interpolator
    )
	{
        return KeyFrameAnimation< T >::Handler
            (
                new KeyFrameAnimation< T >( front , back , interpolator )
            );
   	}

    template< typename T >
    typename KeyFrameAnimation< T >::Handler
    makeKeyFrameAnimationHandler
    (
	    const std::pair< FrameNumber, T>& front,
        const std::pair< FrameNumber, T>& back  
    )
    {
        return KeyFrameAnimation< T >::Handler
            (
                new KeyFrameAnimation< T >( front , back ) 
            );
   	}

    template< typename T >
    typename std::shared_ptr<KeyFrameAnimation<T>> 
    makeKeyFrameAnimationHandler
    (
	    FrameNumber fn1 , const T& v1 ,
	    FrameNumber fn2 , const T& v2 
    )
    {
        KeyFrameAnimation<T>::KeyFrame keyFrames[] =
        {
            makeKeyFrame(fn1 , v1) ,
            makeKeyFrame(fn2 , v2) 
        };
        return makeKeyFrameAnimationHandler(keyFrames[0] , lastOf(keyFrames));
   	}
    
    template< typename T >
    typename std::shared_ptr<KeyFrameAnimation< T >> 
    makeKeyFrameAnimationHandler
    (
	    FrameNumber fn1 , const T& v1 ,
	    FrameNumber fn2 , const T& v2 ,
		const boost::function2< T , const std::map< FrameNumber , T >& , FrameNumber> & interpolator
    )
    {
        KeyFrameAnimation<T>::KeyFrame keyFrames[] =
        {
            makeKeyFrame(fn1 , v1) ,
            makeKeyFrame(fn2 , v2) 
        };
        return makeKeyFrameAnimationHandler(keyFrames[0] , lastOf(keyFrames) , interpolator);
   	}



//}}}
} //namespace mof

