/*
 * Copyright (C) 2005 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_STRING16_H
#define ANDROID_STRING16_H

#include <iostream>
#include <string>

#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/TypeHelpers.h>

#if __has_include(<string_view>)
#include <string_view>
#define HAS_STRING_VIEW
#endif

#if __cplusplus >= 202002L
#include <compare>
#endif

// ---------------------------------------------------------------------------

namespace android {

// ---------------------------------------------------------------------------

template <size_t N>
class StaticString16;

// DO NOT USE: please use std::u16string

//! This is a string holding UTF-16 characters.
class String16
{
public:
                                String16();
                                String16(const String16& o);
                                String16(String16&& o) noexcept;
                                String16(const String16& o,
                                         size_t len,
                                         size_t begin=0);
    explicit                    String16(const char16_t* o);
    explicit                    String16(const char16_t* o, size_t len);
    explicit                    String16(const String8& o);
    explicit                    String16(const char* o);
    explicit                    String16(const char* o, size_t len);

                                ~String16();

    inline  const char16_t*     c_str() const;

            size_t              size() const;
    inline  bool                empty() const;

    inline  size_t              length() const;

            void                setTo(const String16& other);
            status_t            setTo(const char16_t* other);
            status_t            setTo(const char16_t* other, size_t len);
            status_t            setTo(const String16& other,
                                      size_t len,
                                      size_t begin=0);

            status_t            append(const String16& other);
            status_t            append(const char16_t* other, size_t len);

    inline  String16&           operator=(const String16& other);
            String16&           operator=(String16&& other) noexcept;

    inline  String16&           operator+=(const String16& other);
    inline  String16            operator+(const String16& other) const;

            status_t            insert(size_t pos, const char16_t* chrs);
            status_t            insert(size_t pos,
                                       const char16_t* chrs, size_t len);

            ssize_t             findFirst(char16_t c) const;
            ssize_t             findLast(char16_t c) const;

            bool                startsWith(const String16& prefix) const;
            bool                startsWith(const char16_t* prefix) const;

            bool                contains(const char16_t* chrs) const;
    inline  bool                contains(const String16& other) const;

            status_t            replaceAll(char16_t replaceThis,
                                           char16_t withThis);

    inline  int                 compare(const String16& other) const;

    inline  bool                operator<(const String16& other) const;
    inline  bool                operator<=(const String16& other) const;
    inline  bool                operator==(const String16& other) const;
    inline  bool                operator!=(const String16& other) const;
    inline  bool                operator>=(const String16& other) const;
    inline  bool                operator>(const String16& other) const;
#if __cplusplus >= 202002L
    inline std::strong_ordering operator<=>(const String16& other) const;
#endif

    inline  bool                operator<(const char16_t* other) const;
    inline  bool                operator<=(const char16_t* other) const;
    inline  bool                operator==(const char16_t* other) const;
    inline  bool                operator!=(const char16_t* other) const;
    inline  bool                operator>=(const char16_t* other) const;
    inline  bool                operator>(const char16_t* other) const;
#if __cplusplus >= 202002L
    inline std::strong_ordering operator<=>(const char16_t* other) const;
#endif

    inline                      operator const char16_t*() const;

#ifdef HAS_STRING_VIEW
    // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
    // lighter and if one needs, they can still create u16string from u16string_view.
    inline                      operator std::u16string_view() const;
#endif

    // Static and non-static String16 behave the same for the users, so
    // this method isn't of much use for the users. It is public for testing.
            bool                isStaticString() const;

  private:
    /*
     * A flag indicating the type of underlying buffer.
     */
    static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;

    /*
     * alloc() returns void* so that SharedBuffer class is not exposed.
     */
    static void* alloc(size_t size);
    static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
    static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);

    /*
     * edit() and editResize() return void* so that SharedBuffer class
     * is not exposed.
     */
    void* edit();
    void* editResize(size_t new_size);

    void acquire();
    void release();

    size_t staticStringSize() const;

    const char16_t* mString;

protected:
    /*
     * Data structure used to allocate static storage for static String16.
     *
     * Note that this data structure and SharedBuffer are used interchangably
     * as the underlying data structure for a String16.  Therefore, the layout
     * of this data structure must match the part in SharedBuffer that is
     * visible to String16.
     */
    template <size_t N>
    struct StaticData {
        // The high bit of 'size' is used as a flag.
        static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
        constexpr StaticData() : size(N - 1), data{0} {}
        const uint32_t size;
        char16_t data[N];

        constexpr StaticData(const StaticData<N>&) = default;
    };

    /*
     * Helper function for constructing a StaticData object.
     */
    template <size_t N>
    static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
        StaticData<N> r;
        // The 'size' field is at the same location where mClientMetadata would
        // be for a SharedBuffer.  We do NOT set kIsSharedBufferAllocated flag
        // here.
        for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
        return r;
    }

    template <size_t N>
    explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}

// These symbols are for potential backward compatibility with prebuilts. To be removed.
#ifdef ENABLE_STRING16_OBSOLETE_METHODS
public:
#else
private:
#endif
    inline  const char16_t*     string() const;
};

// String16 can be trivially moved using memcpy() because moving does not
// require any change to the underlying SharedBuffer contents or reference count.
ANDROID_TRIVIAL_MOVE_TRAIT(String16)

static inline std::ostream& operator<<(std::ostream& os, const String16& str) {
    os << String8(str);
    return os;
}

// ---------------------------------------------------------------------------

/*
 * A StaticString16 object is a specialized String16 object.  Instead of holding
 * the string data in a ref counted SharedBuffer object, it holds data in a
 * buffer within StaticString16 itself.  Note that this buffer is NOT ref
 * counted and is assumed to be available for as long as there is at least a
 * String16 object using it.  Therefore, one must be extra careful to NEVER
 * assign a StaticString16 to a String16 that outlives the StaticString16
 * object.
 *
 * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
 *
 * A StaticString16 SHOULD NEVER APPEAR IN APIs.  USE String16 INSTEAD.
 */
template <size_t N>
class StaticString16 : public String16 {
public:
    constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}

    constexpr StaticString16(const StaticString16<N>& other)
        : String16(mData), mData(other.mData) {}

    constexpr StaticString16(const StaticString16<N>&&) = delete;

    // There is no reason why one would want to 'new' a StaticString16.  Delete
    // it to discourage misuse.
    static void* operator new(std::size_t) = delete;

private:
    const StaticData<N> mData;
};

template <typename F>
StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;

// ---------------------------------------------------------------------------
// No user servicable parts below.

inline int compare_type(const String16& lhs, const String16& rhs)
{
    return lhs.compare(rhs);
}

inline int strictly_order_type(const String16& lhs, const String16& rhs)
{
    return compare_type(lhs, rhs) < 0;
}

inline const char16_t* String16::c_str() const
{
    return mString;
}

inline const char16_t* String16::string() const
{
    return mString;
}

inline bool String16::empty() const
{
    return length() == 0;
}

inline size_t String16::length() const
{
    return size();
}

inline bool String16::contains(const String16& other) const
{
    return contains(other.c_str());
}

inline String16& String16::operator=(const String16& other)
{
    setTo(other);
    return *this;
}

inline String16& String16::operator+=(const String16& other)
{
    append(other);
    return *this;
}

inline String16 String16::operator+(const String16& other) const
{
    String16 tmp(*this);
    tmp += other;
    return tmp;
}

inline int String16::compare(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size());
}

inline bool String16::operator<(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) < 0;
}

inline bool String16::operator<=(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
}

inline bool String16::operator==(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) == 0;
}

inline bool String16::operator!=(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) != 0;
}

inline bool String16::operator>=(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
}

inline bool String16::operator>(const String16& other) const
{
    return strzcmp16(mString, size(), other.mString, other.size()) > 0;
}

#if __cplusplus >= 202002L
inline std::strong_ordering String16::operator<=>(const String16& other) const {
    int result = strzcmp16(mString, size(), other.mString, other.size());
    if (result == 0) {
        return std::strong_ordering::equal;
    } else if (result < 0) {
        return std::strong_ordering::less;
    } else {
        return std::strong_ordering::greater;
    }
}
#endif

inline bool String16::operator<(const char16_t* other) const
{
    return strcmp16(mString, other) < 0;
}

inline bool String16::operator<=(const char16_t* other) const
{
    return strcmp16(mString, other) <= 0;
}

inline bool String16::operator==(const char16_t* other) const
{
    return strcmp16(mString, other) == 0;
}

inline bool String16::operator!=(const char16_t* other) const
{
    return strcmp16(mString, other) != 0;
}

inline bool String16::operator>=(const char16_t* other) const
{
    return strcmp16(mString, other) >= 0;
}

inline bool String16::operator>(const char16_t* other) const
{
    return strcmp16(mString, other) > 0;
}

#if __cplusplus >= 202002L
inline std::strong_ordering String16::operator<=>(const char16_t* other) const {
    int result = strcmp16(mString, other);
    if (result == 0) {
        return std::strong_ordering::equal;
    } else if (result < 0) {
        return std::strong_ordering::less;
    } else {
        return std::strong_ordering::greater;
    }
}
#endif

inline String16::operator const char16_t*() const
{
    return mString;
}

inline String16::operator std::u16string_view() const
{
    return {mString, length()};
}

}  // namespace android

// ---------------------------------------------------------------------------

#undef HAS_STRING_VIEW

#endif // ANDROID_STRING16_H
