#pragma once
#include "mof/stream/Manipulator.hpp"
#include "mof/stream/Cache.hpp"
#include "mof/Rectangle.hpp"
#include <vector> 

namespace mof
{
    #define DEFAULT_CASCADOR Multiply
//{{{ struct Add
    template<typename T>
    struct Add
    {
	    inline static T apply(const T& a , const T& b)
        {
	    	return a + b;
	    }
    };
//}}}
//{{{ struct Multiply
    template<typename T>
    struct Multiply
    {
	    inline static T apply(const T& a , const T& b)
        {
		    return a * b;
	    }
    };
//}}}
//{{{ struct Overwrite
    template<typename T>
    struct Overwrite
    {
	    inline static T apply(const T& , const T& b)
        {
		    return b;
	    }
    };
//}}}
//{{{ class Cascade
    template< typename T , class Cascador = DEFAULT_CASCADOR< T > >
    class Cascade : public Manipulator< T >{
    public:
        typedef typename std::shared_ptr< Cascade > Handler;

	    virtual ~Cascade()
        {
	    }

	    virtual T value( FrameNumber frame) const
        {
		    T obj = m_list[0]->value(frame);
		    for(unsigned int i = 1 ; i < m_list.size() ; i++)
            {
			    obj = Cascador::apply(obj , m_list[i]->value(frame));
		    }
		    return obj;
	    }

	    void clear()
        {
		    m_list.clear();
			mof::state_changed();
	    }

	    void add( const typename Manipulator<T>::Handler& handler )
        {
		    m_list.push_back( handler );
			mof::state_changed();
    	}

    private:	
	    typedef std::vector< typename Manipulator< T >::Handler > List;
	    List m_list;
        
        Cascade
        (
		    const typename Manipulator< T >::Handler& front , 
		    const typename Manipulator< T >::Handler& back 
        )
	    {
		    int length = &back - &front + 1;
		    if(length <= 0)throw std::invalid_argument("list size is 0");

		    for( int i = 0 ; i < length ; i++)
            {
		    	m_list.push_back( (&front)[i] );
	    	}
	    }
    

        Cascade(){}
        
        template< typename T , class Cascador >
        friend typename std::shared_ptr< Cascade< T , Cascador > >
        makeCascadeHandler
        (
	        const typename Manipulator< T >::Handler& front , 
	        const typename Manipulator< T >::Handler& back 
        );

        template< typename T , class Cascador >
        friend typename std::shared_ptr< Cascade< T , Cascador > > makeCascadeHandler();


    };
//}}}
//{{{ ヘルパ関数 
    template< typename T , class Cascador >
    std::shared_ptr< Cascade< T , Cascador > >
    makeCascadeHandler
    (
	    const typename Manipulator< T >::Handler& front , 
	    const typename Manipulator< T >::Handler& back 
    )
	{
        return typename Cascade< T , Cascador >::Handler
            (
                new Cascade< T , Cascador >( front , back ) 
            );
	}
    
    template< typename T , class Cascador >
    typename std::shared_ptr< Cascade< T , Cascador > >
    makeCascadeHandler( )
	{
        return typename Cascade< T , Cascador >::Handler
            (
                new Cascade< T , Cascador >
            );
	}

    template<typename T >
    typename std::shared_ptr< Cascade< T , DEFAULT_CASCADOR< T > > >
    makeCascadeHandler
    (
	    const typename Manipulator< T >::Handler& front , 
	    const typename Manipulator< T >::Handler& back 
    )
	{
        return makeCascadeHandler< T , DEFAULT_CASCADOR< T > >( front , back );
	}
    
    template<typename T >
    typename std::shared_ptr< Cascade< T , DEFAULT_CASCADOR< T > > >
    makeCascadeHandler( )
	{
        return makeCascadeHandler< T , DEFAULT_CASCADOR< T > >();
	}
//}}}
//{{{ ストリーム演算子
    template< typename T , class Cascador >
    std::shared_ptr< Cascade< T , Cascador > >& 
    operator << 
    (
        std::shared_ptr< Cascade< T , Cascador > >& a ,
        const typename Manipulator< T >::Handler& b 
    )
    {
        a->add( b );
        return a;
    }

    template< typename T , class Cascador >
    typename std::shared_ptr< Cascade< T , Cascador > >& 
    operator << 
    (
        std::shared_ptr< Cascade< T , Cascador > >& a ,
        const T& b 
    )
    {
        a->add( mof::makeConstantHandler( b ) );
        return a;
    }
//}}}
} //namespace mof

