//
// MessagePack for C++ static resolution routine
//
// Copyright (C) 2008 FURUHASHI Sadayuki
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//
#ifndef MSGPACK_TYPE_TUPLE_HPP__
#define MSGPACK_TYPE_TUPLE_HPP__

#include "msgpack/object.hpp"

namespace msgpack {
namespace type {


// FIXME operator==
// FIXME operator!=

<% GENERATION_LIMIT = 15 %>

template < typename A0 <%1.upto(GENERATION_LIMIT+1) {|i|%>, typename A<%=i%> = void<%}%> >
struct tuple;

template <typename Tuple, int N>
struct tuple_type;

template <typename Tuple, int N>
struct const_tuple_type;

template <typename T>
struct tuple_element {
	typedef T type;
	tuple_element(T& x) : _x(x) {}
	type& get() { return _x; }
	const type& get() const { return _x; }
private:
	type& _x;
};

template <typename T>
struct const_tuple_element {
	typedef T type;
	const_tuple_element(const T& x) : _x(x) {}
	const type& get() const { return _x; }
private:
	const type& _x;
};


<%0.upto(GENERATION_LIMIT) {|i|%>
<%0.upto(i) {|j|%>
template < typename A0 <%1.upto(i) {|k|%>, typename A<%=k%> <%}%>>
struct tuple_type<tuple<A0 <%1.upto(i) {|k|%>, A<%=k%> <%}%>>, <%=j%>> : tuple_element<A<%=j%>> {
	tuple_type(tuple<A0 <%1.upto(i) {|k|%>, A<%=k%> <%}%>>& x) : tuple_element<A<%=j%>>(x.a<%=j%>) {}
};
<%}%>
<%}%>


<%0.upto(GENERATION_LIMIT) {|i|%>
<%0.upto(i) {|j|%>
template < typename A0 <%1.upto(i) {|k|%>, typename A<%=k%> <%}%>>
struct const_tuple_type<tuple<A0 <%1.upto(i) {|k|%>, A<%=k%> <%}%>>, <%=j%>> : const_tuple_element<A<%=j%>> {
	const_tuple_type(const tuple<A0 <%1.upto(i) {|k|%>, A<%=k%> <%}%>>& x) : const_tuple_element<A<%=j%>>(x.a<%=j%>) {}
};
<%}%>
<%}%>


<%0.upto(GENERATION_LIMIT) {|i|%>
template < typename A0 <%1.upto(i) {|j|%>, typename A<%=j%> <%}%>>
tuple< A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>> make_tuple(const A0& a0 <%1.upto(i) {|j|%>, const A<%=j%>& a<%=j%><%}%>)
{
	return tuple< A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>(a0 <%1.upto(i) {|j|%>, a<%=j%><%}%>);
}
<%}%>


<%0.upto(GENERATION_LIMIT) {|i|%>
template < typename A0 <%1.upto(i) {|j|%>, typename A<%=j%> <%}%>>
struct tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>> {
	tuple() {}
	tuple(const A0& _a0 <%1.upto(i) {|j|%>, const A<%=j%>& _a<%=j%><%}%>) :
		a0(_a0) <%1.upto(i) {|j|%>, a<%=j%>(_a<%=j%>)<%}%> {}
	tuple(object o) { convert(*this, o); }
	template <int N> typename tuple_type<tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>, N>::type& get()
		{ return tuple_type<tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>, N>(*this).get(); }
	template <int N> const typename const_tuple_type<tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>, N>::type& get() const
		{ return const_tuple_type<tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>, N>(*this).get(); }
	<%0.upto(i) {|j|%>
	A<%=j%> a<%=j%>;<%}%>
};
<%}%>


<%0.upto(GENERATION_LIMIT) {|i|%>
template < typename A0 <%1.upto(i) {|j|%>, typename A<%=j%> <%}%>>
tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>& operator<< (
		tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>& v,
		object o) {
	if(o.type != ARRAY) { throw type_error(); }
	if(o.via.container.size < <%=i+1%>) { throw type_error(); }
	<%0.upto(i) {|j|%>
	convert<A<%=j%>>(v.template get<<%=j%>>(), o.via.container.ptr[<%=j%>]);<%}%>
	return v;
}
<%}%>


// FIXME
/*
template <typename A0, typename A1 = void, typename A2 = void>
struct tuple_just;

template <typename A0>
struct tuple_just<A0> {
	A0 a0;
	static inline void convert(object o, tuple_just<A0>& v)
	{
		if(o.type != ARRAY) { throw type_error(); }
		if(o.v.container.size != 1) { throw type_error(); }
		msgpack::convert<A0>(o.v.container.ptr[0], v.a0);
	}
};

template <typename A0, typename A1>
struct tuple_just<A0, A1> {
	A0 a0;
	A1 a1;
	static inline void convert(object o, tuple_just<A0, A1>& v)
	{
		if(o.type != ARRAY) { throw type_error(); }
		if(o.v.container.size != 2) { throw type_error(); }
		msgpack::convert<A0>(o.v.container.ptr[0], v.a0);
		msgpack::convert<A1>(o.v.container.ptr[1], v.a1);
	}
};
*/



<%0.upto(GENERATION_LIMIT) {|i|%>
template < typename Stream , typename A0 <%1.upto(i) {|j|%>, typename A<%=j%> <%}%>>
const tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>& operator>> (
		const tuple<A0 <%1.upto(i) {|j|%>, A<%=j%> <%}%>>& v,
		packer<Stream> o) {
	o.pack_array(<%=i+1%>);
	<%0.upto(i) {|j|%>
	pack(v.template get<<%=j%>>(), o);<%}%>
	return v;
}
<%}%>



}  // namespace type
}  // namespace msgpack

#endif /* msgpack/type/tuple.hpp */