#ifndef TILE_BASE_MAKE_UNIQUE_H
#define TILE_BASE_MAKE_UNIQUE_H

#pragma once

#include "tile/base/internal/logging.h"
#include "tile/base/internal/meta.h"

#include <memory>

namespace tile {

// single elem
template<typename T, typename... Args>
inline enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
make_unique(Args &&...args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template<typename T, typename... Args>
inline enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
make_unique(T *ptr)
{
    return std::unique_ptr<T>(ptr);
}

// dynamic array
template<typename T>
inline enable_if_t<std::is_array<T>::value && std::extent<T>::value == 0, std::unique_ptr<T>>
make_unique(std::size_t size)
{
    TILE_DCHECK(size > 0);
    using U = typename std::remove_extent<T>::type;
    return std::unique_ptr<T>(new U[size]());
}

// int a[100];
// char b[100];
template<typename T, typename... Args>
enable_if_t<std::is_array<T>::value && std::extent<T>::value != 0, std::unique_ptr<T>> make_unique(Args &&...) = delete;

// == With Deleter

template<typename T, typename D, typename... Args>
inline enable_if_t<!std::is_array<T>::value, std::unique_ptr<T, D>>
make_unique_with_deleter(D &&deleter, Args &&...args)
{
    return std::unique_ptr<T, D>(new T(std::forward<Args>(args)...), std::forward<D>(deleter));
}

template<typename T, typename D>
inline enable_if_t<std::is_array<T>::value && std::extent<T>::value == 0, std::unique_ptr<T, D>>
make_unique_with_deleter(D &&deleter, std::size_t size)
{
    TILE_DCHECK(size > 0);
    using U = typename std::remove_extent<T>::type;
    return std::unique_ptr<T, D>(new U[size](), std::forward<D>(deleter));
}

template<typename T, typename D, typename... Args>
enable_if_t<std::is_array<T>::value && std::extent<T>::value != 0, std::unique_ptr<T, D>>
make_unique_with_deleter(D &&deleter, Args &&...) = delete;

}// namespace tile

#endif// TILE_BASE_MAKE_UNIQUE_H
