/*//////////////////////////////////////////////////////////////////////////////
    Copyright (c) 2013 Jamboree

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//////////////////////////////////////////////////////////////////////////////*/
#ifndef BOOST_SAW_SUPPORT_FUSION_ADAPTED_PIXEL_HPP_INCLUDED
#define BOOST_SAW_SUPPORT_FUSION_ADAPTED_PIXEL_HPP_INCLUDED


#include <boost/mpl/assert.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/fusion/support/tag_of_fwd.hpp>
#include <boost/fusion/support/iterator_base.hpp>
#include <boost/fusion/support/category_of.hpp>
#include <boost/gil/color_base_algorithm.hpp>
#include <boost/saw/support/scale/raw.hpp>


namespace boost { namespace fusion
{
    struct gil_pixel_iterator_tag;
    
    template<typename ColorBase, int Pos>
    struct gil_pixel_iterator
      : iterator_base<gil_pixel_iterator<ColorBase, Pos> >
    {
        BOOST_MPL_ASSERT_RELATION(Pos, >=, 0);
        BOOST_MPL_ASSERT_RELATION(Pos, <=, gil::size<ColorBase>::value);
        
        struct category
          : random_access_traversal_tag
          , associative_tag
        {};
        
        typedef ColorBase pixel_type;
        BOOST_STATIC_CONSTANT(int, index = Pos);
        
        explicit gil_pixel_iterator(ColorBase& pixel)
          : pixel(pixel)
        {}
          
        ColorBase& pixel;
    };
}}

namespace boost { namespace fusion { namespace traits
{
    template<typename ColorBase, int Pos>
    struct tag_of<gil_pixel_iterator<ColorBase, Pos> >
    {
        typedef gil_pixel_iterator_tag type;
    };
}}}

namespace boost { namespace fusion { namespace extension
{
    template<typename Tag>
    struct value_of_impl;

    template<typename Tag>
    struct value_of_data_impl;
    
    template<typename Tag>
    struct deref_impl;
    
    template<typename Tag>
    struct deref_data_impl;
    
    template<typename Tag>
    struct advance_impl;
    
    template<typename Tag>
    struct distance_impl;
    
    template<typename Tag>
    struct next_impl;

    template<typename Tag>
    struct prior_impl;
    
    template<typename Tag>
    struct equal_to_impl;

    template<typename Tag>
    struct key_of_impl;
    
    template<>
    struct value_of_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator>
        struct apply
        {
            typedef typename
                gil::kth_element_type<
                    typename Iterator::pixel_type, Iterator::index>::type
            value_type;
            
            typedef saw::raw<value_type> type;
        };
    };

    template<>
    struct value_of_data_impl<gil_pixel_iterator_tag>
      : value_of_impl<gil_pixel_iterator_tag>
    {};
    
    template<>
    struct deref_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator>
        struct apply;
        
        template<typename ColorBase, int Pos>
        struct apply<gil_pixel_iterator<ColorBase, Pos> >
        {
            typedef typename
                gil::kth_element_reference_type<ColorBase, Pos>::type
            type;

            static inline
            type call(gil_pixel_iterator<ColorBase, Pos> const& it)
            {
                return gil::at_c<Pos>(it.pixel);
            }
        };
    };

    template<>
    struct deref_data_impl<gil_pixel_iterator_tag>
      : deref_impl<gil_pixel_iterator_tag>
    {};

    template<>
    struct advance_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator, typename N>
        struct apply
        {
            typedef gil_pixel_iterator
            <
                typename Iterator::pixel_type, Iterator::index + N::value
            >
            type;

            static inline
            type call(Iterator const& it)
            {
                return type(it.pixel);
            }
        };
    };

    template<>
    struct distance_impl<gil_pixel_iterator_tag>
    {
        template<typename First, typename Last>
        struct apply
        {
            typedef mpl::int_<First::index - Last::index> type;

            static inline
            type call(First const& first, Last const& last)
            {
                return type();
            }
        };
    };
    
    template<>
    struct next_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator>
        struct apply
        {
            typedef gil_pixel_iterator
            <
                typename Iterator::pixel_type, Iterator::index + 1
            >
            type;

            static inline
            type call(Iterator const& it)
            {
                return type(it.pixel);
            }
        };
    };
    
    template<>
    struct prior_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator>
        struct apply
        {
            typedef gil_pixel_iterator
            <
                typename Iterator::pixel_type, Iterator::index - 1
            >
            type;

            static inline
            type call(Iterator const& it)
            {
                return type(it.pixel);
            }
        };
    };

    template<>
    struct equal_to_impl<gil_pixel_iterator_tag>
    {
        template<typename It1, typename It2>
        struct apply
          : mpl::bool_<It1::index == It2::index>
        {};
    };

    template<>
    struct key_of_impl<gil_pixel_iterator_tag>
    {
        template<typename Iterator>
        struct apply
        {
            typedef typename
                Iterator::pixel_type::layout_t::channel_mapping_t
            channel_mapping;
            
            typedef typename
                mpl::at_c
                <
                    typename Iterator::pixel_type::layout_t::color_space_t
                  , mpl::distance
                    <
                        typename mpl::begin<channel_mapping>::type
                      , typename mpl::find
                        <
                            channel_mapping
                            // !!cannot use mpl::int_ here
                          , mpl::integral_c<int, Iterator::index> 
                        >::type
                    >::value
                >::type
            type;
        };
    };
}}}

namespace boost { namespace fusion
{
    struct gil_pixel_tag;
}}

namespace boost { namespace fusion { namespace traits
{
    template<typename ColorBase>
    struct tag_of<ColorBase,
        typename enable_if<gil::is_pixel<ColorBase> >::type>
    {
        typedef gil_pixel_tag type;
    };
}}}

namespace boost { namespace fusion { namespace extension
{
    template<>
    struct category_of_impl<gil_pixel_tag>
    {
        template<typename Sequence>
        struct apply
        {
            struct type : random_access_traversal_tag, associative_tag {};
        };
    };

    template<typename Tag>
    struct begin_impl;
    
    template<typename Tag>
    struct end_impl;
    
    template<typename Tag>
    struct at_impl;
    
    template<typename Tag>
    struct value_at_impl;
        
    template<typename Tag>
    struct size_impl;

    template<typename Tag>
    struct at_key_impl;
    
    template<typename Tag>
    struct value_at_key_impl;

    template<typename Tag>
    struct has_key_impl;
    
    template<typename Tag>
    struct is_sequence_impl;
        
    template<typename Tag>
    struct is_view_impl;

    template<>
    struct begin_impl<gil_pixel_tag>
    {
        template<typename Sequence>
        struct apply
        {
            typedef gil_pixel_iterator<Sequence, 0> type;

            static inline
            type call(Sequence& seq)
            {
                return type(seq);
            }
        };
    };
    
    template<>
    struct end_impl<gil_pixel_tag>
    {
        template<typename Sequence>
        struct apply
        {
            typedef
                gil_pixel_iterator<Sequence, gil::size<Sequence>::value>
            type;

            static inline
            type call(Sequence& seq)
            {
                return type(seq);
            }
        };
    };
    
    template<>
    struct at_impl<gil_pixel_tag>
    {
        template<typename Sequence, typename N>
        struct apply
        {
            typedef typename
                gil::kth_element_reference_type<Sequence, N::value>::type
            type;

            static inline
            type call(Sequence& seq)
            {
                return gil::at_c<N::value>(seq);
            }
        };
    };
    
    template<>
    struct value_at_impl<gil_pixel_tag>
    {
        template<typename Sequence, typename N>
        struct apply
        {
            typedef typename
                gil::kth_element_type<Sequence, N::value>::type
            value_type;

            typedef saw::raw<value_type> type;
        };
    };
    
    template<>
    struct size_impl<gil_pixel_tag>
    {
        template<typename Sequence>
        struct apply
          : gil::size<Sequence>
        {};
    };
    
    template<>
    struct value_at_key_impl<gil_pixel_tag>
    {
        template<typename Sequence, typename Key>
        struct apply
        {
            typedef typename
                gil::color_element_type<Sequence, Key>::type
            type;
        };
    };
    
    template<>
    struct at_key_impl<gil_pixel_tag>
    {
        template<typename Sequence, typename Key>
        struct apply
        {
            typedef
                gil::color_element_reference_type<Sequence, Key>
            color_element;

            typedef typename color_element::type type;

            static inline
            type call(Sequence& seq)
            {
                return color_element::get(seq);
            }
        };
    };
    
    template<>
    struct has_key_impl<gil_pixel_tag>
    {
        template<typename Sequence, typename Key>
        struct apply
          : gil::contains_color<Sequence, Key>
        {};
    };
    
    template<>
    struct is_sequence_impl<gil_pixel_tag>
    {
        template<typename T>
        struct apply : mpl::true_ {};
    };
    
    template<>
    struct is_view_impl<gil_pixel_tag>
    {
        template<typename T>
        struct apply : mpl::false_ {};
    };
}}}


#endif

        
