// boost heap: node tree iterator helper classes
//
// Copyright (C) 2010 Tim Blechmann
//
// 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_HEAP_DETAIL_TREE_ITERATOR_HPP
#define BOOST_HEAP_DETAIL_TREE_ITERATOR_HPP

#include <functional>
#include <vector>

#include <boost/core/allocator_access.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/conditional.hpp>
#include <queue>

namespace boost { namespace heap { namespace detail {


template < typename type >
struct identity
{
    type& operator()( type& x ) const noexcept
    {
        return x;
    }

    const type& operator()( const type& x ) const noexcept
    {
        return x;
    }
};

template < typename Node >
struct dereferencer
{
    template < typename Iterator >
    Node* operator()( Iterator const& it )
    {
        return static_cast< Node* >( *it );
    }
};

template < typename Node >
struct pointer_to_reference
{
    template < typename Iterator >
    const Node* operator()( Iterator const& it )
    {
        return static_cast< const Node* >( &*it );
    }
};


template < typename HandleType, typename Alloc, typename ValueCompare >
struct unordered_tree_iterator_storage
{
    unordered_tree_iterator_storage( ValueCompare const& )
    {}

    void push( HandleType h )
    {
        data_.push_back( h );
    }

    HandleType const& top( void )
    {
        return data_.back();
    }

    void pop( void )
    {
        data_.pop_back();
    }

    bool empty( void ) const
    {
        return data_.empty();
    }

    std::vector< HandleType, typename boost::allocator_rebind< Alloc, HandleType >::type > data_;
};

template < typename ValueType, typename HandleType, typename Alloc, typename ValueCompare, typename ValueExtractor >
struct ordered_tree_iterator_storage : ValueExtractor
{
    struct compare_values_by_handle : ValueCompare
    {
        explicit compare_values_by_handle( ValueCompare const& cmp ) :
            ValueCompare( cmp )
        {}

        bool operator()( HandleType const& lhs, HandleType const& rhs ) const
        {
            return ValueCompare::operator()( lhs->value, rhs->value );
        }
    };

    ordered_tree_iterator_storage( ValueCompare const& cmp ) :
        data_( compare_values_by_handle( cmp ) )
    {}

    void push( HandleType h )
    {
        data_.push( h );
    }

    void pop( void )
    {
        data_.pop();
    }

    HandleType const& top( void )
    {
        return data_.top();
    }

    bool empty( void ) const noexcept
    {
        return data_.empty();
    }

    std::priority_queue< HandleType,
                         std::vector< HandleType, typename boost::allocator_rebind< Alloc, HandleType >::type >,
                         compare_values_by_handle >
        data_;
};


/* tree iterator helper class
 *
 * Requirements:
 * Node provides child_iterator
 * ValueExtractor can convert Node->value to ValueType
 *
 * */
template < typename Node,
           typename ValueType,
           typename Alloc            = std::allocator< Node >,
           typename ValueExtractor   = identity< typename Node::value_type >,
           typename PointerExtractor = dereferencer< Node >,
           bool check_null_pointer   = false,
           bool ordered_iterator     = false,
           typename ValueCompare     = std::less< ValueType > >
class tree_iterator :
    public boost::iterator_adaptor<
        tree_iterator< Node, ValueType, Alloc, ValueExtractor, PointerExtractor, check_null_pointer, ordered_iterator, ValueCompare >,
        const Node*,
        ValueType,
        boost::forward_traversal_tag >,
    ValueExtractor,
    PointerExtractor
{
    typedef boost::iterator_adaptor<
        tree_iterator< Node, ValueType, Alloc, ValueExtractor, PointerExtractor, check_null_pointer, ordered_iterator, ValueCompare >,
        const Node*,
        ValueType,
        boost::forward_traversal_tag >
        adaptor_type;

    friend class boost::iterator_core_access;

    typedef typename std::conditional<
        ordered_iterator,
        ordered_tree_iterator_storage< ValueType, const Node*, Alloc, ValueCompare, ValueExtractor >,
        unordered_tree_iterator_storage< const Node*, Alloc, ValueCompare > >::type unvisited_node_container;

public:
    tree_iterator( void ) :
        adaptor_type( 0 ),
        unvisited_nodes( ValueCompare() )
    {}

    tree_iterator( ValueCompare const& cmp ) :
        adaptor_type( 0 ),
        unvisited_nodes( cmp )
    {}

    tree_iterator( const Node* it, ValueCompare const& cmp ) :
        adaptor_type( it ),
        unvisited_nodes( cmp )
    {
        if ( it )
            discover_nodes( it );
    }

    /* fills the iterator from a list of possible top nodes */
    template < typename NodePointerIterator >
    tree_iterator( NodePointerIterator begin, NodePointerIterator end, const Node* top_node, ValueCompare const& cmp ) :
        adaptor_type( 0 ),
        unvisited_nodes( cmp )
    {
        BOOST_STATIC_ASSERT( ordered_iterator );
        if ( begin == end )
            return;

        adaptor_type::base_reference() = top_node;
        discover_nodes( top_node );

        for ( NodePointerIterator it = begin; it != end; ++it ) {
            const Node* current_node = static_cast< const Node* >( &*it );
            if ( current_node != top_node )
                unvisited_nodes.push( current_node );
        }
    }

    bool operator!=( tree_iterator const& rhs ) const
    {
        return adaptor_type::base() != rhs.base();
    }

    bool operator==( tree_iterator const& rhs ) const
    {
        return !operator!=( rhs );
    }

    const Node* get_node() const
    {
        return adaptor_type::base_reference();
    }

private:
    void increment( void )
    {
        if ( unvisited_nodes.empty() )
            adaptor_type::base_reference() = 0;
        else {
            const Node* next = unvisited_nodes.top();
            unvisited_nodes.pop();
            discover_nodes( next );
            adaptor_type::base_reference() = next;
        }
    }

    ValueType const& dereference() const
    {
        return ValueExtractor::operator()( adaptor_type::base_reference()->value );
    }

    void discover_nodes( const Node* n )
    {
        for ( typename Node::const_child_iterator it = n->children.begin(); it != n->children.end(); ++it ) {
            const Node* n = PointerExtractor::operator()( it );
            if ( check_null_pointer && n == nullptr )
                continue;
            unvisited_nodes.push( n );
        }
    }

    unvisited_node_container unvisited_nodes;
};

template < typename Node, typename NodeList >
struct list_iterator_converter
{
    typename NodeList::const_iterator operator()( const Node* node )
    {
        return NodeList::s_iterator_to( *node );
    }

    Node* operator()( typename NodeList::const_iterator it )
    {
        return const_cast< Node* >( static_cast< const Node* >( &*it ) );
    }
};

template < typename Node,
           typename NodeIterator,
           typename ValueType,
           typename ValueExtractor   = identity< typename Node::value_type >,
           typename IteratorCoverter = identity< NodeIterator > >
class recursive_tree_iterator :
    public boost::iterator_adaptor< recursive_tree_iterator< Node, NodeIterator, ValueType, ValueExtractor, IteratorCoverter >,
                                    NodeIterator,
                                    ValueType const,
                                    boost::bidirectional_traversal_tag >,
    ValueExtractor,
    IteratorCoverter
{
    typedef boost::iterator_adaptor<
        recursive_tree_iterator< Node, NodeIterator, ValueType, ValueExtractor, IteratorCoverter >,
        NodeIterator,
        ValueType const,
        boost::bidirectional_traversal_tag >
        adaptor_type;

    friend class boost::iterator_core_access;


public:
    recursive_tree_iterator( void ) :
        adaptor_type( 0 )
    {}

    explicit recursive_tree_iterator( NodeIterator const& it ) :
        adaptor_type( it )
    {}

    void increment( void )
    {
        NodeIterator next = adaptor_type::base_reference();

        const Node* n = get_node( next );
        if ( n->children.empty() ) {
            const Node* parent = get_node( next )->get_parent();

            ++next;

            while ( true ) {
                if ( parent == nullptr || next != parent->children.end() )
                    break;

                next   = IteratorCoverter::operator()( parent );
                parent = get_node( next )->get_parent();
                ++next;
            }
        } else
            next = n->children.begin();

        adaptor_type::base_reference() = next;
        return;
    }

    ValueType const& dereference() const
    {
        return ValueExtractor::operator()( get_node( adaptor_type::base_reference() )->value );
    }

    static const Node* get_node( NodeIterator const& it )
    {
        return static_cast< const Node* >( &*it );
    }

    const Node* get_node() const
    {
        return get_node( adaptor_type::base_reference() );
    }
};


}}} // namespace boost::heap::detail

#endif /* BOOST_HEAP_DETAIL_TREE_ITERATOR_HPP */
