/*
 * A test for sending delayed mutable message to mpmc mbox.
 */

#include <so_5/all.hpp>

#include <test/3rd_party/various_helpers/time_limited_execution.hpp>
#include <test/3rd_party/various_helpers/ensure.hpp>

#include <test/3rd_party/utest_helper/helper.hpp>

using namespace std;

class sobj_message_tester final : public so_5::agent_t
{
	struct first final : public so_5::message_t {};

public :
	sobj_message_tester(context_t ctx)
		:	so_5::agent_t(std::move(ctx))
	{
	}

	virtual void
	so_evt_start() override
	{
		try
		{
			m_timer = so_5::send_periodic< so_5::mutable_msg<first> >(
					*this,
					std::chrono::milliseconds(50),
					std::chrono::milliseconds(25) );

			ensure( false,
					"an exception must be thrown before this point (sobj)" );
		}
		catch( const so_5::exception_t & x )
		{
			ensure( so_5::rc_mutable_msg_cannot_be_periodic ==
					x.error_code(),
					"an rc_mutable_msg_cannot_be_periodic expected" );
		}

		so_deregister_agent_coop_normally();
	}

private :
	so_5::timer_id_t m_timer;
};

class user_message_tester final : public so_5::agent_t
{
	struct first final {};

public :
	user_message_tester(context_t ctx)
		:	so_5::agent_t(std::move(ctx))
	{
	}

	virtual void
	so_evt_start() override
	{
		try
		{
			m_timer = so_5::send_periodic< so_5::mutable_msg<first> >(
					*this,
					std::chrono::milliseconds(50),
					std::chrono::milliseconds(25) );

			ensure( false,
					"an exception must be thrown before this point (user)" );
		}
		catch( const so_5::exception_t & x )
		{
			ensure( so_5::rc_mutable_msg_cannot_be_periodic ==
					x.error_code(),
					"an rc_mutable_msg_cannot_be_periodic expected" );
		}

		so_deregister_agent_coop_normally();
	}

private :
	so_5::timer_id_t m_timer;
};

int
main()
{
	try
	{
		run_with_time_limit(
			[]() {
				so_5::launch(
					[&](so_5::environment_t & env) {
						env.register_agent_as_coop(
								env.make_agent<sobj_message_tester>());

						env.register_agent_as_coop(
								env.make_agent<user_message_tester>());
					},
					[](so_5::environment_params_t & params) {
						(void)params;
#if 0
						params.message_delivery_tracer( so_5::msg_tracing::std_cout_tracer() );
#endif
					} );
			},
			5,
			"simple agent");
	}
	catch(const exception & ex)
	{
		cerr << "Error: " << ex.what() << endl;
		return 1;
	}

	return 0;
}

