//-----------------------------------------------------------------------------
// boost-libs variant/test/test7.cpp header file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2003
// Eric Friedman, Itay Maman
//
// 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)

#include "boost/test/minimal.hpp"
#include "boost/variant.hpp"

#include "jobs.h"

#include <iostream>
#include <algorithm>
#include <string>
#include <map>

#include "boost/detail/workaround.hpp"
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1200)
#   include "boost/mpl/bool.hpp"
#   include "boost/type_traits/is_same.hpp"
#endif


using namespace boost;
using namespace std;


struct jas
{
   jas(int n = 364);
   jas(const jas& other);

   ~jas();
   jas& operator=(const jas& other);

   void swap(jas& other);

   int n_;

   int sn_;
   static int s_inst_id_;
}; 

struct Tracker
{
   typedef map<const jas*,int> table_type;
   typedef table_type::iterator iterator_type;

   static table_type s_this_to_sn_;

   static void insert(const jas& j)
   {
      s_this_to_sn_[&j] = j.sn_;      
      cout << "jas( " << j.sn_ << ") Registered" << endl;
   }
   
   static void remove(const jas& j)
   {
      iterator_type iter = s_this_to_sn_.find(&j);
      BOOST_CHECK(iter != s_this_to_sn_.end());
      BOOST_CHECK( ((*iter).second) == j.sn_);

      int sn = (*iter).second;
      if(sn != j.sn_)
      {
         cout << "Mismatch: this = " << (*iter).first << ", sn_ = " << sn
            << ", other: this = " << &j << ", j.sn_ = " << j.sn_ << endl;
      }

      BOOST_CHECK(sn == j.sn_);

   

      

      s_this_to_sn_.erase(&j);
      cout << "jas( " << j.sn_ << ") Removed" << endl;
   }

   static void check()
   {
      BOOST_CHECK(s_this_to_sn_.empty());      
   }
};

Tracker::table_type Tracker::s_this_to_sn_;



jas::jas(int n) : n_(n) 
{ 
   sn_ = s_inst_id_;
   s_inst_id_ += 1;
      
   Tracker::insert(*this);
}

jas::jas(const jas& other) : n_(other.n_)
{
   sn_ = s_inst_id_;
   s_inst_id_ += 1;      

   Tracker::insert(*this);
}

jas::~jas()
{
   Tracker::remove(*this);
}

jas& jas::operator=(const jas& other)
{
   jas temp(other);
   swap(temp);

   return *this;
}

void jas::swap(jas& other)
{   
   Tracker::remove(*this);
   Tracker::remove(other);

   std::swap(n_, other.n_);
   std::swap(sn_, other.sn_);

   Tracker::insert(*this);
   Tracker::insert(other);
}

int jas::s_inst_id_ = 0;


bool operator==(const jas& a, const jas& b)
{
   return a.n_ == b.n_;
}

ostream& operator<<(ostream& out, const jas& a)
{
   cout << "jas::n_ = " << a.n_;
   return out;
}


template<typename ValueType>
struct compare_helper : boost::static_visitor<bool>
{
   compare_helper(ValueType& expected) : expected_(expected) { }

#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1200)

   bool operator()(const ValueType& value)
   {
      return value == expected_;
   }

   template <typename T>
   bool operator()(const T& )
   {
      return false;         
   }

#else // MSVC6

private:

   bool compare_impl(const ValueType& value, boost::mpl::true_)
   {
      return value == expected_;
   }

   template <typename T>
   bool compare_impl(const T&, boost::mpl::false_)
   {
      return false;
   }

public:

   template <typename T>
   bool operator()(const T& value)
   {
      typedef typename boost::is_same<T, ValueType>::type
          T_is_ValueType;

      return compare_impl(value, T_is_ValueType());
   }

#endif // MSVC6 workaround

   ValueType& expected_;

};

template<typename VariantType, typename ExpectedType>
void var_compare(const VariantType& v, ExpectedType expected)
{
   compare_helper<ExpectedType> ch(expected);

   bool checks = boost::apply_visitor(ch, v);
   BOOST_CHECK(checks);
}


void run()
{   
   variant<string, short> v0;

   var_compare(v0, string(""));

   v0 = 8;
   var_compare(v0, static_cast<short>(8));

   v0 = "penny lane";
   var_compare(v0, string("penny lane"));

   variant<jas, string, int> v1, v2 = jas(195);
   var_compare(v1, jas(364));

   v1 = jas(500);
   v1.swap(v2);

   var_compare(v1, jas(195));
   var_compare(v2, jas(500));


   variant<string, int> v3;
   var_compare(v3, string(""));
}


int test_main(int , char* [])
{
   run();
   Tracker::check();

   return 0;
}

