/*
 * 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 http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#pragma once
#include <stdio.h>
#include "CubismFramework.hpp"
#include "Type/csmVector.hpp"
#include "Type/csmMap.hpp"
#include "Type/csmString.hpp"

//------------ LIVE2D NAMESPACE ------------
namespace Live2D { namespace Cubism { namespace Framework { namespace Utils {
class Value;
class Error;
class NullValue;

#define CSM_JSON_ERROR_TYPE_MISMATCH            "Error:type mismatch"
#define CSM_JSON_ERROR_INDEX_OUT_OF_BOUNDS      "Error:index out of bounds"


/**
 * @brief  パースしたJSONエレメントの要素の基底クラス
 *
 */
class Value
{
    friend class Array;
    friend class Live2D::Cubism::Framework::CubismFramework;

public:
    static Value* ErrorValue; ///< 一時的な返り値として返すエラー。CubismFramework::Dispose()するまではCSM_DELETEしない。
    static Value* NullValue;  ///< 一時的な返り値として返すNULL。CubismFramework::Dispose()するまではCSM_DELETEしない。

    /**
     * @brief   コンストラクタ
     *
     */
    Value() {}

    /**
     * @brief   デストラクタ
     *
     */
    virtual ~Value() {}

    /**
     * @brief   要素を文字列で返す(csmString型)
     *
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "") = 0;

    /**
     * @brief   要素を文字列で返す(csmChar*)
     *
     */
    virtual const csmChar* GetRawString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        return this->GetString(defaultValue, indent).GetRawString();
    }

    /**
     * @brief   要素を数値型で返す(csmInt32)
     *
     */
    virtual csmInt32 ToInt(csmInt32 defaultValue = 0) { return defaultValue; }

    /**
     * @brief   要素を数値型で返す(csmFloat32)
     *
     */
    virtual csmFloat32 ToFloat(csmFloat32 defaultValue = 0.0f) { return defaultValue; }

    /**
     * @brief   要素を真偽値で返す(csmBool)
     *
     */
    virtual csmBool ToBoolean(csmBool defaultValue = false) { return defaultValue; }

    /**
     * @brief   要素を真偽値で返す(csmBool)
     *
     */
    virtual csmInt32 GetSize() { return 0; }

    /**
     * @brief   要素をコンテナで返す(csmVector<Value*>)
     *
     */
    virtual csmVector<Value*>* GetVector(csmVector<Value*>* defaultValue = NULL) { return defaultValue; }

    /**
     * @brief   要素をマップで返す(csmMap<csmString, Value*>)
     *
     */
    virtual csmMap<csmString, Value*>* GetMap(csmMap<csmString, Value*>* defaultValue = NULL) { return defaultValue; }

    /**
     * @brief   添字演算子[csmInt32]
     *
     */
    virtual Value& operator[](csmInt32 index)
    {
        return *(ErrorValue->SetErrorNotForClientCall(CSM_JSON_ERROR_TYPE_MISMATCH));
    }

    /**
     * @brief   添字演算子[csmString]
     *
     */
    virtual Value& operator[](const csmString& string)
    {
        return *(NullValue->SetErrorNotForClientCall(CSM_JSON_ERROR_TYPE_MISMATCH));
    }

    /**
     * @brief   マップのキー一覧をコンテナで返す
     *
     * @return  マップのキー一覧
     */
    virtual csmVector<csmString>& GetKeys()
    {
        return *s_dummyKeys;
    }

    /**
     *@brief Valueの種類がエラー値ならtrue。
     */
    virtual csmBool IsError() { return false; }

    /**
     *@brief Valueの種類がNULL値ならtrue。
     */
    virtual csmBool IsNull() { return false; }

    /**
     *@brief Valueの種類が真偽値ならtrue。
     */
    virtual csmBool IsBool() { return false; }

    /**
     *@brief Valueの種類が数値型ならtrue。
     */
    virtual csmBool IsFloat() { return false; }

    /**
     *@brief Valueの種類が文字列ならtrue。
     */
    virtual csmBool IsString() { return false; }

    /**
     *@brief Valueの種類が配列ならtrue。
     */
    virtual csmBool IsArray() { return false; }

    /**
     *@brief Valueの種類がマップ型ならtrue。
     */
    virtual csmBool IsMap() { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmString& value) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmChar* value) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmInt32 value) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmFloat32 value) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmBool value) { return false; }

    /**
     *@brief Valueの値が静的ならtrue. 静的なら解放しない
     */
    virtual csmBool IsStatic() { return false; }

    /**
     *@brief Valueにエラー値をセットする
     */
    virtual Value* SetErrorNotForClientCall(const csmChar* errorStr) { return ErrorValue; }

protected:
    csmString _stringBuffer;        ///< 文字列バッファ

private:
    static csmVector<csmString>* s_dummyKeys;    ///< ダミーキー

    /**
    * @brief    初期化用メソッド
    */
    static void StaticInitializeNotForClientCall();

    /**
    * @brief   リリース用メソッド
    */
    static void StaticReleaseNotForClientCall();


};

/**
 * @brief   Ascii文字のみ対応した最小限の軽量JSONパーサ。<br>
 *           仕様はJSONのサブセットとなる。<br>
 *           設定ファイル(model3.json)などのロード用<br>
 *           <br>
 *           [未対応項目]<br>
 *           ・日本語などの非ASCII文字<br>
 *           ・e による指数表現
 */
class CubismJson
{
public:
    /**
     * @brief  バイトデータから直接ロードしてパースする<br>
     *          引数 buffer は外部で管理（破棄）する必要がある
     *
     * @param   buffer  ->  バイトデータのバッファ
     * @param   size    ->  バッファサイズ
     * @return  CubismJsonクラスのインスタンス。失敗したらNULL。
     */
    static CubismJson* Create(const csmByte* buffer, csmSizeInt size);

    /**
    * @brief   パースしたJSONオブジェクトの解放処理
    *
    * @param[in] instance CubismJsonクラスのインスタンス
    *
    */
    static void Delete(CubismJson* instance);

    /**
     * @brief   パースしたJSONのルート要素のポインタを返す
     *
     */
    Value& GetRoot() const;

    /**
     * @brief   パース時のエラー値を返す
     *
     */
    const csmChar* GetParseError() const { return _error; }

    /**
     * @brief   ルート要素の次の要素がファイルの終端だったらtrueを返す
     *
     */
    csmBool CheckEndOfFile() const { return (*_root)[1].Equals("EOF"); }

protected:
    /**
     * @brief JSONのパースを実行する
     *
     * @param[in]   buffer  ->  パース対象のデータバイト
     * @param[in]   size    ->  データバイトのサイズ
     * @retval      true    ->  成功
     * @retval      false   ->  失敗
     */
    csmBool ParseBytes(const csmByte* buffer, csmInt32 size);

    /**
     * @brief   次の「"」までの文字列をパースする。文字列は外部で解放する必要がある。
     *
     * @param[in]   string  ->  パース対象の文字列
     * @param[in]   length  ->  パースする長さ
     * @param[in]   begin   ->  パースを開始する位置
     * @param[out]  outEndPos   ->  パース終了時の位置
     * @return      パースした文字列要素
     */
    csmString ParseString(const csmChar* string, csmInt32 length, csmInt32 begin, csmInt32* outEndPos);


    /**
     * @brief   JSONのオブジェクトエレメントをパースしてValueオブジェクトを返す
     *
     * @param[in]   buffer  ->  JSONエレメントのバッファ
     * @param[in]   length  ->  パースする長さ
     * @param[in]   begin   ->  パースを開始する位置
     * @param[out]  outEndPos   ->  パース終了時の位置
     * @return      パースから取得したValueオブジェクト
     */
    Value* ParseObject(const csmChar* buffer, csmInt32 length, csmInt32 begin, csmInt32* outEndPos);

    /**
     * @brief   JSONの配列エレメントをパースしてValueオブジェクトを返す
     *
     * @param[in]   buffer  ->  JSONエレメントのバッファ
     * @param[in]   length  ->  パースする長さ
     * @param[in]   begin   ->  パースを開始する位置
     * @param[out]  outEndPos   ->  パース終了時の位置
     * @return      パースから取得したValueオブジェクト
     */
    Value* ParseArray(const csmChar* buffer, csmInt32 length, csmInt32 begin, csmInt32* outEndPos);

    /**
     * @brief   JSONエレメントからValue(float,String,Value*,Array,null,true,false)をパースする<br>
     *           エレメントの書式に応じて内部でParseString(), ParseObject(), ParseArray()を呼ぶ<br>
     *
     * @param[in]   buffer  ->  JSONエレメントのバッファ
     * @param[in]   length  ->  パースする長さ
     * @param[in]   begin   ->  パースを開始する位置
     * @param[out]  outEndPos   ->  パース終了時の位置
     * @return      パースから取得したValueオブジェクト
     */
    Value* ParseValue(const csmChar* buffer, csmInt32 length, csmInt32 begin, csmInt32* outEndPos);

private:
    /**
    * @brief   コンストラクタ
    *
    */
    CubismJson();

    /**
    * @brief   引数付きコンストラクタ
    * @param[in]   buffer  ->  JSONエレメントのバッファ
    * @param[in]   length  ->  パースする長さ
    */
    CubismJson(const csmByte* buffer, csmInt32 length);

    /**
    * @brief   デストラクタ
    *
    */
    virtual ~CubismJson();

    const csmChar*  _error;         ///< パース時のエラー
    csmInt32        _lineCount;     ///< エラー報告に用いる行数カウント
    Value*          _root;          ///< パースされたルート要素
};


/**
 *@brief パースしたJSONの要素をDouble値として扱う
 */
class Float : public Value
{
public:
    /**
     * @brief   コンストラクタ
     */
    Float(csmFloat32 v) : Value()
    {
        this->_value = v;
    }

    /**
     * @brief   デストラクタ
     */
    virtual ~Float() {}

    /**
     *@brief Valueの種類が数値型ならtrue。
     */
    virtual csmBool IsFloat() { return true; }

    /**
     * @brief   要素を文字列で返す(csmString型)
     *
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
#if defined(CSM_TARGET_WIN_GL) || defined(_MSC_VER)
        csmChar strbuf[32] = {'\0'};
        _snprintf_s(strbuf, 32, 32, "%f", this->_value);
        _stringBuffer = csmString(strbuf);
        return _stringBuffer;
#else
        // string stream 未対応
        csmChar strbuf[32] = { '\0' };
        snprintf(strbuf, 32, "%f", this->_value);
        _stringBuffer = csmString(strbuf);
        return _stringBuffer;
#endif
    }

    /**
     * @brief   要素を数値型で返す(csmInt32)
     */
    virtual csmInt32 ToInt(csmInt32 defaultValue = 0) { return static_cast<csmInt32>(this->_value); }

    /**
     * @brief   要素を数値型で返す(csmFloat32)
     */
    virtual csmFloat32 ToFloat(csmFloat32 defaultValue = 0) { return this->_value; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmFloat32 v) { return v == this->_value; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmString& v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmChar* v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmInt32 v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmBool v) { return false; }

private:
    csmFloat32 _value;  ///< JSON要素の値
};


/**
 * @brief   パースしたJSONの要素を真偽値として扱う
 *
 */
class Boolean : public Value
{
    friend class Value;

public:
    static Boolean* TrueValue;  ///< true
    static Boolean* FalseValue; ///< false

    /**
     * @brief   デストラクタ
     *
     */
    virtual ~Boolean() {}

    /**
     *@brief Valueの種類が真偽値ならtrue。
     */
    virtual csmBool IsBool() { return true; }

    /**
     * @brief   要素を真偽値で返す(csmBool)
     */
    virtual csmBool ToBoolean(csmBool defaultValue = false) { return _boolValue; }

    /**
     * @brief   要素を文字列で返す(csmString型)
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        _stringBuffer = csmString(_boolValue ? "true" : "false");
        return _stringBuffer;
    }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmBool v) { return v == _boolValue; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmString& v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmChar* v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmInt32 v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmFloat32 v) { return false; }

    /**
     *@brief Valueの値が静的ならtrue. 静的なら解放しない
     */
    virtual csmBool IsStatic() { return true; }

private:
    /**
     * @brief   引数付きコンストラクタ
     */
    Boolean(csmBool v) : Value() { this->_boolValue = v; }
    csmBool _boolValue;     ///< JSON要素の値
};


/**
 * @brief   パースしたJSONの要素を文字列として扱う
 *
 */
class String : public Value
{
public:
    /**
     * @brief   引数付きコンストラクタ
     */
    String(const csmString& s) : Value() { this->_stringBuffer = s; }

    /**
     * @brief   引数付きコンストラクタ
     */
    String(const csmChar* s) : Value() { this->_stringBuffer = s; }

    /**
     * @brief   デストラクタ
     */
    virtual ~String() {}

    /**
     *@brief Valueの種類が文字列ならtrue。
     */
    virtual csmBool IsString() { return true; }

    /**
     * @brief   要素を文字列で返す(csmString型)
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        return _stringBuffer;
    }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmString& v) { return (_stringBuffer == v); }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(const csmChar* v) { return (_stringBuffer == v); }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmInt32 v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmFloat32 v) { return false; }

    /**
     *@brief 引数の値と等しければtrue。
     */
    virtual csmBool Equals(csmBool v) { return false; }
};


/**
 * @brief  JSONパース時のエラー結果。文字列型のように振る舞う
 *
 */
class Error : public String
{
    friend class Value; //
    friend class Array; //
    friend class CubismJson; //

public:
    /**
     *@brief Valueの値が静的ならtrue. 静的なら解放しない
     */
    virtual csmBool IsStatic() { return _isStatic; }

    /**
    * @brief    エラー情報をセットする
    */
    virtual Value* SetErrorNotForClientCall(const csmChar* s)
    {
        this->_stringBuffer = s;
        return this;
    }

protected:
    /**
     * @brief   引数付きコンストラクタ
     */
    Error(const csmString& s, csmBool isStatic) : String(s)
                                                , _isStatic(isStatic) {}

    /**
     * @brief   デストラクタ
     */
    virtual ~Error() {}

    /**
     *@brief Valueの種類がエラー値ならtrue。
     */
    virtual csmBool IsError() { return true; }

    csmBool _isStatic; ///< 静的なValueかどうか
};


/**
 * @brief   パースしたJSONの要素をNull値として持つ
 */
class NullValue : public Value
{
    friend class Value; //
    friend class CubismJson; //

public:
    /**
     * @brief   デストラクタ
     */
    virtual ~NullValue() {}

    /**
     *@brief Valueの種類がNULL値ならtrue。
     */
    virtual csmBool IsNull() { return true; }

    /**
     * @brief   要素を文字列で返す(csmString型)
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        return _stringBuffer;
    }

    /**
     *@brief Valueの値が静的ならtrue. 静的なら解放しない
     */
    virtual csmBool IsStatic() { return true; }

private:
    /**
     * @brief    コンストラクタ
     */
    NullValue() : Value() { _stringBuffer = "NullValue"; }
};


/**
 * @brief   パースしたJSONの要素を配列として持つ
 *
 */
class Array : public Value
{
public:
    /**
     * @brief    コンストラクタ
     */
    Array() : Value()
            , _array() {}

    /**
     * @brief   デストラクタ
     */
    virtual ~Array();

    /**
     *@brief Valueの種類が配列ならtrue。
     */
    virtual csmBool IsArray() { return true; }

    /**
     * @brief   添字演算子[csmInt32]
     *
     */
    virtual Value& operator[](csmInt32 index)
    {
        if (index < 0 || (static_cast<csmInt32>(_array.GetSize()) <= index))
            return *(ErrorValue->SetErrorNotForClientCall(CSM_JSON_ERROR_INDEX_OUT_OF_BOUNDS));
        Value* v = _array[index];

        if (v == NULL) return *Value::NullValue;
        return *v;
    }

    /**
     * @brief   添字演算子[csmString]
     *
     */
    virtual Value& operator[](const csmString& string)
    {
        return *(ErrorValue->SetErrorNotForClientCall(CSM_JSON_ERROR_TYPE_MISMATCH));
    }

    /**
     * @brief   要素を文字列で返す(csmString型)
     *
     */
    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        _stringBuffer = indent + "[\n";
        csmVector<Value*>::iterator ite = _array.Begin();
        for (; ite != _array.End(); ite++)
        {
            Value* v = (*ite);
            _stringBuffer += indent + "	" + v->GetString(indent + "	") + "\n";
        }
        _stringBuffer += indent + "]\n";

        return _stringBuffer;
    }

    /**
     * @brief   配列要素を追加する
     */
    void Add(Value* v) { _array.PushBack(v, false); }

    /**
     * @brief   要素をコンテナで返す(csmVector<Value*>)
     *
     */
    virtual csmVector<Value*>* GetVector(csmVector<Value*>* defaultValue = NULL) { return &_array; }

    /**
     * @brief   要素の数を返す
     *
     */
    virtual csmInt32 GetSize() { return static_cast<csmInt32>(_array.GetSize()); }

private:
    csmVector<Value*> _array; ///< JSON要素の値
};


/**
 * @brief   パースしたJSONの要素をマップとして持つ
 *
 */
class Map : public Value
{
public:
    /**
     * @brief    コンストラクタ
     */
    Map() : Value()
          , _keys(NULL) {}

    /**
     * @brief    デストラクタ
     */
    virtual ~Map();

    /**
     * @brief    Valueの値がMap型ならtrue
     */
    virtual csmBool IsMap() { return true; }

    /**
     * @brief    添字演算子[csmString]
     */
    virtual Value& operator[](const csmString& s)
    {
        Value* ret = _map[s];
        if (ret == NULL)
        {
            return *Value::NullValue;
        }
        return *ret;
    }

    /**
     * @brief    添字演算子[csmInt32]
     */
    virtual Value& operator[](csmInt32 index)
    {
        return *(ErrorValue->SetErrorNotForClientCall(CSM_JSON_ERROR_TYPE_MISMATCH));
    }

    virtual const csmString& GetString(const csmString& defaultValue = "", const csmString& indent = "")
    {
        _stringBuffer = indent + "{\n";
        csmMap<csmString, Value*>::const_iterator ite = _map.Begin();
        while (ite != _map.End())
        {
            const csmString& key = (*ite).First;
            Value* v = (*ite).Second;

            _stringBuffer += indent + "	" + key + " : " + v->GetString(indent + "	") + "\n";
            ++ite;
        }
        _stringBuffer += indent + "}\n";
        return _stringBuffer;
    }

    /**
     * @brief    要素をMap型で返す
     */
    virtual csmMap<csmString, Value*>* GetMap(csmMap<csmString, Value*>* defaultValue = NULL)
    {
        return &_map;
    }

    /**
     * @brief    Mapに要素を追加する
     */
    void Put(csmString& key, Value* v)
    {
        _map[key] = v;
    }

    /**
     * @brief    Mapからキーのリストを取得する
     */
    virtual csmVector<csmString>& GetKeys()
    {
        if (!_keys)
        {
            _keys = CSM_NEW csmVector<csmString>();
            csmMap<csmString, Value*>::const_iterator ite = _map.Begin();
            while (ite != _map.End())
            {
                const csmString& key = (*ite).First;
                _keys->PushBack(key, true);
                ++ite;
            }
        }
        return *_keys;
    }

    /**
     * @brief    Mapの要素数を取得する
     */
    virtual csmInt32 GetSize() { return static_cast<csmInt32>(_keys->GetSize()); }

private:
    csmMap<csmString, Value*> _map;     ///< JSON要素の値
    csmVector<csmString>* _keys;        ///< JSON要素の値
};
}}}}

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