#pragma once
#include "mof/stream/Manipulator.hpp"


namespace mof{

    template< typename T >
    class Loop : public Manipulator< T >
    {
	    
    public:
		typedef std::shared_ptr<Loop<T>> Handler;
		typedef std::shared_ptr<Loop<T>> ptr; 

        virtual ~Loop( ){}	

    	virtual T value( FrameNumber frame) const
        {
	    	return m_pBody->value( getInnerFrameNumber( frame ) );
	    }


		Loop(const typename Manipulator<T>::Handler& pBody , FrameNumber endLoopFrameNumber)
	    : m_pBody(pBody) , m_beginLoopFrameNumber(0) , m_endLoopFrameNumber(endLoopFrameNumber)
	    {
	    }

	    Loop
        (
		    const typename Manipulator<T>::Handler& pBody ,
		    FrameNumber beginLoopFrameNumber ,
		    FrameNumber endLoopFrameNumber
	    ) 
        : m_pBody(pBody) ,  m_beginLoopFrameNumber(beginLoopFrameNumber) , m_endLoopFrameNumber(endLoopFrameNumber)
	    {
		    if(m_endLoopFrameNumber < m_beginLoopFrameNumber)throw std::invalid_argument("endLoop < beginLoop");
	    }


    private:

        typename Manipulator<T>::Handler m_pBody;
	    FrameNumber m_beginLoopFrameNumber;
	    FrameNumber m_endLoopFrameNumber;

	    FrameNumber getInnerFrameNumber(FrameNumber frame) const
        {
		    if(frame < m_beginLoopFrameNumber)return frame;
		    FrameNumber d = m_endLoopFrameNumber - m_beginLoopFrameNumber;
		    return (frame - m_beginLoopFrameNumber) % d + m_beginLoopFrameNumber;
	    }

        template<typename T > friend
		typename Loop<T>::Handler
	    makeLoopHandler
        (
		    const typename Manipulator<T>::Handler& pBody ,
		    FrameNumber beginLoopFrameNumber ,
		    FrameNumber endLoopFrameNumber
        );

        template<typename T > friend
		typename Loop<T>::Handler
	    makeLoopHandler
        (
		    const typename Manipulator<T>::Handler& pBody ,
		    FrameNumber endLoopFrameNumber
        );

    };
//{{{ ヘルパ関数
	template<typename T >
	typename Loop<T>::Handler
	makeLoopHandler
    (
		const typename Manipulator<T>::Handler& pBody ,
		FrameNumber beginLoopFrameNumber ,
		FrameNumber endLoopFrameNumber
    )
    {
		return typename Loop< T >::Handler
            (
			    new Loop< T >(pBody , beginLoopFrameNumber , endLoopFrameNumber)
			);
	}

    template<typename T >
	typename Loop<T>::Handler
	makeLoopHandler
    (
		const typename Manipulator<T>::Handler& pBody ,
		FrameNumber endLoopFrameNumber
    )
    {
		return typename Loop< T >::Handler
            (
			    new Loop< T >(pBody , endLoopFrameNumber)
			);
	}
//}}}
} //namespace mof

