// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WT_DBO_TUPLE_PTR_H_
#define WT_DBO_TUPLE_PTR_H_

#include <boost/tuple/tuple.hpp>
#include <Wt/Dbo/Query>

namespace Wt {
  namespace Dbo {

template <>
struct persist<boost::tuples::null_type, void>
{
  template <class A>
  static void apply(boost::tuples::null_type& obj, A& action)
  { }
};

namespace Impl {
  template <class T>
  struct ptr_type
  {
    typedef ptr<T> type;
  };

  template <>
  struct ptr_type< boost::tuples::null_type >
  {
    typedef boost::tuples::null_type type;
  };

  template <int N, class T>
  struct helper
  {
    typedef typename boost::tuples::element<N, T>::type ElementType;

    static void getFields(Session& session,
			  std::vector<std::string> *aliases,
			  std::vector<FieldInfo>& result)
    {
      helper<N-1, T>::getFields(session, aliases, result);
      query_result_traits<ElementType>::getFields(session, aliases, result);
    }

    static void load(Session& session, SqlStatement& statement,
		     int& column, T& result)
    {
      helper<N-1, T>::load(session, statement, column, result);

      boost::get<N>(result)
	= query_result_traits<ElementType>::load(session, statement, column);
    }

    static void getValues(const T& result, std::vector<boost::any>& values)
    {
      helper<N-1, T>::getValues(result, values);

      query_result_traits<ElementType>::getValues(boost::get<N>(result),
						  values);
    }

    static void setValue(T& result, int& index, const boost::any& value)
    {
      if (index >= 0) {
	helper<N-1, T>::setValue(result, index, value);

	if (index >= 0) {
	  query_result_traits<ElementType>::setValue(boost::get<N>(result),
						     index, value);
	}
      }
    }

    static void create(T& result)
    {
      helper<N-1, T>::create(result);

      boost::get<N>(result) = query_result_traits<ElementType>::create();
    }

    static void add(Session& session, T& result)
    {
      helper<N-1, T>::add(session, result);

      query_result_traits<ElementType>::add(session, boost::get<N>(result));
    }

    static void remove(T& result)
    {
      helper<N-1, T>::remove(result);

      query_result_traits<ElementType>::remove(boost::get<N>(result));
    }
  };

  template <class T>
  struct helper<-1, T>
  {
    static void getFields(Session& session,
			  std::vector<std::string> *aliases,
			  std::vector<FieldInfo>& result)
    { }

    static void load(Session& session, SqlStatement& statement,
		     int& column, T& result)
    { }

    static void getValues(const T& result, std::vector<boost::any>& values)
    { }

    static void setValue(T& result, int& index, const boost::any& value)
    { }

    static void create(T& result)
    { }

    static void add(Session& session, T& result)
    { }

    static void remove(T& result)
    { }
  };

}

/*! \brief A utility class for defining a tuple of database objects.
 *
 * Since C++ (at least prior to C++0x) does not support template
 * typedefs, this class provides a nested \p type that is a typedef
 * for a Boost.Tuple containing one or more Wt::Dbo::ptr.
 *
 * Thus:
 * \code
 * namespace dbo = Wt::Dbo;
 * typedef dbo::ptr_tuple<A, B, C>::type ABC;
 * \endcode
 * Is equivalent to:
 * \code
 * namespace dbo = Wt::Dbo;
 * typedef boost::tuple<dbo::ptr<A>, dbo::ptr<B>, dbo::ptr<C> > ABC;
 * \endcode
 *
 * \note Boost.Tuple (in its general form) is supported as a result for a
 *       Session::query() by a partial template specialization of
 *       query_result_traits.
 *
 * \ingroup dbo
 */
#ifdef DOXYGEN_ONLY
template <class T0 = boost::tuples::null_type,
	  class T1 = boost::tuples::null_type,
	  ...,
	  class T9 = boost::tuples::null_type>
#else
template <class T0 = boost::tuples::null_type,
	  class T1 = boost::tuples::null_type,
	  class T2 = boost::tuples::null_type,
	  class T3 = boost::tuples::null_type,
	  class T4 = boost::tuples::null_type,
	  class T5 = boost::tuples::null_type,
	  class T6 = boost::tuples::null_type,
	  class T7 = boost::tuples::null_type,
	  class T8 = boost::tuples::null_type,
	  class T9 = boost::tuples::null_type>
#endif
struct ptr_tuple
{
  /*! \brief A typedef for a Boost.Tuple for ptrs.
   */
#ifdef DOXYGEN_ONLY
  typedef boost::tuple< ptr<T0>, ptr<T1>, ..., ptr<T9> > type;
#else
  typedef boost::tuple< typename Impl::ptr_type<T0>::type,
			typename Impl::ptr_type<T1>::type,
			typename Impl::ptr_type<T2>::type,
			typename Impl::ptr_type<T3>::type,
			typename Impl::ptr_type<T4>::type,
			typename Impl::ptr_type<T5>::type,
			typename Impl::ptr_type<T6>::type,
			typename Impl::ptr_type<T7>::type,
			typename Impl::ptr_type<T8>::type,
			typename Impl::ptr_type<T9>::type > type;
#endif // DOXYGEN_ONLY
};

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
struct query_result_traits< boost::tuple<T0, T1, T2, T3, T4,
				       T5, T6, T7, T8, T9> >
{
  typedef boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> tuple_type;
  typedef Impl::helper<boost::tuples::length<tuple_type>::value - 1, tuple_type>
    helper;

  static void getFields(Session& session,
			std::vector<std::string> *aliases,
			std::vector<FieldInfo>& result);

  static tuple_type load(Session& session, SqlStatement& statement,
			 int& column);

  static void getValues(const tuple_type& t, std::vector<boost::any>& values);

  static void setValue(tuple_type& t, int& index, const boost::any& value);

  static tuple_type create();
  static void add(Session& session, tuple_type& t);
  static void remove(tuple_type& t);

  static long long id(const tuple_type& ptr);
  static tuple_type findById(Session& session, long long id);
};

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
void query_result_traits< boost::tuple<T0, T1, T2, T3, T4,
				     T5, T6, T7, T8, T9> >
::getFields(Session& session,
	    std::vector<std::string> *aliases,
	    std::vector<FieldInfo>& result)
{
  helper::getFields(session, aliases, result);
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::load(Session& session, SqlStatement& statement, int& column)
{
  tuple_type result;

  helper::load(session, statement, column, result);

  return result;
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
void 
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::getValues(const tuple_type& t, std::vector<boost::any>& values)
{
  helper::getValues(t, values);
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
void 
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::setValue(tuple_type& t, int& index, const boost::any& value)
{
  helper::setValue(t, index, value);
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::create()
{
  tuple_type result;

  helper::create(result);

  return result;
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
void 
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::add(Session& session, tuple_type& t)
{
  helper::add(session, t);
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
void 
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::remove(tuple_type& t)
{
  helper::remove(t);
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
long long 
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::id(const tuple_type& t)
{
  return -1;
}

template <class T0, class T1, class T2, class T3, class T4,
	  class T5, class T6, class T7, class T8, class T9>
boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>
query_result_traits< boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
::findById(Session& session, long long id)
{
  return tuple_type();
}

  }
}

#endif // WT_DBO_DBO_PTR_H_
