#ifndef TILE_BASE_FUTURE_SPLIT_H
#define TILE_BASE_FUTURE_SPLIT_H

#pragma once

#include "tile/base/future/basic.h"
#include "tile/base/future/future.h"

#include <atomic>
#include <memory>

namespace tile {
namespace future {
namespace detail {

template<typename... Ts>
auto
SplitImpl(Future<Ts...> *future) -> enable_if_t<(sizeof...(Ts) > 0), Future<Ts...>>
{
    Promise<Ts...> promise;
    auto rc            = promise.GetFuture();

    auto moved_promise = MakeMoveOnCopy(promise);
    *future            = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable {
        moved_promise->SetBoxed(boxed);

        return std::move(boxed.Get());
    });
    return rc;
}

template<typename... Ts>
auto
SplitImpl(Future<Ts...> *future) -> enable_if_t<(sizeof...(Ts) == 0), Future<Ts...>>
{
    Promise<Ts...> promise;
    auto rc            = promise.GetFuture();

    auto moved_promise = MakeMoveOnCopy(promise);
    *future = std::move(*future).Then([moved_promise](Boxed<Ts...> boxed) mutable { moved_promise->SetBoxed(boxed); });

    return rc;
}
}// namespace detail

template<typename... Ts, typename = enable_if_t<std::is_copy_constructible<Boxed<Ts...>>::value>>
std::pair<Future<Ts...>, Future<Ts...>>
Split(Future<Ts...> *future)
{
    auto new_f = detail::SplitImpl(future);
    return std::make_pair(std::move(new_f), std::move(*future));
}

template<typename... Ts, typename = enable_if_t<std::is_copy_constructible<Boxed<Ts...>>::value>>
std::pair<Future<Ts...>, Future<Ts...>>
Split(Future<Ts...> &&future)
{
    return Split(&future);
}

}// namespace future
}// namespace tile

#endif// TILE_BASE_FUTURE_SPLIT_H
