//-----------------------------------------------------------------------------
// boost-libs variant/libs/test/jobs.h header file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2003
// Eric Friedman, Itay Maman
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee, 
// provided that the above copyright notice appears in all copies and 
// that both the copyright notice and this permission notice appear in 
// supporting documentation. No representations are made about the 
// suitability of this software for any purpose. It is provided "as is" 
// without express or implied warranty.

#ifndef _JOBSH_INC_
#define _JOBSH_INC_

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <typeinfo>
#include <vector>

#include "boost/variant/variant_fwd.hpp"
#include "boost/variant/get.hpp"
#include "boost/variant/apply_visitor.hpp"
#include "boost/variant/static_visitor.hpp"

#include "boost/detail/workaround.hpp"
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0551))
#    pragma warn -lvc
#endif

struct to_text : boost::static_visitor<std::string>
{
private: // NO_FUNCTION_TEMPLATE_ORDERING workaround

    template < BOOST_VARIANT_ENUM_PARAMS(typename U) >
    std::string to_text_impl(
          const boost::variant< BOOST_VARIANT_ENUM_PARAMS(U) >& operand, long
        ) const
    {
        std::ostringstream ost;
        ost << "[V] " << boost::apply_visitor(to_text(), operand);

        return ost.str();
    }

    template <typename Value>
    std::string to_text_impl(const Value& operand, int) const
    {
        std::ostringstream ost;
        ost << "[V] " << operand;

        return ost.str();
    }

public:

    template <typename T>
    std::string operator()(const T& operand) const
    {
        return to_text_impl(operand, 1L);
    }

};

struct total_sizeof : boost::static_visitor<int>
{
   total_sizeof() : total_(0) { }

   template<class Value>
   int operator()(const Value&) const
   {
      total_ += sizeof(Value);
      return total_;
   }

   int result() const
   {
      return total_;
   }

   mutable int total_;

}; // total_sizeof



//Function object: sum_int
//Description: Compute total sum of a series of numbers, (when called successively)
//Use sizeof(T) if applied with a non-integral type
struct sum_int : boost::static_visitor<int>
{
   
   sum_int() : total_(0) { }


   template<int n>
   struct int_to_type
   {
      BOOST_STATIC_CONSTANT(int, value = n);
   }; 

   //Integral type - add numerical value
   template<typename T>
   void add(T t, int_to_type<true> ) const
   {
      total_ += t;
   }

   //Other types - add sizeof<T>
   template<typename T>
   void add(T& , int_to_type<false> ) const
   {
      total_ += sizeof(T);
   }

   template<typename T>
   int operator()(const T& t) const
   {
      //Int_to_type is used to select the correct add() overload
      add(t, int_to_type<boost::is_integral<T>::value>());
      return total_;
   }

   int result() const
   {
      return total_;
   }

private:
   mutable int total_;

}; //sum_int






//Function object: sum_double
//Description: Compute total sum of a series of numbers, (when called successively)
//Accpetable input types: float, double (Other types are silently ignored)
struct sum_double : boost::static_visitor<double>
{
   
   sum_double() : total_(0) { }

   void operator()(float value) const
   {
      total_ += value;
   }

   void operator()(double value) const
   {
      total_ += value;
   }

   template<typename T>
   void operator()(const T&) const
   {
      //Do nothing
   }

   double result() const
   {
      return total_;
   }

private:
   mutable double total_;

}; //sum_double



struct int_printer : boost::static_visitor<std::string>
{
   
   int_printer(std::string prefix_s = "") : prefix_s_(prefix_s) { }
   int_printer(const int_printer& other) : prefix_s_(other.prefix_s_)
   {
      ost_ << other.str();
   }

   std::string operator()(int x) const
   {
      ost_ << prefix_s_ << x;
      return str();
   }

   std::string operator()(const std::vector<int>& x) const
   {
      ost_ << prefix_s_;

      //Use another Int_printer object for printing a list of all integers
      int_printer job(",");
      ost_ << std::for_each(x.begin(), x.end(), job).str();
      
      return str();
   }

   std::string str() const
   {
      return ost_.str();
   }

private:
   std::string prefix_s_;
   mutable std::ostringstream ost_;
};  //int_printer


struct int_adder : boost::static_visitor<>
{
   
   int_adder(int rhs) : rhs_(rhs) { }

   result_type operator()(int& lhs) const
   {
      lhs += rhs_;
   }

   template<typename T>
   result_type operator()(const T& ) const
   {
      //Do nothing
   }

   int rhs_;
}; //int_adder




struct held_type_name : boost::static_visitor<std::string>
{
   
   template<typename T>
   std::string operator()(const T& ) const
   {
      ost_ << '[' << typeid(T).name() << ']';
      return result();
   }

   std::string result() const
   {
      return ost_.str();
   }

   mutable std::ostringstream ost_;

}; //held_type_name




template<typename T>
struct spec 
{
   typedef T result;
};

template<typename VariantType, typename S>
inline void verify(VariantType& var, spec<S>, std::string str = "")
{
   const VariantType& cvar = var;

   BOOST_CHECK(boost::apply_visitor(total_sizeof(), cvar) == sizeof(S));
   BOOST_CHECK(cvar.type() == typeid(S));

   //
   // Check get<>()
   //
   BOOST_CHECK(boost::get<S>(&var));
   BOOST_CHECK(boost::get<S>(&cvar));

   const S* ptr1 = 0;
   const S* ptr2 = 0;
   try
   {
      S& r = boost::get<S>(var);
      ptr1 = &r;
   }
   catch(boost::bad_get& )
   {
      BOOST_ERROR( "get<S> failed unexpectedly" );
   }

   try
   {
      const S& cr = boost::get<S>(cvar);
      ptr2 = &cr;
   }
   catch(boost::bad_get& )
   {
      BOOST_ERROR( "get<S> const failed unexpectedly" );
   }

   BOOST_CHECK(ptr1 != 0 && ptr2 == ptr1);

   //
   // Check string content
   //
   if(str.length() > 0)
   {
      std::string temp = boost::apply_visitor(to_text(), cvar);
      std::cout << "temp = " << temp << ", str = " << str << std::endl;
      BOOST_CHECK(temp == str);         
   }
}


template<typename VariantType, typename S>
inline void verify_not(VariantType& var, spec<S>)
{
   const VariantType& cvar = var;

   BOOST_CHECK(cvar.type() != typeid(S));

   //
   // Check get<>()
   //
   BOOST_CHECK(!boost::get<S>(&var));
   BOOST_CHECK(!boost::get<S>(&cvar));

   const S* ptr1 = 0;
   const S* ptr2 = 0;
   try
   {
      S& r = boost::get<S>(var); // should throw
      BOOST_ERROR( "get<S> passed unexpectedly" );

      ptr1 = &r;
   }
   catch(boost::bad_get& )
   {
      // do nothing except pass-through
   }

   try
   {
      const S& cr = boost::get<S>(var); // should throw
      BOOST_ERROR( "get<S> const passed unexpectedly" );

      ptr2 = &cr;
   }
   catch(boost::bad_get& )
   {
      // do nothing except pass-through
   }

   BOOST_CHECK(ptr1 == 0 && ptr2 == 0);   
}


#endif //_JOBSH_INC_
