//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2004-2009. 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/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#include <boost/interprocess/detail/config_begin.hpp>
#include <algorithm>
#include <memory>
#include <deque>
#include <iostream>
#include <functional>
#include <list>

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/deque.hpp>
#include <boost/interprocess/indexes/flat_map_index.hpp>
#include "print_container.hpp"
#include "check_equal_containers.hpp"
#include "dummy_test_allocator.hpp"
#include "movable_int.hpp"
#include <boost/interprocess/allocators/allocator.hpp>
#include "allocator_v1.hpp"
#include <boost/interprocess/exceptions.hpp>
#include <boost/interprocess/detail/move.hpp>
#include <boost/interprocess/detail/mpl.hpp>
#include <boost/interprocess/detail/type_traits.hpp>
#include <string>
#include "get_process_id_name.hpp"
#include "emplace_test.hpp"

///////////////////////////////////////////////////////////////////
//                                                               //
//  This example repeats the same operations with std::deque and //
//  shmem_deque using the node allocator                         //
//  and compares the values of both containers                   //
//                                                               //
///////////////////////////////////////////////////////////////////

using namespace boost::interprocess;

//Explicit instantiation to detect compilation errors
template class boost::interprocess::deque<test::movable_and_copyable_int, 
   test::dummy_test_allocator<test::movable_and_copyable_int> >;

//Function to check if both sets are equal
template<class V1, class V2>
bool copyable_only(V1 *, V2 *, detail::false_type)
{
   return true;
}

//Function to check if both sets are equal
template<class V1, class V2>
bool copyable_only(V1 *shmdeque, V2 *stddeque, detail::true_type)
{
   typedef typename V1::value_type IntType;
   std::size_t size = shmdeque->size();
   stddeque->insert(stddeque->end(), 50, 1);
   shmdeque->insert(shmdeque->end(), 50, 1);
   if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
   {
      IntType move_me(1);
      stddeque->insert(stddeque->begin()+size/2, 50, 1);
      shmdeque->insert(shmdeque->begin()+size/2, 50, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
   }
   {
      IntType move_me(2);
      shmdeque->assign(shmdeque->size()/2, boost::interprocess::move(move_me));
      stddeque->assign(stddeque->size()/2, 2);
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
   }
   {
      IntType move_me(1);
      stddeque->clear();
      shmdeque->clear();
      stddeque->insert(stddeque->begin(), 50, 1);
      shmdeque->insert(shmdeque->begin(), 50, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
      stddeque->insert(stddeque->begin()+20, 50, 1);
      shmdeque->insert(shmdeque->begin()+20, 50, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
      stddeque->insert(stddeque->begin()+20, 20, 1);
      shmdeque->insert(shmdeque->begin()+20, 20, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
   }
   {
      IntType move_me(1);
      stddeque->clear();
      shmdeque->clear();
      stddeque->insert(stddeque->end(), 50, 1);
      shmdeque->insert(shmdeque->end(), 50, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
      stddeque->insert(stddeque->end()-20, 50, 1);
      shmdeque->insert(shmdeque->end()-20, 50, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
      stddeque->insert(stddeque->end()-20, 20, 1);
      shmdeque->insert(shmdeque->end()-20, 20, boost::interprocess::move(move_me));
      if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
   }

   return true;
}

//Test recursive structures
class recursive_deque
{
public:
   int id_;
   deque<recursive_deque> deque_;
};

template<class IntType, template<class T, class SegmentManager> class AllocatorType >
bool do_test()
{
   //Test for recursive types
   {
      deque<recursive_deque> recursive_deque_deque;
   }

   {
      //Now test move semantics
      deque<recursive_deque> original;
      deque<recursive_deque> move_ctor(boost::interprocess::move(original));
      deque<recursive_deque> move_assign;
      move_assign = boost::interprocess::move(move_ctor);
      move_assign.swap(original);
   }

   //Customize managed_shared_memory class
   typedef basic_managed_shared_memory
      <char,
      //simple_seq_fit<mutex_family>,
      rbtree_best_fit<mutex_family>,
      //flat_map_index
      iset_index
      > my_managed_shared_memory;

   //Alias AllocatorType type
   typedef AllocatorType<IntType, my_managed_shared_memory::segment_manager>
      shmem_allocator_t;

   //Alias deque types
   typedef deque<IntType, shmem_allocator_t>   MyShmDeque;
   typedef std::deque<int>                     MyStdDeque;
   const int Memsize = 65536;
   const char *const shMemName = test::get_process_id_name();
   const int max = 100;

   /*try*/{
      shared_memory_object::remove(shMemName);

      //Create shared memory
      my_managed_shared_memory segment(create_only, shMemName, Memsize);

      segment.reserve_named_objects(100);

      //Shared memory allocator must be always be initialized
      //since it has no default constructor
      MyShmDeque *shmdeque = segment.template construct<MyShmDeque>("MyShmDeque")
                              (segment.get_segment_manager());

      MyStdDeque *stddeque = new MyStdDeque;

      /*try*/{
         //Compare several shared memory deque operations with std::deque
         int i;
         for(i = 0; i < max*50; ++i){
            IntType move_me(i);
            shmdeque->insert(shmdeque->end(), boost::interprocess::move(move_me));
            stddeque->insert(stddeque->end(), i);
            shmdeque->insert(shmdeque->end(), IntType(i));
            stddeque->insert(stddeque->end(), int(i));
         }
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         shmdeque->clear();
         stddeque->clear();

         for(i = 0; i < max*50; ++i){
            IntType move_me(i);
            shmdeque->push_back(boost::interprocess::move(move_me));
            stddeque->push_back(i);
            shmdeque->push_back(IntType(i));
            stddeque->push_back(i);
         }
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         shmdeque->clear();
         stddeque->clear();

         for(i = 0; i < max*50; ++i){
            IntType move_me(i);
            shmdeque->push_front(boost::interprocess::move(move_me));
            stddeque->push_front(i);
            shmdeque->push_front(IntType(i));
            stddeque->push_front(int(i));
         }
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         typename MyShmDeque::iterator it;
         typename MyShmDeque::const_iterator cit = it;

         shmdeque->erase(shmdeque->begin()++);
         stddeque->erase(stddeque->begin()++);
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         shmdeque->erase(shmdeque->begin());
         stddeque->erase(stddeque->begin());
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         {
            //Initialize values
            IntType aux_vect[50];
            for(int i = 0; i < 50; ++i){
               IntType move_me (-1);
               aux_vect[i] = boost::interprocess::move(move_me);
            }
            int aux_vect2[50];
            for(int i = 0; i < 50; ++i){
               aux_vect2[i] = -1;
            }

            shmdeque->insert(shmdeque->end()
                              ,::boost::interprocess::make_move_iterator(&aux_vect[0])
                              ,::boost::interprocess::make_move_iterator(aux_vect + 50));
            stddeque->insert(stddeque->end(), aux_vect2, aux_vect2 + 50);
            if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

            for(int i = 0, j = static_cast<int>(shmdeque->size()); i < j; ++i){
               shmdeque->erase(shmdeque->begin());
               stddeque->erase(stddeque->begin());
            }
            if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
         }
         {
            IntType aux_vect[50];
            for(int i = 0; i < 50; ++i){
               IntType move_me(-1);
               aux_vect[i] = boost::interprocess::move(move_me);
            }
            int aux_vect2[50];
            for(int i = 0; i < 50; ++i){
               aux_vect2[i] = -1;
            }
            shmdeque->insert(shmdeque->begin()
                              ,::boost::interprocess::make_move_iterator(&aux_vect[0])
                              ,::boost::interprocess::make_move_iterator(aux_vect + 50));
            stddeque->insert(stddeque->begin(), aux_vect2, aux_vect2 + 50);
            if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;
         }

         if(!copyable_only(shmdeque, stddeque
                        ,detail::bool_<!::boost::interprocess::is_movable<IntType>::value>())){
            return false;
         }

         shmdeque->erase(shmdeque->begin());
         stddeque->erase(stddeque->begin());

         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         for(i = 0; i < max; ++i){
            IntType move_me(i);
            shmdeque->insert(shmdeque->begin(), boost::interprocess::move(move_me));
            stddeque->insert(stddeque->begin(), i);
         }
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return false;

         //Test insertion from list
         {
            std::list<int> l(50, int(1));
            shmdeque->insert(shmdeque->begin(), l.begin(), l.end());
            stddeque->insert(stddeque->begin(), l.begin(), l.end());
            if(!test::CheckEqualContainers(shmdeque, stddeque)) return 1;
            shmdeque->assign(l.begin(), l.end());
            stddeque->assign(l.begin(), l.end());
            if(!test::CheckEqualContainers(shmdeque, stddeque)) return 1;
         }

         shmdeque->resize(100);
         stddeque->resize(100);
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return 1;         

         shmdeque->resize(200);
         stddeque->resize(200);
         if(!test::CheckEqualContainers(shmdeque, stddeque)) return 1;         

         segment.template destroy<MyShmDeque>("MyShmDeque");
         delete stddeque;
         segment.shrink_to_fit_indexes();

         if(!segment.all_memory_deallocated())
            return false;
      }/*
      catch(std::exception &ex){
         std::cout << ex.what() << std::endl;
         return false;
      }*/
      
      std::cout << std::endl << "Test OK!" << std::endl;
   }/*
   catch(...){
      shared_memory_object::remove(shMemName);
      throw;
   }*/
   shared_memory_object::remove(shMemName);
   return true;
}

int main ()
{
   if(!do_test<int, allocator>())
      return 1;

   if(!do_test<test::movable_int, allocator>())
      return 1;

   if(!do_test<test::copyable_int, allocator>())
      return 1;

   if(!do_test<int, test::allocator_v1>())
      return 1;

   const test::EmplaceOptions Options = (test::EmplaceOptions)(test::EMPLACE_BACK | test::EMPLACE_FRONT | test::EMPLACE_BEFORE);

   if(!boost::interprocess::test::test_emplace
      < deque<test::EmplaceInt>, Options>())
      return 1;

   return 0;
}

#include <boost/interprocess/detail/config_end.hpp>
