#pragma once

#include <Windows.h>

#include <type_traits>
#include <string>
#include <locale>
#include <codecvt>
#include <cstring>
#include <stdexcept>
#include <cstddef>

#include "libipc/utility/concept.h"
#include "libipc/memory/resource.h"
#include "libipc/platform/detail.h"

namespace ipc {
namespace detail {

struct has_value_type_ {
    template <typename T> static std::true_type  check(typename T::value_type *);
    template <typename T> static std::false_type check(...);
};

template <typename T, typename U, typename = decltype(has_value_type_::check<U>(nullptr))>
struct is_same_char : std::is_same<T, U> {};

template <typename T, typename U>
struct is_same_char<T, U, std::true_type> : std::is_same<T, typename U::value_type> {};

template <typename T, typename S, typename R = S>
using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;

////////////////////////////////////////////////////////////////
/// to_tchar implementation
////////////////////////////////////////////////////////////////

template <typename T = TCHAR>
constexpr auto to_tchar(ipc::string &&str) -> IsSameChar<T, ipc::string, ipc::string &&> {
    return std::move(str); // noconv
}

/**
 * \remarks codecvt_utf8_utf16/std::wstring_convert is deprecated
 * \see https://codingtidbit.com/2020/02/09/c17-codecvt_utf8-is-deprecated/
 *      https://stackoverflow.com/questions/42946335/deprecated-header-codecvt-replacement
 *      https://en.cppreference.com/w/cpp/locale/codecvt/in
 *      https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
*/
template <typename T = TCHAR>
auto to_tchar(ipc::string &&external) -> IsSameChar<T, ipc::wstring> {
    if (external.empty()) {
        return {}; // noconv
    }
    /**
     * CP_ACP       : The system default Windows ANSI code page.
     * CP_MACCP     : The current system Macintosh code page.
     * CP_OEMCP     : The current system OEM code page.
     * CP_SYMBOL    : Symbol code page (42).
     * CP_THREAD_ACP: The Windows ANSI code page for the current thread.
     * CP_UTF7      : UTF-7. Use this value only when forced by a 7-bit transport mechanism. Use of UTF-8 is preferred.
     * CP_UTF8      : UTF-8.
    */
    int size_needed = ::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), NULL, 0);
    if (size_needed <= 0) {
        return {};
    }
    ipc::wstring internal(size_needed, L'\0');
    ::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), &internal[0], size_needed);
    return internal;
}

} // namespace detail
} // namespace ipc