#pragma once

#include <psemek/react/value.hpp>

namespace psemek::react
{

	template <typename T>
	value<T> join(value<value<T>> x)
	{
		auto node = std::make_shared<detail::node<T>>();
		auto weak_node = std::weak_ptr{node};

		auto internal_subscriber = [weak_node]{
			if (auto node = weak_node.lock())
			{
				node->cached_value = std::nullopt;
				node->internal_signal();
			}
		};

		auto inner_external_subscriber = [weak_node](T const &){
			if (auto node = weak_node.lock())
				if (!node->cached_value)
					node->external_signal(node->value());
		};

		auto inner_internal_token = std::make_shared<util::signal<>::subscription_token>();
		auto inner_external_token = std::make_shared<typename util::signal<T>::subscription_token>();

		auto external_subscriber = [weak_node, inner_internal_token, inner_external_token, internal_subscriber, inner_external_subscriber](value<T> const & x){
			if (auto node = weak_node.lock())
			{
				if (!node->cached_value)
				{
					*inner_internal_token = x.subscribe(detail::internal_tag{}, internal_subscriber);
					*inner_external_token = x.subscribe(inner_external_subscriber);
					node->external_signal(node->value());
				}
			}
		};

		auto internal_token = x.subscribe(detail::internal_tag{}, internal_subscriber);
		auto external_token = x.subscribe(external_subscriber);

		node->getter = [
			inner_internal_token,
			inner_external_token,
			internal_token,
			external_token,
			x
		]{
			return **x;
		};

		return value<T>(detail::internal_tag{}, std::move(node));
	}

}
