/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

#pragma once

#include "antlr4-common.h"

#ifdef USE_UTF8_INSTEAD_OF_CODECVT
#include "utf8.h"
#endif

namespace antlrcpp {

// For all conversions utf8 <-> utf32.
// I wouldn't prefer wstring_convert because: according to
// https://en.cppreference.com/w/cpp/locale/wstring_convert,
// wstring_convert is deprecated in C++17.
// utfcpp (https://github.com/nemtrif/utfcpp) is a substitution.
#ifndef USE_UTF8_INSTEAD_OF_CODECVT
// VS 2015 and VS 2017 have different bugs in std::codecvt_utf8<char32_t> (VS 2013 works fine).
#if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER < 2000
typedef std::wstring_convert<std::codecvt_utf8<__int32>, __int32> UTF32Converter;
#else
typedef std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> UTF32Converter;
#endif
#endif

// The conversion functions fails in VS2017, so we explicitly use a workaround.
template<typename T>
inline std::string utf32_to_utf8(T const& data) {
#ifndef USE_UTF8_INSTEAD_OF_CODECVT
    // Don't make the converter static or we have to serialize access to it.
    thread_local UTF32Converter converter;

#if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER < 2000
    const auto p = reinterpret_cast<const int32_t*>(data.data());
    return converter.to_bytes(p, p + data.size());
#else
    return converter.to_bytes(data);
#endif
#else
    std::string narrow;
    utf8::utf32to8(data.begin(), data.end(), std::back_inserter(narrow));
    return narrow;
#endif
}

inline UTF32String utf8_to_utf32(const char* first, const char* last) {
#ifndef USE_UTF8_INSTEAD_OF_CODECVT
    thread_local UTF32Converter converter;

#if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER < 2000
    auto r = converter.from_bytes(first, last);
    i32string s = reinterpret_cast<const int32_t*>(r.data());
    return s;
#else
    std::u32string s = converter.from_bytes(first, last);
    return s;
#endif
#else
    UTF32String wide;
    utf8::utf8to32(first, last, std::back_inserter(wide));
    return wide;
#endif
}

void replaceAll(std::string& str, std::string const& from, std::string const& to);

// string <-> wstring conversion (UTF-16), e.g. for use with Window's wide APIs.
ANTLR4CPP_PUBLIC std::string ws2s(std::wstring const& wstr);
ANTLR4CPP_PUBLIC std::wstring s2ws(std::string const& str);
} // namespace antlrcpp
