/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#include "csmString.hpp"
#include <stdarg.h>
#include "CubismFramework.hpp"
#include "Utils/CubismDebug.hpp"

//--------- LIVE2D NAMESPACE ------------
namespace Live2D { namespace Cubism { namespace Framework {
csmInt32 csmString::s_totalInstanceNo = 0;

namespace {
const csmChar* s_emptyString = "";

csmChar* GetEmptyString()
{
    return const_cast<csmChar*>(s_emptyString);
}
}

csmString::csmString()
    : _ptr(NULL)
    , _length(0)
    , _hashcode(-1)
{
    this->_small[0] = '\0';
    _hashcode = CalcHashcode(WritePointer(), this->_length);
    _instanceNo = s_totalInstanceNo++;
}

csmString::csmString(const csmChar* c)
{
    const csmInt32 count = static_cast<csmInt32>(strlen(c));

    if (count)
    {
        Copy(c, count);
        this->_hashcode = CalcHashcode(WritePointer(), _length);
    }
    else
    {
        SetEmpty();
    }

    _instanceNo = s_totalInstanceNo++;
}

csmString::csmString(const csmString& s)
{
    if (!s.IsEmpty())
    {
        Copy(s.GetRawString(), s._length);
        this->_hashcode = s._hashcode;
    }
    else
    {
        SetEmpty();
    }

    _instanceNo = s_totalInstanceNo++;
}

csmString::csmString(const csmChar* s, csmInt32 length)
{
    if (length)
    {
        Copy(s, length);
        this->_hashcode = CalcHashcode(WritePointer(), _length);
    }
    else
    {
        SetEmpty();
    }

    _instanceNo = s_totalInstanceNo++;
}

csmString::csmString(const csmChar* c, csmInt32 length, csmBool useptr)
{
    Initialize(c, length, useptr);
    _instanceNo = s_totalInstanceNo++;
}

void csmString::Initialize(const csmChar* c, csmInt32 length, csmBool usePtr)
{
    if (!length)
    {
        SetEmpty();

        return;
    }

    if (!usePtr)
    {
        Copy(c, length);
    }
    else
    {
        this->_ptr = const_cast<csmChar*>(c);
        this->_length = length;
        this->_ptr[length] = 0x0;
    }

    this->_hashcode = CalcHashcode(this->GetRawString(), this->_length);
}

csmString::~csmString()
{
    if (IsEmpty())
    {
        return;
    }

    if (this->_ptr)
    {
        CSM_FREE(this->_ptr);
    }
}

void csmString::Clear()
{
    if (IsEmpty())
    {
        return;
    }

    CSM_FREE(this->_ptr);

    SetEmpty();
}

csmString& csmString::operator=(const csmChar* c)
{
    Clear(); //現在のポインタを開放してから処理する

    Copy(c, static_cast<csmInt32>(strlen(c)));
    this->_hashcode = CalcHashcode(this->GetRawString(), this->_length);
    return *this;
}

csmString& csmString::operator=(const csmString& s)
{
    Clear(); //現在のポインタを開放してから処理する

    Copy(s.GetRawString(), s._length);
    this->_hashcode = s._hashcode;
    return *this;
}

csmString csmString::operator+(const csmString& s) const
{
    csmSizeType len1 = static_cast<csmSizeType>(this->_length);
    csmSizeType len2 = static_cast<csmSizeType>(s._length);

    if (!len1 && !len2)
    {
        csmString ret;
        return ret;
    }

    if (len1 + len2 < SmallLength - 1)
    {
        csmChar buffer[SmallLength];
        csmChar* newptr = buffer;

        //
        memcpy(newptr, GetRawString(), sizeof(csmChar) * len1); //nullを含めない
        memcpy(&newptr[len1], s.GetRawString(), sizeof(csmChar) * (s._length + 1)); //nullを含めて+1

        csmString ret(newptr, static_cast<csmInt32>(len1 + len2), false);
        return ret;
    }
    else
    {
        //メモリ管理の対象外とする（寿命が把握出来ない）
        csmChar* newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (len1 + len2 + 1)));

        if (newptr == NULL)
        {
            csmString ret;
            return ret;
        }

        //
        memcpy(newptr, GetRawString(), sizeof(csmChar) * len1); //nullを含めない
        memcpy(&newptr[len1], s.GetRawString(), sizeof(csmChar) * (s._length + 1)); //nullを含めて+1

        csmString ret(newptr, static_cast<csmInt32>(len1 + len2), true);
        return ret;
    }
}

csmString csmString::operator+(const csmChar* c) const
{
    csmSizeType len1 = static_cast<csmSizeType>(this->_length);
    csmSizeType len2 = strlen(c);

    if (len1 + len2 < SmallLength - 1)
    {
        csmChar buffer[SmallLength];
        csmChar* newptr = buffer;

        //
        memcpy(newptr, this->GetRawString(), sizeof(csmChar) * len1); //nullを含めない
        memcpy(&newptr[len1], c, sizeof(csmChar) * (len2 + 1)); //nullを含めて+1

        csmString ret(newptr, static_cast<csmInt32>(len1 + len2), false);
        return ret;
    }
    else
    {
        //メモリ管理の対象外とする（寿命が把握出来ない）
        csmChar* newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (len1 + len2 + 1)));

        if (newptr == NULL)
        {
            csmString ret;
            return ret;
        }

        memcpy(newptr, this->GetRawString(), sizeof(csmChar) * len1); //nullを含めない
        memcpy(&newptr[len1], c, sizeof(csmChar) * (len2 + 1)); //nullを含めて+1

        csmString ret(newptr, static_cast<csmInt32>(len1 + len2), true);
        return ret;
    }
}

csmString& csmString::operator+=(const csmString& s)
{
    csmChar* newptr = NULL;
    csmInt32 len1 = this->_length;

    if (this->_length + s._length < SmallLength - 1)
    {
        csmChar buffer[SmallLength];
        newptr = buffer;

        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], s.GetRawString(), s._length + 1); //nullを含めて+1

        Clear(); // 現在のポインタを開放してから処理する
        Initialize(newptr, len1 + s._length, false);
    }
    else
    {
        newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (this->_length + s._length + 1)));

        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], s.GetRawString(), s._length + 1); //nullを含めて+1

        Clear(); //現在のポインタを開放してから処理する
        Initialize(newptr, len1 + s._length, true);
    }
    return *this;
}

csmString& csmString::operator+=(const csmChar* c)
{
    csmSizeType len1 = static_cast<csmSizeType>(this->_length);
    csmSizeType len2 = strlen(c);

    csmChar* newptr = NULL;

    if (len1 + len2 < SmallLength - 1)
    {
        csmChar buffer[SmallLength];
        newptr = buffer;

        memcpy(newptr, GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], c, len2 + 1); //nullを含めて+1

        Clear(); //現在のポインタを開放してから処理する
        Initialize(newptr, static_cast<csmInt32>(len1 + len2), false);
    }
    else
    {
        newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (len1 + len2 + 1)));

        memcpy(newptr, GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], c, len2 + 1); //nullを含めて+1

        Clear(); //現在のポインタを開放してから処理する
        Initialize(newptr, static_cast<csmInt32>(len1 + len2), true);
    }
    return *this;
}

csmBool csmString::operator==(const csmString& s) const
{
    //サイズ違い
    if (s._length != this->_length) return false;

    //hashcode比較
    if (this->_hashcode != s._hashcode) return false;

    const csmChar* c1 = this->GetRawString();
    const csmChar* c2 = s.GetRawString();

    //文字違い（逆順なのはPARAMの比較の特性）
    for (csmInt32 i = this->_length - 1; i >= 0; --i)
    {
        if (c1[i] != c2[i]) return false;
    }
    return true;
}

csmBool csmString::operator==(const csmChar* rc) const
{
    //サイズ違い
    if (static_cast<csmInt32>(strlen(rc)) != this->_length) return false;

    const csmChar* lc = this->GetRawString();

    //文字違い（逆順なのはPARAMの比較の特性）
    for (csmInt32 i = this->_length - 1; i >= 0; --i)
    {
        if (lc[i] != rc[i]) return false;
    }
    return true;
}

csmBool csmString::operator<(const csmString& s) const
{
    return strcmp(this->GetRawString(), s.GetRawString()) < 0;
}

csmBool csmString::operator<(const csmChar* c) const
{
    return strcmp(this->GetRawString(), c) < 0;
}

csmBool csmString::operator>(const csmString& s) const
{
    return strcmp(this->GetRawString(), s.GetRawString()) > 0;
}

csmBool csmString::operator>(const csmChar* c) const
{
    return strcmp(this->GetRawString(), c) > 0;
}

csmString& csmString::Append(const csmChar* c, csmInt32 len2)
{
    csmInt32 len1 = this->_length;

    csmChar* newptr = NULL;

    if (len1 + len2 < SmallLength - 1)
    {
        csmChar buffer[SmallLength];
        newptr = buffer;

        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], c, len2); //nullを含めて+1

        Clear(); // 現在のポインタを開放してから処理する
        Initialize(newptr, len1 + len2, false);
    }
    else
    {
        newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (len1 + len2 + 1)));

        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        memcpy(&newptr[len1], c, len2); //nullを含めて+1

        Clear(); //現在のポインタを開放してから処理する
        Initialize(newptr, len1 + len2, true);
    }
    return *this;
}

csmString& csmString::Append(csmInt32 len2, const csmChar c)
{
    csmInt32 len1 = this->_length;

    csmChar* newptr = NULL;

    if(len1 + len2< SmallLength -1)
    {
        csmChar buffer[SmallLength];
        newptr = buffer;
        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        for (csmInt32 i = len1 + len2 - 1; i >= len1; --i) newptr[i] = c;

        Clear(); // 現在のポインタを開放してから処理する
        Initialize(newptr, len1 + len2, false);
    }
    else
    {
        // 必要な長さ∔NULL分1バイトを新規確保
        newptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (len1 + len2 + 1)));
        // 元々の分をコピー
        memcpy(newptr, this->GetRawString(), len1); //nullを含めない
        // 新しい箇所をコピー
        for (csmInt32 i = len1 + len2 - 1; i >= len1; --i) newptr[i] = c;

        Clear(); //現在のポインタを開放してから処理する
        Initialize(newptr, len1 + len2, true);
    }
    return *this;
}

void csmString::Copy(const csmChar* c, csmInt32 length)
{
    if (!length)
    {
        return;
    }

    this->_length = length;

    if (this->_length < SmallLength -1)
    {
        this->_ptr = NULL;

        memcpy(this->_small, c, length);
        this->_small[length] = 0x0;
    }
    else
    {
        this->_ptr = static_cast<csmChar*>(CSM_MALLOC(sizeof(csmChar) * (length + 1)));

        memcpy(this->_ptr, c, length);
        this->_ptr[length] = 0x0;
    }
}

csmInt32 csmString::CalcHashcode(const csmChar* c, csmInt32 length)
{
    csmInt32 hash = 0;
    for (csmInt32 i = length; i >= 0; --i)
    {
        hash = hash * 31 + c[i];
    }
    if ((hash == -1) || (c == GetEmptyString()))
    {
        hash = -2; //-1だけ特別な意味をもたせる
    }
    return hash;
}

const csmChar* csmString::GetRawString() const
{
    if (this->_length < SmallLength -1)
    {
        return &_small[0];
    }
    else
    {
        return _ptr;
    }
}

csmInt32 csmString::GetHashcode()
{
    if (_hashcode == -1) _hashcode = CalcHashcode(WritePointer(), this->_length);
    return _hashcode;
}

csmBool csmString::IsEmpty() const
{
#ifdef CSM_DEBUG

    csmBool isEmpty = (_ptr == NULL && _small[0]=='\0');

    if (isEmpty)
    {
        CSM_ASSERT(_length == 0);
    }

    return isEmpty;

#else

    //return (_ptr == GetEmptyString());

    return (_ptr == NULL && _small[0] == '\0');

#endif
}

void csmString::SetEmpty()
{
    //_ptr = GetEmptyString();
    _ptr = NULL;
    _small[0] = '\0';
    _length = 0;
    _hashcode = -1;
    _hashcode = CalcHashcode(WritePointer(), this->_length);
}

csmChar* csmString::WritePointer()
{
    if(this->_length< SmallLength -1)
    {
        return _small;
    }
    else
    {
        return _ptr;
    }
}

}}}

//------------------------- LIVE2D NAMESPACE ------------
