//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-2011. 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)
//
// See http://www.boost.org/libs/container for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_CONTAINERS_DETAIL_MULTIALLOCATION_CHAIN_HPP
#define BOOST_CONTAINERS_DETAIL_MULTIALLOCATION_CHAIN_HPP

#include "config_begin.hpp"
#include <boost/container/container_fwd.hpp>
#include <boost/container/detail/utilities.hpp>
#include <boost/container/detail/type_traits.hpp>
#include <boost/container/detail/transform_iterator.hpp>
#include <boost/intrusive/slist.hpp>
#include <boost/pointer_to_other.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/move/move.hpp>

namespace boost {
namespace container {
namespace containers_detail {

template<class VoidPointer>
class basic_multiallocation_chain
{
   private:
   typedef bi::slist_base_hook<bi::void_pointer<VoidPointer>
                        ,bi::link_mode<bi::normal_link>
                        > node;

   typedef typename boost::pointer_to_other<VoidPointer, char>::type   char_ptr;
   typedef typename std::iterator_traits<char_ptr>::difference_type    difference_type;

   typedef bi::slist< node
                    , bi::linear<true>
                    , bi::cache_last<true>
                    , bi::size_type<typename boost::make_unsigned<difference_type>::type>
                    > slist_impl_t;
   slist_impl_t slist_impl_;

   static node & to_node(VoidPointer p)
   {  return *static_cast<node*>(static_cast<void*>(containers_detail::get_pointer(p))); }

   BOOST_MOVABLE_BUT_NOT_COPYABLE(basic_multiallocation_chain)

   public:


   typedef VoidPointer  void_pointer;
   typedef typename slist_impl_t::iterator iterator;
   typedef typename slist_impl_t::size_type size_type;

   basic_multiallocation_chain()
      :  slist_impl_()
   {}

   basic_multiallocation_chain(BOOST_RV_REF(basic_multiallocation_chain) other)
      :  slist_impl_()
   {  slist_impl_.swap(other.slist_impl_); }

   basic_multiallocation_chain& operator=(BOOST_RV_REF(basic_multiallocation_chain) other)
   {
      basic_multiallocation_chain tmp(boost::move(other));
      this->swap(tmp);
      return *this;
   }

   bool empty() const
   {  return slist_impl_.empty(); }

   size_type size() const
   {  return slist_impl_.size();  }

   iterator before_begin()
   {  return slist_impl_.before_begin(); }

   iterator begin()
   {  return slist_impl_.begin(); }

   iterator end()
   {  return slist_impl_.end(); }

   iterator last()
   {  return slist_impl_.last(); }

   void clear()
   {  slist_impl_.clear(); }

   iterator insert_after(iterator it, void_pointer m)
   {  return slist_impl_.insert_after(it, to_node(m));   }

   void push_front(void_pointer m)
   {  return slist_impl_.push_front(to_node(m));   }

   void push_back(void_pointer m)
   {  return slist_impl_.push_back(to_node(m));   }

   void pop_front()
   {  return slist_impl_.pop_front();   }

   void *front()
   {  return &*slist_impl_.begin();   }

   void splice_after(iterator after_this, basic_multiallocation_chain &x, iterator before_begin, iterator before_end)
   {  slist_impl_.splice_after(after_this, x.slist_impl_, before_begin, before_end);   }

   void splice_after(iterator after_this, basic_multiallocation_chain &x, iterator before_begin, iterator before_end, size_type n)
   {  slist_impl_.splice_after(after_this, x.slist_impl_, before_begin, before_end, n);   }

   void splice_after(iterator after_this, basic_multiallocation_chain &x)
   {  slist_impl_.splice_after(after_this, x.slist_impl_);   }

   void incorporate_after(iterator after_this, void_pointer begin , iterator before_end)
   {  slist_impl_.incorporate_after(after_this, &to_node(begin), &to_node(before_end));   }

   void incorporate_after(iterator after_this, void_pointer begin, void_pointer before_end, size_type n)
   {  slist_impl_.incorporate_after(after_this, &to_node(begin), &to_node(before_end), n);   }

   void swap(basic_multiallocation_chain &x)
   {  slist_impl_.swap(x.slist_impl_);   }

   static iterator iterator_to(void_pointer p)
   {  return slist_impl_t::s_iterator_to(to_node(p));   }

   std::pair<void_pointer, void_pointer> extract_data()
   {
      std::pair<void_pointer, void_pointer> ret
         (slist_impl_.begin().operator->()
         ,slist_impl_.last().operator->());
      slist_impl_.clear();
      return ret;
   }
};

template<class T>
struct cast_functor
{
   typedef typename containers_detail::add_reference<T>::type result_type;
   template<class U>
   result_type operator()(U &ptr) const
   {  return *static_cast<T*>(static_cast<void*>(&ptr));  }
};

template<class MultiallocationChain, class T>
class transform_multiallocation_chain
{
   private:
   BOOST_MOVABLE_BUT_NOT_COPYABLE(transform_multiallocation_chain)

   MultiallocationChain   holder_;
   typedef typename MultiallocationChain::void_pointer   void_pointer;
   typedef typename boost::pointer_to_other
      <void_pointer, T>::type                            pointer;

   static pointer cast(void_pointer p)
   {
      return pointer(static_cast<T*>(containers_detail::get_pointer(p)));
   }

   public:
   typedef transform_iterator
      < typename MultiallocationChain::iterator
      , containers_detail::cast_functor <T> >                 iterator;
   typedef typename MultiallocationChain::size_type           size_type;

   transform_multiallocation_chain()
      : holder_()
   {}

   transform_multiallocation_chain(BOOST_RV_REF(transform_multiallocation_chain) other)
      : holder_()
   {  this->swap(other); }

   transform_multiallocation_chain(BOOST_RV_REF(MultiallocationChain) other)
      : holder_(boost::move(other))
   {}

   transform_multiallocation_chain& operator=(BOOST_RV_REF(transform_multiallocation_chain) other)
   {
      transform_multiallocation_chain tmp(boost::move(other));
      this->swap(tmp);
      return *this;
   }

   void push_front(pointer mem)
   {  holder_.push_front(mem);  }

   void swap(transform_multiallocation_chain &other_chain)
   {  holder_.swap(other_chain.holder_); }

   void splice_after(iterator after_this, transform_multiallocation_chain &x, iterator before_begin, iterator before_end, size_type n)
   {  holder_.splice_after(after_this.base(), x.holder_, before_begin.base(), before_end.base(), n);  }

   void incorporate_after(iterator after_this, void_pointer begin, void_pointer before_end, size_type n)
   {  holder_.incorporate_after(after_this.base(), begin, before_end, n);  }

   void pop_front()
   {  holder_.pop_front();  }

   pointer front()
   {  return cast(holder_.front());   }

   bool empty() const
   {  return holder_.empty(); }

   iterator before_begin()
   {  return iterator(holder_.before_begin());   }

   iterator begin()
   {  return iterator(holder_.begin());   }

   iterator end()
   {  return iterator(holder_.end());   }

   iterator last()
   {  return iterator(holder_.last());   }

   size_type size() const
   {  return holder_.size();  }

   void clear()
   {  holder_.clear(); }

   iterator insert_after(iterator it, pointer m)
   {  return iterator(holder_.insert_after(it.base(), m)); }

   static iterator iterator_to(pointer p)
   {  return iterator(MultiallocationChain::iterator_to(p));  }

   std::pair<void_pointer, void_pointer> extract_data()
   {  return holder_.extract_data();  }

   MultiallocationChain extract_multiallocation_chain()
   {
      return MultiallocationChain(boost::move(holder_));
   }
};

}}}

// namespace containers_detail {
// namespace container {
// namespace boost {

#include <boost/container/detail/config_end.hpp>

#endif   //BOOST_CONTAINERS_DETAIL_MULTIALLOCATION_CHAIN_HPP
