#include <psemek/io/synchronized.hpp>

namespace psemek::io
{

	namespace
	{

		struct synchronized_stream_impl
			: ostream
		{
			synchronized_stream_impl(std::shared_ptr<ostream> stream, std::vector<std::weak_ptr<ostream>> others)
				: stream_(std::move(stream))
				, others_(std::move(others))
			{}

			std::size_t write(char const * p, std::size_t size) override
			{
				for (auto const & ptr : others_)
					if (auto stream = ptr.lock())
						stream->flush();
				return stream_->write(p, size);
			}

			void flush() override
			{
				stream_->flush();
			}

		private:
			std::shared_ptr<ostream> stream_;
			std::vector<std::weak_ptr<ostream>> others_;
		};

	}

	// Convert a set of output streams to a set of mutually
	// synchronized streams: writing to any of the streams
	// flushes all the other streams
	std::vector<std::unique_ptr<ostream>> synchronized(std::vector<std::unique_ptr<ostream>> streams)
	{
		std::vector<std::shared_ptr<ostream>> shared_streams(streams.size());
		for (std::size_t i = 0; i < streams.size(); ++i)
			shared_streams[i] = std::move(streams[i]);

		std::vector<std::unique_ptr<ostream>> result;
		for (std::size_t i = 0; i < streams.size(); ++i)
		{
			std::vector<std::weak_ptr<ostream>> others;
			for (std::size_t j = 0; j < streams.size(); ++j)
				if (i != j)
					others.push_back(shared_streams[i]);

			result.push_back(std::make_unique<synchronized_stream_impl>(shared_streams[i], std::move(others)));
		}

		return result;
	}

}
