/*=============================================================================
  Copyright (c) 2011-2017 Bolero MURAKAMI
  https://github.com/bolero-MURAKAMI/Sprout

  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)
=============================================================================*/
#ifndef SPROUT_STATEFUL_COUNTER_HPP
#define SPROUT_STATEFUL_COUNTER_HPP

#include <sprout/config.hpp>
#include <sprout/type_traits/integral_constant.hpp>

namespace sprout {
#ifndef SPROUT_CONFIG_DISABLE_CONSTEXPR

	namespace counter_detail {
#if defined(__GNUC__) && !defined(__clang__)
#	pragma GCC diagnostic push
#	pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
#if defined(__clang__)
#	pragma clang diagnostic push
#	pragma clang diagnostic ignored "-Wundefined-inline"
#endif
		template<int N>
		struct tag {
			friend SPROUT_CONSTEXPR int adl_counter(sprout::counter_detail::tag<N>);
		};
#if defined(__clang__)
#	pragma clang diagnostic pop
#endif
#if defined(__GNUC__) && !defined(__clang__)
#	pragma GCC diagnostic pop
#endif

		template<int N>
		struct state
			: public sprout::integral_constant<int, N>
		{
			friend SPROUT_CONSTEXPR int adl_counter(sprout::counter_detail::tag<N>) {
				return N;
			}
		};

		template<int N, int = adl_counter(sprout::counter_detail::tag<N>())>
		inline SPROUT_CONSTEXPR bool check(int, sprout::counter_detail::tag<N>) {
			return true;
		}
		template<int N>
		inline SPROUT_CONSTEXPR bool check(long, sprout::counter_detail::tag<N>) {
			return false;
		}
		template<int N>
		inline SPROUT_CONSTEXPR bool check(bool R = sprout::counter_detail::check(0, sprout::counter_detail::tag<N>())) {
			return R;
		}

		template<int N>
		inline SPROUT_CONSTEXPR int counter(sprout::false_type, sprout::counter_detail::tag<N>) {
			return 0;
		}
		template<int N>
		inline SPROUT_CONSTEXPR int counter(
			sprout::true_type, sprout::counter_detail::tag<N>,
			int R = !sprout::counter_detail::check<N>() ? N
				: counter(sprout::bool_constant<sprout::counter_detail::check<N>()>(), sprout::counter_detail::tag<N + 1>())
			)
		{
			return R;
		}
		template<int N = 0>
		inline SPROUT_CONSTEXPR int counter(int R = sprout::counter_detail::counter(sprout::true_type(), sprout::counter_detail::tag<N>())) {
			return R;
		}
	}	// namespace counter_detail
	//
	// counter
	//
	template<
		int N = 1,
		int R = sprout::counter_detail::state<
			sprout::counter_detail::counter() + N - 1
			>::value
	>
	inline SPROUT_CONSTEXPR int counter() {
		return R;
	}

#endif
}	// namespace sprout

#endif	// #ifndef SPROUT_STATEFUL_COUNTER_HPP
