#include "JSONDataParser.h"
#include "base/Macros.h"

DRAGONBONES_NAMESPACE_BEGIN

void JSONDataParser::_getCurvePoint(
    float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
    float t,
    Point &result) {
    const auto l_t = 1.0f - t;
    const auto powA = l_t * l_t;
    const auto powB = t * t;
    const auto kA = l_t * powA;
    const auto kB = 3.0f * t * powA;
    const auto kC = 3.0f * l_t * powB;
    const auto kD = t * powB;

    result.x = kA * x1 + kB * x2 + kC * x3 + kD * x4;
    result.y = kA * y1 + kB * y2 + kC * y3 + kD * y4;
}

void JSONDataParser::_samplingEasingCurve(const rapidjson::Value &curve, std::vector<float> &samples) {
    int curveCount = curve.Size();
    int stepIndex = -2;
    for (std::size_t i = 0, l = samples.size(); i < l; ++i) {
        float t = (float)(i + 1) / (l + 1);                                             // float
        while ((stepIndex + 6 < curveCount ? curve[stepIndex + 6].GetDouble() : 1) < t) // stepIndex + 3 * 2
        {
            stepIndex += 6;
        }

        const auto isInCurve = stepIndex >= 0 && stepIndex + 6 < curveCount;
        const auto x1 = isInCurve ? curve[stepIndex].GetDouble() : 0.0f;
        const auto y1 = isInCurve ? curve[stepIndex + 1].GetDouble() : 0.0f;
        const auto x2 = curve[stepIndex + 2].GetDouble();
        const auto y2 = curve[stepIndex + 3].GetDouble();
        const auto x3 = curve[stepIndex + 4].GetDouble();
        const auto y3 = curve[stepIndex + 5].GetDouble();
        const auto x4 = isInCurve ? curve[stepIndex + 6].GetDouble() : 1.0f;
        const auto y4 = isInCurve ? curve[stepIndex + 7].GetDouble() : 1.0f;

        float lower = 0.0f;
        float higher = 1.0f;
        while (higher - lower > 0.0001f) {
            const auto percentage = (higher + lower) * 0.5f;
            _getCurvePoint(x1, y1, x2, y2, x3, y3, x4, y4, percentage, _helpPoint);
            if (t - _helpPoint.x > 0.0f) {
                lower = percentage;
            } else {
                higher = percentage;
            }
        }

        samples[i] = _helpPoint.y;
    }
}

void JSONDataParser::_parseActionDataInFrame(const rapidjson::Value &rawData, unsigned frameStart, BoneData *bone, SlotData *slot) {
    if (rawData.HasMember(EVENT)) {
        _mergeActionFrame(rawData[EVENT], frameStart, ActionType::Frame, bone, slot);
    }

    if (rawData.HasMember(SOUND)) {
        _mergeActionFrame(rawData[SOUND], frameStart, ActionType::Sound, bone, slot);
    }

    if (rawData.HasMember(ACTION)) {
        _mergeActionFrame(rawData[ACTION], frameStart, ActionType::Play, bone, slot);
    }

    if (rawData.HasMember(EVENTS)) {
        _mergeActionFrame(rawData[EVENTS], frameStart, ActionType::Frame, bone, slot);
    }

    if (rawData.HasMember(ACTIONS)) {
        _mergeActionFrame(rawData[ACTIONS], frameStart, ActionType::Play, bone, slot);
    }
}

void JSONDataParser::_mergeActionFrame(const rapidjson::Value &rawData, unsigned frameStart, ActionType type, BoneData *bone, SlotData *slot) {
    const auto actionOffset = _armature->actions.size();
    const auto &actions = _parseActionData(rawData, type, bone, slot);
    ActionFrame *frame = nullptr;

    for (const auto action : actions) {
        _armature->addAction(action, false);
    }

    if (_actionFrames.empty()) // First frame.
    {
        _actionFrames.resize(1);
        _actionFrames[0].frameStart = 0;
    }

    for (auto &eachFrame : _actionFrames) // Get same frame.
    {
        if (eachFrame.frameStart == frameStart) {
            frame = &eachFrame;
            break;
        }
    }

    if (frame == nullptr) // Create and cache frame.
    {
        const auto frameCount = _actionFrames.size();
        _actionFrames.resize(frameCount + 1);
        frame = &_actionFrames[frameCount];
        frame->frameStart = frameStart;
    }

    for (std::size_t i = 0; i < actions.size(); ++i) // Cache action offsets.
    {
        frame->actions.push_back(actionOffset + i);
    }
}

unsigned JSONDataParser::_parseCacheActionFrame(ActionFrame &frame) {
    const auto frameOffset = _frameArray.size();
    const auto actionCount = frame.actions.size();
    _frameArray.resize(_frameArray.size() + 1 + 1 + actionCount);
    _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition] = frame.frameStart;
    _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition + 1] = actionCount; // Action count.

    for (std::size_t i = 0; i < actionCount; ++i) // Action offsets.
    {
        _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition + 2 + i] = frame.actions[i];
    }

    return frameOffset;
}

ArmatureData *JSONDataParser::_parseArmature(const rapidjson::Value &rawData, float scale) {
    const auto armature = BaseObject::borrowObject<ArmatureData>();
    armature->name = _getString(rawData, NAME, "");
    armature->frameRate = _getNumber(rawData, FRAME_RATE, _data->frameRate);
    armature->scale = scale;

    if (rawData.HasMember(TYPE) && rawData[TYPE].IsString()) {
        armature->type = _getArmatureType(rawData[TYPE].GetString());
    } else {
        armature->type = (ArmatureType)_getNumber(rawData, TYPE, (int)ArmatureType::Armature);
    }

    if (armature->frameRate == 0) // Data error.
    {
        armature->frameRate = 24;
    }

    _armature = armature;

    if (rawData.HasMember(CANVAS)) {
        const auto &rawCanvas = rawData[CANVAS];
        const auto canvas = BaseObject::borrowObject<CanvasData>();
        canvas->hasBackground = rawCanvas.HasMember(COLOR);
        canvas->color = _getNumber(rawCanvas, COLOR, 0);
        canvas->aabb.x = _getNumber(rawCanvas, X, 0.0f) * armature->scale;
        canvas->aabb.y = _getNumber(rawCanvas, Y, 0.0f) * armature->scale;
        canvas->aabb.width = _getNumber(rawCanvas, WIDTH, 0.0f) * armature->scale;
        canvas->aabb.height = _getNumber(rawCanvas, HEIGHT, 0.0f) * armature->scale;
        //
        armature->canvas = canvas;
    }

    if (rawData.HasMember(AABB)) {
        const auto &rawAABB = rawData[AABB];
        armature->aabb.x = _getNumber(rawAABB, X, 0.0f) * armature->scale;
        armature->aabb.y = _getNumber(rawAABB, Y, 0.0f) * armature->scale;
        armature->aabb.width = _getNumber(rawAABB, WIDTH, 0.0f) * armature->scale;
        armature->aabb.height = _getNumber(rawAABB, HEIGHT, 0.0f) * armature->scale;
    }

    if (rawData.HasMember(BONE)) {
        const auto &rawBones = rawData[BONE];
        for (std::size_t i = 0, l = rawBones.Size(); i < l; ++i) {
            const auto &rawBone = rawBones[i];
            const auto &parentName = _getString(rawBone, PARENT, "");
            const auto bone = _parseBone(rawBone);

            if (!parentName.empty()) // Get bone parent.
            {
                const auto parent = armature->getBone(parentName);
                if (parent != nullptr) {
                    bone->parent = parent;
                } else // Cache.
                {
                    auto &cacheBones = _cacheBones[parentName];
                    cacheBones.push_back(bone);
                }
            }

            auto iterator = _cacheBones.find(bone->name);
            if (iterator != _cacheBones.end()) {
                for (const auto child : _cacheBones[bone->name]) {
                    child->parent = bone;
                }

                _cacheBones.erase(iterator);
            }

            armature->addBone(bone);
            _rawBones.push_back(bone); // Cache raw bones sort.
        }
    }

    if (rawData.HasMember(IK)) {
        const auto &rawIKS = rawData[IK];
        for (std::size_t i = 0, l = rawIKS.Size(); i < l; ++i) {
            const auto constraint = _parseIKConstraint(rawIKS[i]);
            if (constraint) {
                armature->addConstraint(constraint);
            }
        }
    }

    armature->sortBones();

    if (rawData.HasMember(SLOT)) {
        int zOrder = 0;
        const auto &rawSlots = rawData[SLOT];
        for (std::size_t i = 0, l = rawSlots.Size(); i < l; ++i) {
            armature->addSlot(_parseSlot(rawSlots[i], zOrder++));
        }
    }

    if (rawData.HasMember(SKIN)) {
        const auto &rawSkins = rawData[SKIN];
        for (std::size_t i = 0, l = rawSkins.Size(); i < l; ++i) {
            armature->addSkin(_parseSkin(rawSkins[i]));
        }
    }

    for (std::size_t i = 0, l = _cacheRawMeshes.size(); i < l; ++i) // Link mesh.
    {
        const auto rawMeshData = _cacheRawMeshes[i];
        const auto &shareName = _getString(*rawMeshData, SHARE, "");
        if (shareName.empty()) {
            continue;
        }

        auto skinName = _getString(*rawMeshData, SKIN, DEFAULT_NAME);
        if (skinName.empty()) //
        {
            skinName = DEFAULT_NAME;
        }

        const auto shareMesh = armature->getMesh(skinName, "", shareName); // TODO slot;
        if (shareMesh == nullptr) {
            continue; // Error.
        }

        const auto mesh = _cacheMeshes[i];
        mesh->vertices.shareFrom(shareMesh->vertices);
    }

    if (rawData.HasMember(ANIMATION)) {
        const auto &rawAnimations = rawData[ANIMATION];
        for (std::size_t i = 0, l = rawAnimations.Size(); i < l; ++i) {
            armature->addAnimation(_parseAnimation(rawAnimations[i]));
        }
    }

    if (rawData.HasMember(DEFAULT_ACTIONS)) {
        const auto &actions = _parseActionData(rawData[DEFAULT_ACTIONS], ActionType::Play, nullptr, nullptr);
        for (const auto action : actions) {
            armature->addAction(action, true);

            if (action->type == ActionType::Play) // Set default animation from default action.
            {
                const auto animation = armature->getAnimation(action->name);
                if (animation != nullptr) {
                    armature->defaultAnimation = animation;
                }
            }
        }
    }

    if (rawData.HasMember(ACTIONS)) {
        const auto &actions = _parseActionData(rawData[ACTIONS], ActionType::Play, nullptr, nullptr);

        for (const auto action : actions) {
            armature->addAction(action, false);
        }
    }

    // Clear helper.
    _rawBones.clear();
    _cacheRawMeshes.clear();
    _cacheMeshes.clear();
    _armature = nullptr;

    _weightSlotPose.clear();
    _weightBonePoses.clear();
    _cacheBones.clear();
    _slotChildActions.clear();

    return armature;
}

BoneData *JSONDataParser::_parseBone(const rapidjson::Value &rawData) {
    const auto bone = BaseObject::borrowObject<BoneData>();
    bone->inheritTranslation = _getBoolean(rawData, INHERIT_TRANSLATION, true);
    bone->inheritRotation = _getBoolean(rawData, INHERIT_ROTATION, true);
    bone->inheritScale = _getBoolean(rawData, INHERIT_SCALE, true);
    bone->inheritReflection = _getBoolean(rawData, INHERIT_REFLECTION, true);
    bone->length = _getNumber(rawData, LENGTH, 0.0f) * _armature->scale;
    bone->name = _getString(rawData, NAME, "");

    if (rawData.HasMember(TRANSFORM)) {
        _parseTransform(rawData[TRANSFORM], bone->transform, _armature->scale);
    }

    return bone;
}

ConstraintData *JSONDataParser::_parseIKConstraint(const rapidjson::Value &rawData) {
    const auto bone = _armature->getBone(_getString(rawData, BONE, ""));
    if (bone == nullptr) {
        return nullptr;
    }

    const auto target = _armature->getBone(_getString(rawData, TARGET, ""));
    if (target == nullptr) {
        return nullptr;
    }

    const auto constraint = BaseObject::borrowObject<IKConstraintData>();
    constraint->scaleEnabled = _getBoolean(rawData, SCALE, false);
    constraint->bendPositive = _getBoolean(rawData, BEND_POSITIVE, true);
    constraint->weight = _getNumber(rawData, WEIGHT, 1.0f);
    constraint->name = _getString(rawData, NAME, "");
    constraint->bone = bone;
    constraint->target = target;

    const auto chain = _getNumber(rawData, CHAIN, (unsigned)0);
    if (chain > 0 && bone->parent != nullptr) {
        constraint->root = bone->parent;
        constraint->bone = bone;
    } else {
        constraint->root = bone;
        constraint->bone = nullptr;
    }

    return constraint;
}

SlotData *JSONDataParser::_parseSlot(const rapidjson::Value &rawData, int zOrder) {
    const auto slot = BaseObject::borrowObject<SlotData>();
    slot->displayIndex = _getNumber(rawData, DISPLAY_INDEX, (int)0);
    slot->zOrder = zOrder;
    slot->name = _getString(rawData, NAME, "");
    slot->parent = _armature->getBone(_getString(rawData, PARENT, ""));

    if (rawData.HasMember(BLEND_MODE) && rawData[BLEND_MODE].IsString()) {
        slot->blendMode = _getBlendMode(rawData[BLEND_MODE].GetString());
    } else {
        slot->blendMode = (BlendMode)_getNumber(rawData, BLEND_MODE, (int)BlendMode::Normal);
    }

    if (rawData.HasMember(COLOR)) {
        slot->color = SlotData::createColor();
        _parseColorTransform(rawData[COLOR], *slot->color);
    } else {
        slot->color = &SlotData::DEFAULT_COLOR;
    }

    if (rawData.HasMember(ACTIONS)) {
        _slotChildActions[slot->name] = _parseActionData(rawData[ACTIONS], ActionType::Play, nullptr, nullptr);
    }

    return slot;
}

SkinData *JSONDataParser::_parseSkin(const rapidjson::Value &rawData) {
    const auto skin = BaseObject::borrowObject<SkinData>();
    skin->name = _getString(rawData, NAME, DEFAULT_NAME);
    if (skin->name.empty()) {
        skin->name = DEFAULT_NAME;
    }

    if (rawData.HasMember(SLOT)) {
        const auto &rawSlots = rawData[SLOT];
        _skin = skin;

        for (std::size_t i = 0, l = rawSlots.Size(); i < l; ++i) {
            const auto &rawSlot = rawSlots[i];
            const auto &slotName = _getString(rawSlot, NAME, "");
            const auto slot = _armature->getSlot(slotName);
            if (slot != nullptr) {
                _slot = slot;

                if (rawSlot.HasMember(DISPLAY)) {
                    const auto &rawDisplays = rawSlot[DISPLAY];
                    for (std::size_t j = 0, lJ = rawDisplays.Size(); j < lJ; ++j) {
                        const auto &rawDisplay = rawDisplays[j];
                        if (!rawDisplay.IsNull()) {
                            skin->addDisplay(slotName, _parseDisplay(rawDisplay));
                        } else {
                            skin->addDisplay(slotName, nullptr);
                        }
                    }
                }

                _slot = nullptr;
            }
        }

        _skin = nullptr;
    }

    return skin;
}

DisplayData *JSONDataParser::_parseDisplay(const rapidjson::Value &rawData) {
    const auto &name = _getString(rawData, NAME, "");
    const auto &path = _getString(rawData, PATH, "");
    auto type = DisplayType::Image;
    DisplayData *display = nullptr;

    if (rawData.HasMember(TYPE) && rawData[TYPE].IsString()) {
        type = _getDisplayType(rawData[TYPE].GetString());
    } else {
        type = (DisplayType)_getNumber(rawData, TYPE, (int)DisplayType::Image);
    }

    switch (type) {
        case dragonBones::DisplayType::Image: {
            const auto imageDisplay = BaseObject::borrowObject<ImageDisplayData>();
            imageDisplay->name = name;
            imageDisplay->path = !path.empty() ? path : name;
            _parsePivot(rawData, *imageDisplay);

            display = imageDisplay;
            break;
        }

        case dragonBones::DisplayType::Armature: {
            const auto armatureDisplay = BaseObject::borrowObject<ArmatureDisplayData>();
            armatureDisplay->name = name;
            armatureDisplay->path = !path.empty() ? path : name;
            armatureDisplay->inheritAnimation = true;

            if (rawData.HasMember(ACTIONS)) {
                const auto &actions = _parseActionData(rawData[ACTIONS], ActionType::Play, nullptr, nullptr);

                for (const auto action : actions) {
                    armatureDisplay->addAction(action);
                }
            } else if (_slotChildActions.find(_slot->name) != _slotChildActions.cend()) {
                const auto displays = _skin->getDisplays(_slot->name);
                if (displays == nullptr ? _slot->displayIndex == 0 : (std::size_t)_slot->displayIndex == displays->size()) {
                    for (const auto action : _slotChildActions[_slot->name]) {
                        armatureDisplay->addAction(action);
                    }

                    _slotChildActions.erase(_slotChildActions.find(_slot->name));
                }
            }

            display = armatureDisplay;
            break;
        }

        case dragonBones::DisplayType::Mesh: {
            const auto meshDisplay = BaseObject::borrowObject<MeshDisplayData>();
            meshDisplay->vertices.inheritDeform = _getBoolean(rawData, INHERIT_DEFORM, true);
            meshDisplay->name = name;
            meshDisplay->path = !path.empty() ? path : name;
            meshDisplay->vertices.data = _data;

            if (rawData.HasMember(SHARE)) {
                _cacheRawMeshes.push_back(&rawData);
                _cacheMeshes.push_back(meshDisplay);
            } else {
                _parseMesh(rawData, *meshDisplay);
            }

            display = meshDisplay;
            break;
        }

        case dragonBones::DisplayType::BoundingBox: {
            const auto boundingBox = _parseBoundingBox(rawData);
            if (boundingBox != nullptr) {
                const auto boundingBoxDisplay = BaseObject::borrowObject<BoundingBoxDisplayData>();
                boundingBoxDisplay->name = name;
                boundingBoxDisplay->path = !path.empty() ? path : name;
                boundingBoxDisplay->boundingBox = boundingBox;

                display = boundingBoxDisplay;
            }
            break;
        }
        case dragonBones::DisplayType::Path:
        default:
            // suppress warning
            //TODO:
            break;
    }

    if (display != nullptr && rawData.HasMember(TRANSFORM)) {
        _parseTransform(rawData[TRANSFORM], display->transform, _armature->scale);
    }

    return display;
}

void JSONDataParser::_parsePivot(const rapidjson::Value &rawData, ImageDisplayData &display) {
    if (rawData.HasMember(PIVOT)) {
        const auto &rawPivot = rawData[PIVOT];
        display.pivot.x = _getNumber(rawPivot, X, 0.0f);
        display.pivot.y = _getNumber(rawPivot, Y, 0.0f);
    } else {
        display.pivot.x = 0.5f;
        display.pivot.y = 0.5f;
    }
}

void JSONDataParser::_parseMesh(const rapidjson::Value &rawData, MeshDisplayData &mesh) {
    const auto &rawVertices = rawData[VERTICES];
    const auto &rawUVs = rawData[UVS];
    const auto &rawTriangles = rawData[TRIANGLES];
    const auto vertexCount = rawVertices.Size() / 2;
    const auto triangleCount = rawTriangles.Size() / 3;
    const auto vertexOffset = _floatArray.size();
    const auto uvOffset = vertexOffset + vertexCount * 2;
    const auto meshOffset = _intArray.size();
    const auto meshName = _skin->name + "_" + _slot->name + "_" + mesh.name; // Cache pose data.

    mesh.vertices.offset = meshOffset;
    _intArray.resize(_intArray.size() + 1 + 1 + 1 + 1 + triangleCount * 3);
    _intArray[meshOffset + (unsigned)BinaryOffset::MeshVertexCount] = vertexCount;
    _intArray[meshOffset + (unsigned)BinaryOffset::MeshTriangleCount] = triangleCount;
    _intArray[meshOffset + (unsigned)BinaryOffset::MeshFloatOffset] = vertexOffset;
    for (std::size_t i = 0, l = triangleCount * 3; i < l; ++i) {
        _intArray[meshOffset + (unsigned)BinaryOffset::MeshVertexIndices + i] = rawTriangles[i].GetUint();
    }

    _floatArray.resize(_floatArray.size() + vertexCount * 2 + vertexCount * 2);
    for (std::size_t i = 0, l = vertexCount * 2; i < l; ++i) {
        _floatArray[vertexOffset + i] = rawVertices[i].GetDouble();
        _floatArray[uvOffset + i] = rawUVs[i].GetDouble();
    }

    if (rawData.HasMember(WEIGHTS)) {
        const auto &rawWeights = rawData[WEIGHTS];
        const auto &rawSlotPose = rawData[SLOT_POSE];
        const auto &rawBonePoses = rawData[BONE_POSE];
        const auto &sortedBones = _armature->sortedBones;
        std::vector<unsigned> weightBoneIndices;
        const unsigned weightBoneCount = rawBonePoses.Size() / 7;
        const auto floatOffset = _floatArray.size();
        const auto weightCount = (rawWeights.Size() - vertexCount) / 2; // uint
        const auto weightOffset = _intArray.size();
        const auto weight = BaseObject::borrowObject<WeightData>();

        weight->count = weightCount;
        weight->offset = weightOffset;
        weightBoneIndices.resize(weightBoneCount);
        _intArray.resize(_intArray.size() + 1 + 1 + weightBoneCount + vertexCount + weightCount);
        _intArray[weightOffset + (unsigned)BinaryOffset::WeigthFloatOffset] = floatOffset;

        for (std::size_t i = 0; i < weightBoneCount; ++i) {
            const auto rawBoneIndex = rawBonePoses[i * 7].GetUint();
            const auto bone = _rawBones[rawBoneIndex];
            weight->addBone(bone);
            weightBoneIndices[i] = rawBoneIndex;
            _intArray[weightOffset + (unsigned)BinaryOffset::WeigthBoneIndices + i] = indexOf(sortedBones, bone);
        }

        _floatArray.resize(_floatArray.size() + weightCount * 3);

        _helpMatrixA.a = rawSlotPose[0].GetDouble();
        _helpMatrixA.b = rawSlotPose[1].GetDouble();
        _helpMatrixA.c = rawSlotPose[2].GetDouble();
        _helpMatrixA.d = rawSlotPose[3].GetDouble();
        _helpMatrixA.tx = rawSlotPose[4].GetDouble();
        _helpMatrixA.ty = rawSlotPose[5].GetDouble();

        for (
            std::size_t i = 0, iW = 0, iB = weightOffset + (unsigned)BinaryOffset::WeigthBoneIndices + weightBoneCount, iV = floatOffset;
            i < vertexCount;
            ++i) {
            const auto iD = i * 2;
            const auto vertexBoneCount = rawWeights[iW++].GetUint();
            _intArray[iB++] = vertexBoneCount;

            auto x = _floatArray[vertexOffset + iD];
            auto y = _floatArray[vertexOffset + iD + 1];
            _helpMatrixA.transformPoint(x, y, _helpPoint);

            x = _helpPoint.x;
            y = _helpPoint.y;

            for (std::size_t j = 0; j < vertexBoneCount; ++j) {
                const auto rawBoneIndex = rawWeights[iW++].GetUint();
                const auto boneIndex = indexOf(weightBoneIndices, rawBoneIndex);
                const auto matrixOffset = boneIndex * 7 + 1;

                _helpMatrixB.a = rawBonePoses[matrixOffset + 0].GetDouble();
                _helpMatrixB.b = rawBonePoses[matrixOffset + 1].GetDouble();
                _helpMatrixB.c = rawBonePoses[matrixOffset + 2].GetDouble();
                _helpMatrixB.d = rawBonePoses[matrixOffset + 3].GetDouble();
                _helpMatrixB.tx = rawBonePoses[matrixOffset + 4].GetDouble();
                _helpMatrixB.ty = rawBonePoses[matrixOffset + 5].GetDouble();
                _helpMatrixB.invert();
                _helpMatrixB.transformPoint(x, y, _helpPoint);

                _intArray[iB++] = boneIndex;
                _floatArray[iV++] = rawWeights[iW++].GetDouble();
                _floatArray[iV++] = _helpPoint.x;
                _floatArray[iV++] = _helpPoint.y;
            }
        }

        mesh.vertices.weight = weight;
        _weightSlotPose[meshName] = &rawSlotPose;
        _weightBonePoses[meshName] = &rawBonePoses;
    }
}

BoundingBoxData *JSONDataParser::_parseBoundingBox(const rapidjson::Value &rawData) {
    BoundingBoxData *boundingBox = nullptr;
    BoundingBoxType type = BoundingBoxType::Rectangle;
    if (rawData.HasMember(SUB_TYPE) && rawData[SUB_TYPE].IsString()) {
        type = _getBoundingBoxType(rawData[SUB_TYPE].GetString());
    } else {
        type = (BoundingBoxType)_getNumber(rawData, SUB_TYPE, (int)type);
    }

    switch (type) {
        case BoundingBoxType::Rectangle:
            boundingBox = BaseObject::borrowObject<RectangleBoundingBoxData>();
            break;

        case BoundingBoxType::Ellipse:
            boundingBox = BaseObject::borrowObject<EllipseBoundingBoxData>();
            break;

        case BoundingBoxType::Polygon:
            boundingBox = _parsePolygonBoundingBox(rawData);
            break;
    }

    if (boundingBox != nullptr) {
        boundingBox->color = _getNumber(rawData, COLOR, 0x000000);
        if (boundingBox->type == BoundingBoxType::Rectangle || boundingBox->type == BoundingBoxType::Ellipse) {
            boundingBox->width = _getNumber(rawData, WIDTH, 0.0f);
            boundingBox->height = _getNumber(rawData, HEIGHT, 0.0f);
        }
    }

    return boundingBox;
}

PolygonBoundingBoxData *JSONDataParser::_parsePolygonBoundingBox(const rapidjson::Value &rawData) {
    const auto polygonBoundingBox = BaseObject::borrowObject<PolygonBoundingBoxData>();

    if (rawData.HasMember(VERTICES)) {
        const auto &rawVertices = rawData[VERTICES];
        auto &vertices = polygonBoundingBox->vertices;

        polygonBoundingBox->vertices.resize(rawVertices.Size());

        for (std::size_t i = 0, l = rawVertices.Size(); i < l; i += 2) {
            const auto x = rawVertices[i].GetDouble();
            const auto y = rawVertices[i + 1].GetDouble();
            vertices[i] = x;
            vertices[i + 1] = y;

            // AABB.
            if (i == 0) {
                polygonBoundingBox->x = x;
                polygonBoundingBox->y = y;
                polygonBoundingBox->width = x;
                polygonBoundingBox->height = y;
            } else {
                if (x < polygonBoundingBox->x) {
                    polygonBoundingBox->x = x;
                } else if (x > polygonBoundingBox->width) {
                    polygonBoundingBox->width = x;
                }

                if (y < polygonBoundingBox->y) {
                    polygonBoundingBox->y = y;
                } else if (y > polygonBoundingBox->height) {
                    polygonBoundingBox->height = y;
                }
            }
        }

        polygonBoundingBox->width -= polygonBoundingBox->x;
        polygonBoundingBox->height -= polygonBoundingBox->y;
    } else {
        DRAGONBONES_ASSERT(false, "Data error.\n Please reexport DragonBones Data to fixed the bug.");
    }

    return polygonBoundingBox;
}

AnimationData *JSONDataParser::_parseAnimation(const rapidjson::Value &rawData) {
    const auto animation = BaseObject::borrowObject<AnimationData>();
    animation->frameCount = std::max(_getNumber(rawData, DURATION, (unsigned)1), (unsigned)1);
    animation->playTimes = _getNumber(rawData, PLAY_TIMES, (unsigned)1);
    animation->duration = (float)animation->frameCount / _armature->frameRate; // float
    animation->fadeInTime = _getNumber(rawData, FADE_IN_TIME, 0.0f);
    animation->scale = _getNumber(rawData, SCALE, 1.0f);
    animation->name = _getString(rawData, NAME, DEFAULT_NAME);

    if (animation->name.empty()) {
        animation->name = DEFAULT_NAME;
    }

    animation->frameIntOffset = _frameIntArray.size();
    animation->frameFloatOffset = _frameFloatArray.size();
    animation->frameOffset = _frameArray.size();

    _animation = animation;

    if (rawData.HasMember(FRAME)) {
        const auto &rawFrames = rawData[FRAME];
        const auto keyFrameCount = rawFrames.Size();
        if (keyFrameCount > 0) {
            for (std::size_t i = 0, frameStart = 0; i < keyFrameCount; ++i) {
                const auto &rawFrame = rawFrames[i];
                _parseActionDataInFrame(rawFrame, frameStart, nullptr, nullptr);
                frameStart += _getNumber(rawFrame, DURATION, (unsigned)1);
            }
        }
    }

    if (rawData.HasMember(Z_ORDER)) {
        _animation->zOrderTimeline = _parseTimeline(
            rawData[Z_ORDER], FRAME, TimelineType::ZOrder,
            false, false, 0,
            std::bind(&JSONDataParser::_parseZOrderFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    if (rawData.HasMember(BONE)) {
        const auto &rawTimelines = rawData[BONE];
        for (std::size_t i = 0, l = rawTimelines.Size(); i < l; ++i) {
            _parseBoneTimeline(rawTimelines[i]);
        }
    }

    if (rawData.HasMember(SLOT)) {
        const auto &rawTimelines = rawData[SLOT];
        for (std::size_t i = 0, l = rawTimelines.Size(); i < l; ++i) {
            _parseSlotTimeline(rawTimelines[i]);
        }
    }

    if (rawData.HasMember(FFD)) {
        const auto &rawTimelines = rawData[FFD];
        for (std::size_t i = 0, l = rawTimelines.Size(); i < l; ++i) {
            const auto &rawTimeline = rawTimelines[i];
            auto skinName = _getString(rawTimeline, SKIN, DEFAULT_NAME);
            const auto &slotName = _getString(rawTimeline, SLOT, "");
            const auto &displayName = _getString(rawTimeline, NAME, "");

            if (skinName.empty()) //
            {
                skinName = DEFAULT_NAME;
            }

            _slot = _armature->getSlot(slotName);
            _mesh = _armature->getMesh(skinName, slotName, displayName);
            if (_slot == nullptr || _mesh == nullptr) {
                continue;
            }

            const auto timeline = _parseTimeline(
                rawTimeline, FRAME, TimelineType::SlotDeform,
                false, true, 0,
                std::bind(&JSONDataParser::_parseSlotFFDFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            if (timeline != nullptr) {
                _animation->addSlotTimeline(_slot, timeline);
            }

            _slot = nullptr;
            _mesh = nullptr;
        }
    }

    if (rawData.HasMember(IK)) {
        const auto &rawTimelines = rawData[IK];
        for (std::size_t i = 0, l = rawTimelines.Size(); i < l; ++i) {
            const auto &rawTimeline = rawTimelines[i];
            const auto &constraintName = _getString(rawTimeline, NAME, "");
            const auto constraint = _armature->getConstraint(constraintName);
            if (constraint == nullptr) {
                continue;
            }

            const auto timeline = _parseTimeline(
                rawTimeline, FRAME, TimelineType::IKConstraint,
                true, false, 2,
                std::bind(&JSONDataParser::_parseIKConstraintFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            if (timeline != nullptr) {
                _animation->addConstraintTimeline(constraint, timeline);
            }
        }
    }

    if (_actionFrames.size() > 0) {
        std::sort(_actionFrames.begin(), _actionFrames.end());

        const auto timeline = _animation->actionTimeline = BaseObject::borrowObject<TimelineData>();
        const auto keyFrameCount = _actionFrames.size();
        timeline->type = TimelineType::Action;
        timeline->offset = _timelineArray.size();
        _timelineArray.resize(_timelineArray.size() + 1 + 1 + 1 + 1 + 1 + keyFrameCount);
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineScale] = 100;
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineOffset] = 0;
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineKeyFrameCount] = keyFrameCount;
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueCount] = 0;
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueOffset] = 0;

        _timeline = timeline;

        if (keyFrameCount == 1) {
            timeline->frameIndicesOffset = -1;
            _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameOffset + 0] = _parseCacheActionFrame(_actionFrames[0]) - _animation->frameOffset;
        } else {
            const auto totalFrameCount = _animation->frameCount + 1; // One more frame than animation.
            auto &frameIndices = _data->frameIndices;
            timeline->frameIndicesOffset = frameIndices.size();
            frameIndices.resize(frameIndices.size() + totalFrameCount);

            for (
                std::size_t i = 0, iK = 0, frameStart = 0, frameCount = 0;
                i < totalFrameCount;
                ++i) {
                if (frameStart + frameCount <= i && iK < keyFrameCount) {
                    auto &frame = _actionFrames[iK];
                    frameStart = frame.frameStart;
                    if (iK == keyFrameCount - 1) {
                        frameCount = _animation->frameCount - frameStart;
                    } else {
                        frameCount = _actionFrames[iK + 1].frameStart - frameStart;
                    }

                    _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameOffset + iK] = _parseActionFrame(frame, frameStart, frameCount) - _animation->frameOffset;
                    iK++;
                }

                frameIndices[timeline->frameIndicesOffset + i] = iK - 1;
            }
        }

        _timeline = nullptr;
        _actionFrames.clear();
    }

    _animation = nullptr;

    return animation;
}

TimelineData *JSONDataParser::_parseTimeline(
    const rapidjson::Value &rawData, const char *framesKey, TimelineType type,
    bool addIntOffset, bool addFloatOffset, unsigned frameValueCount,
    const std::function<unsigned(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount)> &frameParser) {
    if (!rawData.HasMember(framesKey)) {
        return nullptr;
    }

    const auto &rawFrames = rawData[framesKey];
    const auto keyFrameCount = rawFrames.Size();
    if (keyFrameCount == 0) {
        return nullptr;
    }

    const auto timeline = BaseObject::borrowObject<TimelineData>();
    timeline->type = type;
    timeline->offset = _timelineArray.size();
    _timelineArray.resize(_timelineArray.size() + 1 + 1 + 1 + 1 + 1 + keyFrameCount);
    _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineScale] = _getNumber(rawData, SCALE, 1.0f) * 100.f;
    _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineOffset] = _getNumber(rawData, OFFSET, 0.0f) * 100.f;
    _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineKeyFrameCount] = keyFrameCount;
    _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueCount] = frameValueCount;
    if (addIntOffset) {
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueOffset] = _frameIntArray.size() - _animation->frameIntOffset;
    } else if (addFloatOffset) {
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueOffset] = _frameFloatArray.size() - _animation->frameFloatOffset;
    } else {
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueOffset] = 0;
    }

    _timeline = timeline;

    if (keyFrameCount == 1) // Only one frame.
    {
        timeline->frameIndicesOffset = -1;
        _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameOffset + 0] = frameParser(rawFrames[0], 0, 0) - _animation->frameOffset;
    } else {
        unsigned frameIndicesOffset = 0;
        auto &frameIndices = _data->frameIndices;
        const auto totalFrameCount = _animation->frameCount + 1; // One more frame than animation.
        frameIndicesOffset = frameIndices.size();
        frameIndices.resize(frameIndicesOffset + totalFrameCount);
        timeline->frameIndicesOffset = frameIndicesOffset;

        for (
            std::size_t i = 0, iK = 0, frameStart = 0, frameCount = 0;
            i < totalFrameCount;
            ++i) {
            if (frameStart + frameCount <= i && iK < keyFrameCount) {
                const auto &rawFrame = rawFrames[iK];
                frameStart = i;
                frameCount = _getNumber(rawFrame, DURATION, (unsigned)1);
                if (iK == keyFrameCount - 1) {
                    frameCount = _animation->frameCount - frameStart;
                }

                _timelineArray[timeline->offset + (unsigned)BinaryOffset::TimelineFrameOffset + iK] = frameParser(rawFrame, frameStart, frameCount) - _animation->frameOffset;
                iK++;
            }

            frameIndices[frameIndicesOffset + i] = iK - 1;
        }
    }

    _timeline = nullptr;

    return timeline;
}

void JSONDataParser::_parseBoneTimeline(const rapidjson::Value &rawData) {
    const auto bone = _armature->getBone(_getString(rawData, NAME, ""));
    if (bone == nullptr) {
        return;
    }

    _bone = bone;
    _slot = _armature->getSlot(_bone->name);

    if (rawData.HasMember(TRANSLATE_FRAME)) {
        const auto timeline = _parseTimeline(
            rawData, TRANSLATE_FRAME, TimelineType::BoneTranslate,
            false, true, 2,
            std::bind(&JSONDataParser::_parseBoneTranslateFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        if (timeline != nullptr) {
            _animation->addBoneTimeline(bone, timeline);
        }
    }

    if (rawData.HasMember(ROTATE_FRAME)) {
        const auto timeline = _parseTimeline(
            rawData, ROTATE_FRAME, TimelineType::BoneRotate,
            false, true, 2,
            std::bind(&JSONDataParser::_parseBoneRotateFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        if (timeline != nullptr) {
            _animation->addBoneTimeline(bone, timeline);
        }
    }

    if (rawData.HasMember(SCALE_FRAME)) {
        const auto timeline = _parseTimeline(
            rawData, SCALE_FRAME, TimelineType::BoneScale,
            false, true, 2,
            std::bind(&JSONDataParser::_parseBoneScaleFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        if (timeline != nullptr) {
            _animation->addBoneTimeline(bone, timeline);
        }
    }

    if (rawData.HasMember(FRAME)) {
        const auto timeline = _parseTimeline(
            rawData, FRAME, TimelineType::BoneAll,
            false, true, 6,
            std::bind(&JSONDataParser::_parseBoneAllFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        if (timeline != nullptr) {
            _animation->addBoneTimeline(bone, timeline);
        }
    }

    _bone = nullptr;
    _slot = nullptr;
}

void JSONDataParser::_parseSlotTimeline(const rapidjson::Value &rawData) {
    const auto slot = _armature->getSlot(_getString(rawData, NAME, ""));
    if (slot == nullptr) {
        return;
    }

    TimelineData *displayTimeline = nullptr;
    TimelineData *colorTimeline = nullptr;
    _slot = slot;

    if (rawData.HasMember(DISPLAY_FRAME)) {
        displayTimeline = _parseTimeline(
            rawData, DISPLAY_FRAME, TimelineType::SlotDisplay,
            false, false, 0,
            std::bind(&JSONDataParser::_parseSlotDisplayFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    } else {
        displayTimeline = _parseTimeline(
            rawData, FRAME, TimelineType::SlotDisplay,
            false, false, 0,
            std::bind(&JSONDataParser::_parseSlotDisplayFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    if (rawData.HasMember(COLOR_FRAME)) {
        colorTimeline = _parseTimeline(
            rawData, COLOR_FRAME, TimelineType::SlotColor,
            true, false, 1,
            std::bind(&JSONDataParser::_parseSlotColorFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    } else {
        colorTimeline = _parseTimeline(
            rawData, FRAME, TimelineType::SlotColor,
            true, false, 1,
            std::bind(&JSONDataParser::_parseSlotColorFrame, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    if (displayTimeline != nullptr) {
        _animation->addSlotTimeline(slot, displayTimeline);
    }

    if (colorTimeline != nullptr) {
        _animation->addSlotTimeline(slot, colorTimeline);
    }

    _slot = nullptr;
}

unsigned JSONDataParser::_parseFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _frameArray.size();
    _frameArray.resize(_frameArray.size() + 1);
    _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition] = frameStart;

    return frameOffset;
}

unsigned JSONDataParser::_parseTweenFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseFrame(rawData, frameStart, frameCount);

    if (frameCount > 0) {
        if (rawData.HasMember(CURVE)) {
            const auto sampleCount = frameCount + 1;
            _helpArray.resize(sampleCount);
            _samplingEasingCurve(rawData[CURVE], _helpArray);
            _frameArray.resize(_frameArray.size() + 1 + 1 + _helpArray.size());
            _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int)TweenType::Curve;
            _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenEasingOrCurveSampleCount] = sampleCount;
            for (std::size_t i = 0; i < sampleCount; ++i) {
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameCurveSamples + i] = _helpArray[i] * 10000.0f;
            }
        } else {
            const auto noTween = -2.0f;
            auto tweenEasing = noTween;
            if (rawData.HasMember(TWEEN_EASING)) {
                tweenEasing = _getNumber(rawData, TWEEN_EASING, noTween);
            }

            if (tweenEasing == noTween) {
                _frameArray.resize(_frameArray.size() + 1);
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::None;
            } else if (tweenEasing == 0.0f) {
                _frameArray.resize(_frameArray.size() + 1);
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::Line;
            } else if (tweenEasing < 0.0f) {
                _frameArray.resize(_frameArray.size() + 1 + 1);
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::QuadIn;
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenEasingOrCurveSampleCount] = -tweenEasing * 100.0f;
            } else if (tweenEasing <= 1.0f) {
                _frameArray.resize(_frameArray.size() + 1 + 1);
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::QuadOut;
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenEasingOrCurveSampleCount] = tweenEasing * 100.0f;
            } else {
                _frameArray.resize(_frameArray.size() + 1 + 1);
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::QuadInOut;
                _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenEasingOrCurveSampleCount] = tweenEasing * 100.0f - 100.0f;
            }
        }
    } else {
        _frameArray.resize(_frameArray.size() + 1);
        _frameArray[frameOffset + (unsigned)BinaryOffset::FrameTweenType] = (int16_t)TweenType::None;
    }

    return frameOffset;
}

unsigned JSONDataParser::_parseActionFrame(const ActionFrame &frame, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _frameArray.size();
    const auto actionCount = frame.actions.size();
    _frameArray.resize(_frameArray.size() + 1 + 1 + actionCount);
    _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition] = frameStart;
    _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition + 1] = actionCount; // Action count.

    for (std::size_t i = 0; i < actionCount; ++i) // Action offsets.
    {
        _frameArray[frameOffset + (unsigned)BinaryOffset::FramePosition + 2 + i] = frame.actions[i];
    }

    return frameOffset;
}

unsigned JSONDataParser::_parseZOrderFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseFrame(rawData, frameStart, frameCount);

    if (rawData.HasMember(Z_ORDER)) {
        const auto &rawZOrder = rawData[Z_ORDER];
        if (!rawZOrder.Empty()) {
            const auto slotCount = _armature->sortedSlots.size();
            std::vector<int> unchanged;
            std::vector<int> zOrders;
            unchanged.resize(slotCount - rawZOrder.Size() / 2);
            zOrders.resize(slotCount);

            for (std::size_t i = 0; i < unchanged.size(); ++i) {
                unchanged[i] = 0;
            }

            for (std::size_t i = 0; i < slotCount; ++i) {
                zOrders[i] = -1;
            }

            unsigned originalIndex = 0;
            unsigned unchangedIndex = 0;
            for (std::size_t i = 0, l = rawZOrder.Size(); i < l; i += 2) {
                const auto slotIndex = rawZOrder[i].GetInt();
                const auto zOrderOffset = rawZOrder[i + 1].GetInt();
                while (originalIndex != (unsigned)slotIndex) {
                    unchanged[unchangedIndex++] = originalIndex++;
                }

                unsigned index = originalIndex + zOrderOffset;
                // CC_ASSERT(index >= 0 && index < zOrders.size());

                if (!(index >= 0 && index < zOrders.size())) {
                    originalIndex++;
                    continue;
                }
                zOrders[index] = originalIndex++;
            }

            while (originalIndex < slotCount) {
                unchanged[unchangedIndex++] = originalIndex++;
            }

            _frameArray.resize(_frameArray.size() + 1 + slotCount);
            _frameArray[frameOffset + 1] = slotCount;

            int i = slotCount;
            while (i--) {
                if (zOrders[i] == -1) {
                    // _frameArray[frameOffset + 2 + i] = unchanged[--unchangedIndex];

                    if (unchangedIndex > 0) {
                        _frameArray[frameOffset + 2 + i] = unchanged[--unchangedIndex];
                    } else {
                        _frameArray[frameOffset + 2 + i] = 0;
                    }
                } else {
                    _frameArray[frameOffset + 2 + i] = zOrders[i];
                }
            }

            return frameOffset;
        }
    }

    _frameArray.resize(_frameArray.size() + 1);
    _frameArray[frameOffset + 1] = 0;

    return frameOffset;
}

unsigned JSONDataParser::_parseBoneAllFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    _helpTransform.identity();
    if (rawData.HasMember(TRANSFORM)) {
        _parseTransform(rawData[TRANSFORM], _helpTransform, 1.0f);
    }

    // Modify rotation.
    auto rotation = _helpTransform.rotation;
    if (frameStart != 0) {
        if (_prevClockwise == 0) {
            rotation = _prevRotation + Transform::normalizeRadian(rotation - _prevRotation);
        } else {
            if (_prevClockwise > 0 ? rotation >= _prevRotation : rotation <= _prevRotation) {
                _prevClockwise = _prevClockwise > 0 ? _prevClockwise - 1 : _prevClockwise + 1;
            }

            rotation = _prevRotation + rotation - _prevRotation + Transform::PI_D * _prevClockwise;
        }
    }

    _prevClockwise = _getNumber(rawData, TWEEN_ROTATE, 0.0f);
    _prevRotation = rotation;
    //
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);
    auto frameFloatOffset = _frameFloatArray.size();
    _frameFloatArray.resize(_frameFloatArray.size() + 6);
    _frameFloatArray[frameFloatOffset++] = _helpTransform.x;
    _frameFloatArray[frameFloatOffset++] = _helpTransform.y;
    _frameFloatArray[frameFloatOffset++] = rotation;
    _frameFloatArray[frameFloatOffset++] = _helpTransform.skew;
    _frameFloatArray[frameFloatOffset++] = _helpTransform.scaleX;
    _frameFloatArray[frameFloatOffset++] = _helpTransform.scaleY;
    _parseActionDataInFrame(rawData, frameStart, _bone, _slot);

    return frameOffset;
}

unsigned JSONDataParser::_parseBoneTranslateFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);
    auto frameFloatOffset = _frameFloatArray.size();
    _frameFloatArray.resize(_frameFloatArray.size() + 2);
    _frameFloatArray[frameFloatOffset++] = _getNumber(rawData, X, 0.0f);
    _frameFloatArray[frameFloatOffset++] = _getNumber(rawData, Y, 0.0f);

    return frameOffset;
}

unsigned JSONDataParser::_parseBoneRotateFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    // Modify rotation.
    auto rotation = _getNumber(rawData, ROTATE, 0.0f) * Transform::DEG_RAD;
    if (frameStart != 0) {
        if (_prevClockwise == 0) {
            rotation = _prevRotation + Transform::normalizeRadian(rotation - _prevRotation);
        } else {
            if (_prevClockwise > 0 ? rotation >= _prevRotation : rotation <= _prevRotation) {
                _prevClockwise = _prevClockwise > 0 ? _prevClockwise - 1 : _prevClockwise + 1;
            }

            rotation = _prevRotation + rotation - _prevRotation + Transform::PI_D * _prevClockwise;
        }
    }

    _prevClockwise = _getNumber(rawData, CLOCK_WISE, 0.0f);
    _prevRotation = rotation;
    //
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);
    auto frameFloatOffset = _frameFloatArray.size();
    _frameFloatArray.resize(_frameFloatArray.size() + 2);
    _frameFloatArray[frameFloatOffset++] = rotation;
    _frameFloatArray[frameFloatOffset++] = _getNumber(rawData, SKEW, 0.0f) * Transform::DEG_RAD;

    return frameOffset;
}

unsigned JSONDataParser::_parseBoneScaleFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);

    auto frameFloatOffset = _frameFloatArray.size();
    _frameFloatArray.resize(_frameFloatArray.size() + 2);
    _frameFloatArray[frameFloatOffset++] = _getNumber(rawData, X, 1.0f);
    _frameFloatArray[frameFloatOffset++] = _getNumber(rawData, Y, 1.0f);

    return frameOffset;
}

unsigned JSONDataParser::_parseSlotDisplayFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseFrame(rawData, frameStart, frameCount);

    _frameArray.resize(_frameArray.size() + 1);

    if (rawData.HasMember(VALUE)) {
        _frameArray[frameOffset + 1] = _getNumber(rawData, VALUE, 0);
    } else {
        _frameArray[frameOffset + 1] = _getNumber(rawData, DISPLAY_INDEX, 0);
    }

    _parseActionDataInFrame(rawData, frameStart, _slot->parent, _slot);

    return frameOffset;
}

unsigned JSONDataParser::_parseSlotColorFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);
    auto colorOffset = -1;

    if (rawData.HasMember(VALUE) || rawData.HasMember(COLOR)) {
        const auto &rawColor = rawData.HasMember(VALUE) ? rawData[VALUE] : rawData[COLOR];
        if (
            rawColor.HasMember(ALPHA_MULTIPLIER) ||
            rawColor.HasMember(RED_MULTIPLIER) ||
            rawColor.HasMember(GREEN_MULTIPLIER) ||
            rawColor.HasMember(BLUE_MULTIPLIER) ||
            rawColor.HasMember(ALPHA_OFFSET) ||
            rawColor.HasMember(RED_OFFSET) ||
            rawColor.HasMember(GREEN_OFFSET) ||
            rawColor.HasMember(BLUE_OFFSET)) {
            _parseColorTransform(rawColor, _helpColorTransform);
            colorOffset = _intArray.size();
            _intArray.resize(_intArray.size() + 8);
            _intArray[colorOffset++] = _helpColorTransform.alphaMultiplier * 100;
            _intArray[colorOffset++] = _helpColorTransform.redMultiplier * 100;
            _intArray[colorOffset++] = _helpColorTransform.greenMultiplier * 100;
            _intArray[colorOffset++] = _helpColorTransform.blueMultiplier * 100;
            _intArray[colorOffset++] = _helpColorTransform.alphaOffset;
            _intArray[colorOffset++] = _helpColorTransform.redOffset;
            _intArray[colorOffset++] = _helpColorTransform.greenOffset;
            _intArray[colorOffset++] = _helpColorTransform.blueOffset;
            colorOffset -= 8;
        }
    }

    if (colorOffset < 0) {
        if (_defaultColorOffset < 0) {
            _defaultColorOffset = colorOffset = _intArray.size();
            _intArray.resize(_intArray.size() + 8);
            _intArray[colorOffset++] = 100;
            _intArray[colorOffset++] = 100;
            _intArray[colorOffset++] = 100;
            _intArray[colorOffset++] = 100;
            _intArray[colorOffset++] = 0;
            _intArray[colorOffset++] = 0;
            _intArray[colorOffset++] = 0;
            _intArray[colorOffset++] = 0;
        }

        colorOffset = _defaultColorOffset;
    }

    const auto frameIntOffset = _frameIntArray.size();
    _frameIntArray.resize(_frameIntArray.size() + 1);
    _frameIntArray[frameIntOffset] = colorOffset;

    return frameOffset;
}

unsigned JSONDataParser::_parseSlotFFDFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameFloatOffset = _frameFloatArray.size();
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);
    const auto offset = _getNumber(rawData, OFFSET, (unsigned)0);
    const auto vertexCount = (unsigned)_intArray[_mesh->vertices.offset + (unsigned)BinaryOffset::MeshVertexCount];
    const auto meshName = _mesh->parent->name + "_" + _slot->name + "_" + _mesh->name;
    const auto weight = _mesh->vertices.weight;

    auto x = 0.0f;
    auto y = 0.0f;
    unsigned iB = 0;
    unsigned iV = 0;
    if (weight != nullptr) {
        const auto &rawSlotPose = *(_weightSlotPose[meshName]);

        _helpMatrixA.a = rawSlotPose[0].GetDouble();
        _helpMatrixA.b = rawSlotPose[1].GetDouble();
        _helpMatrixA.c = rawSlotPose[2].GetDouble();
        _helpMatrixA.d = rawSlotPose[3].GetDouble();
        _helpMatrixA.tx = rawSlotPose[4].GetDouble();
        _helpMatrixA.ty = rawSlotPose[5].GetDouble();

        _frameFloatArray.resize(_frameFloatArray.size() + weight->count * 2);
        iB = weight->offset + (unsigned)BinaryOffset::WeigthBoneIndices + weight->bones.size();
    } else {
        _frameFloatArray.resize(_frameFloatArray.size() + vertexCount * 2);
    }

    for (
        std::size_t i = 0;
        i < vertexCount * 2;
        i += 2) {
        if (!rawData.HasMember(VERTICES)) // Fill 0.
        {
            x = 0.0f;
            y = 0.0f;
        } else {
            if (i < offset || i - offset >= rawData[VERTICES].Size()) {
                x = 0.0f;
            } else {
                x = rawData[VERTICES][i - offset].GetDouble();
            }

            if (i + 1 < offset || i + 1 - offset >= rawData[VERTICES].Size()) {
                y = 0.0f;
            } else {
                y = rawData[VERTICES][i + 1 - offset].GetDouble();
            }
        }

        if (weight != nullptr) // If mesh is skinned, transform point by bone bind pose.
        {
            const auto &rawBonePoses = *(_weightBonePoses[meshName]);
            const unsigned vertexBoneCount = _intArray[iB++];

            _helpMatrixA.transformPoint(x, y, _helpPoint, true);
            x = _helpPoint.x;
            y = _helpPoint.y;

            for (std::size_t j = 0; j < vertexBoneCount; ++j) {
                const auto boneIndex = _intArray[iB++];
                const auto matrixOffset = boneIndex * 7 + 1;

                _helpMatrixB.a = rawBonePoses[matrixOffset + 0].GetDouble();
                _helpMatrixB.b = rawBonePoses[matrixOffset + 1].GetDouble();
                _helpMatrixB.c = rawBonePoses[matrixOffset + 2].GetDouble();
                _helpMatrixB.d = rawBonePoses[matrixOffset + 3].GetDouble();
                _helpMatrixB.tx = rawBonePoses[matrixOffset + 4].GetDouble();
                _helpMatrixB.ty = rawBonePoses[matrixOffset + 5].GetDouble();
                _helpMatrixB.invert();
                _helpMatrixB.transformPoint(x, y, _helpPoint, true);

                _frameFloatArray[frameFloatOffset + iV++] = _helpPoint.x;
                _frameFloatArray[frameFloatOffset + iV++] = _helpPoint.y;
            }
        } else {
            _frameFloatArray[frameFloatOffset + i] = x;
            _frameFloatArray[frameFloatOffset + i + 1] = y;
        }
    }

    if (frameStart == 0) {
        const auto frameIntOffset = _frameIntArray.size();
        _frameIntArray.resize(_frameIntArray.size() + 1 + 1 + 1 + 1 + 1);
        _frameIntArray[frameIntOffset + (unsigned)BinaryOffset::DeformVertexOffset] = _mesh->vertices.offset;
        _frameIntArray[frameIntOffset + (unsigned)BinaryOffset::DeformCount] = _frameFloatArray.size() - frameFloatOffset;
        _frameIntArray[frameIntOffset + (unsigned)BinaryOffset::DeformValueCount] = _frameFloatArray.size() - frameFloatOffset;
        _frameIntArray[frameIntOffset + (unsigned)BinaryOffset::DeformValueOffset] = 0;
        _frameIntArray[frameIntOffset + (unsigned)BinaryOffset::DeformFloatOffset] = frameFloatOffset;
        _timelineArray[_timeline->offset + (unsigned)BinaryOffset::TimelineFrameValueCount] = frameIntOffset - _animation->frameIntOffset;
    }

    return frameOffset;
}

unsigned JSONDataParser::_parseIKConstraintFrame(const rapidjson::Value &rawData, unsigned frameStart, unsigned frameCount) {
    const auto frameOffset = _parseTweenFrame(rawData, frameStart, frameCount);

    auto frameIntOffset = _frameIntArray.size();
    _frameIntArray.resize(_frameIntArray.size() + 2);
    _frameIntArray[frameIntOffset++] = _getBoolean(rawData, BEND_POSITIVE, true) ? 1 : 0;
    _frameIntArray[frameIntOffset++] = std::round(_getNumber(rawData, WEIGHT, 1.0f) * 100.0f);

    return frameOffset;
}

const std::vector<ActionData *> &JSONDataParser::_parseActionData(const rapidjson::Value &rawData, ActionType type, BoneData *bone, SlotData *slot) {
    static std::vector<ActionData *> actions;
    actions.clear();

    if (rawData.IsString()) {
        const auto action = BaseObject::borrowObject<ActionData>();
        action->type = type;
        action->name = rawData.GetString();
        action->bone = bone;
        action->slot = slot;
        actions.push_back(action);
    } else if (rawData.IsArray()) {
        for (std::size_t iA = 0, lA = rawData.Size(); iA < lA; ++iA) {
            const auto &rawAction = rawData[iA];
            const auto action = BaseObject::borrowObject<ActionData>();

            if (rawAction.HasMember(GOTO_AND_PLAY)) {
                action->type = ActionType::Play;
                action->name = _getString(rawAction, GOTO_AND_PLAY, "");
            } else {
                if (rawAction.HasMember(TYPE) && rawAction[TYPE].IsString()) {
                    action->type = _getActionType(rawAction[TYPE].GetString());
                } else {
                    action->type = (ActionType)_getNumber(rawAction, TYPE, (int)type);
                }

                action->name = _getString(rawAction, NAME, "");
            }

            if (rawAction.HasMember(BONE)) {
                const auto &boneName = _getString(rawAction, BONE, "");
                action->bone = _armature->getBone(boneName);
            } else {
                action->bone = bone;
            }

            if (rawAction.HasMember(SLOT)) {
                const auto &slotName = _getString(rawAction, SLOT, "");
                action->slot = _armature->getSlot(slotName);
            } else {
                action->slot = slot;
            }

            if (rawAction.HasMember(INTS)) {
                if (action->data == nullptr) {
                    action->data = BaseObject::borrowObject<UserData>();
                }

                const auto &rawInts = rawAction[INTS];
                for (std::size_t i = 0, l = rawInts.Size(); i < l; ++i) {
                    action->data->addInt(rawInts[i].GetInt());
                }
            }

            if (rawAction.HasMember(FLOATS)) {
                if (action->data == nullptr) {
                    action->data = BaseObject::borrowObject<UserData>();
                }

                const auto &rawFloats = rawAction[FLOATS];
                for (std::size_t i = 0, l = rawFloats.Size(); i < l; ++i) {
                    action->data->addFloat(rawFloats[i].GetDouble());
                }
            }

            if (rawAction.HasMember(STRINGS)) {
                if (action->data == nullptr) {
                    action->data = BaseObject::borrowObject<UserData>();
                }

                const auto &rawStrings = rawAction[STRINGS];
                for (std::size_t i = 0, l = rawStrings.Size(); i < l; ++i) {
                    action->data->addString(rawStrings[i].GetString());
                }
            }

            actions.push_back(action);
        }
    }

    return actions;
}

void JSONDataParser::_parseTransform(const rapidjson::Value &rawData, Transform &transform, float scale) {
    transform.x = _getNumber(rawData, X, 0.0f) * scale;
    transform.y = _getNumber(rawData, Y, 0.0f) * scale;

    if (rawData.HasMember(ROTATE) || rawData.HasMember(SKEW)) {
        transform.rotation = Transform::normalizeRadian(_getNumber(rawData, ROTATE, 0.0f) * Transform::DEG_RAD);
        transform.skew = Transform::normalizeRadian(_getNumber(rawData, SKEW, 0.0f) * Transform::DEG_RAD);
    } else if (rawData.HasMember(SKEW_X) || rawData.HasMember(SKEW_Y)) {
        transform.rotation = Transform::normalizeRadian(_getNumber(rawData, SKEW_Y, 0.0f) * Transform::DEG_RAD);
        transform.skew = Transform::normalizeRadian(_getNumber(rawData, SKEW_X, 0.0f) * Transform::DEG_RAD) - transform.rotation;
    }

    transform.scaleX = _getNumber(rawData, SCALE_X, 1.0f);
    transform.scaleY = _getNumber(rawData, SCALE_Y, 1.0f);
}

void JSONDataParser::_parseColorTransform(const rapidjson::Value &rawData, ColorTransform &color) {
    color.alphaMultiplier = _getNumber(rawData, ALPHA_MULTIPLIER, (int)100) * 0.01f;
    color.redMultiplier = _getNumber(rawData, RED_MULTIPLIER, (int)100) * 0.01f;
    color.greenMultiplier = _getNumber(rawData, GREEN_MULTIPLIER, (int)100) * 0.01f;
    color.blueMultiplier = _getNumber(rawData, BLUE_MULTIPLIER, (int)100) * 0.01f;
    color.alphaOffset = _getNumber(rawData, ALPHA_OFFSET, (int)0);
    color.redOffset = _getNumber(rawData, RED_OFFSET, (int)0);
    color.greenOffset = _getNumber(rawData, GREEN_OFFSET, (int)0);
    color.blueOffset = _getNumber(rawData, BLUE_OFFSET, (int)0);
}

void JSONDataParser::_parseArray(const rapidjson::Value &rawData) {
    _intArray.clear();
    _floatArray.clear();
    _frameIntArray.clear();
    _frameFloatArray.clear();
    _frameArray.clear();
    _timelineArray.clear();
}

DragonBonesData *JSONDataParser::_parseDragonBonesData(const rapidjson::Value &rawData, float scale) {
    const auto &version = _getString(rawData, VERSION, "");
    const auto &compatibleVersion = _getString(rawData, COMPATIBLE_VERSION, "");

    if (
        indexOf(DATA_VERSIONS, version) >= 0 ||
        indexOf(DATA_VERSIONS, compatibleVersion) >= 0) {
        const auto data = BaseObject::borrowObject<DragonBonesData>();
        data->version = version;
        data->name = _getString(rawData, NAME, "");
        data->frameRate = _getNumber(rawData, FRAME_RATE, 24);

        if (data->frameRate == 0) // Data error.
        {
            data->frameRate = 24;
        }

        if (rawData.HasMember(ARMATURE)) {
            _data = data;
            _parseArray(rawData);

            const auto &rawArmatures = rawData[ARMATURE];
            for (std::size_t i = 0, l = rawArmatures.Size(); i < l; ++i) {
                data->addArmature(_parseArmature(rawArmatures[i], scale));
            }

            if (data->binary == nullptr) {
                // Align.
                if (fmod(_intArray.size(), 2) != 0) {
                    _intArray.push_back(0);
                }

                if (fmod(_frameIntArray.size(), 2) != 0) {
                    _frameIntArray.push_back(0);
                }

                if (fmod(_frameArray.size(), 2) != 0) {
                    _frameArray.push_back(0);
                }

                if (fmod(_timelineArray.size(), 2) != 0) {
                    _timelineArray.push_back(0);
                }

                const auto l1 = _intArray.size() * 2;
                const auto l2 = _floatArray.size() * 4;
                const auto l3 = _frameIntArray.size() * 2;
                const auto l4 = _frameFloatArray.size() * 4;
                const auto l5 = _frameArray.size() * 2;
                const auto l6 = _timelineArray.size() * 2;

                // NOTE: binary is freed in DragonBonesData::_onClear
                char *binary = static_cast<char *>(malloc((l1 + l2 + l3 + l4 + l5 + l6) * sizeof(char)));
                auto intArray = (int16_t *)binary;
                auto floatArray = (float *)(binary + l1);
                auto frameIntArray = (int16_t *)(binary + l1 + l2);
                auto frameFloatArray = (float *)(binary + l1 + l2 + l3);
                auto frameArray = (int16_t *)(binary + l1 + l2 + l3 + l4);
                auto timelineArray = (uint16_t *)(binary + l1 + l2 + l3 + l4 + l5);

                for (std::size_t i = 0, l = _intArray.size(); i < l; ++i) {
                    intArray[i] = _intArray[i];
                }

                for (std::size_t i = 0, l = _floatArray.size(); i < l; ++i) {
                    floatArray[i] = _floatArray[i];
                }

                for (std::size_t i = 0, l = _frameIntArray.size(); i < l; ++i) {
                    frameIntArray[i] = _frameIntArray[i];
                }

                for (std::size_t i = 0, l = _frameFloatArray.size(); i < l; ++i) {
                    frameFloatArray[i] = _frameFloatArray[i];
                }

                for (std::size_t i = 0, l = _frameArray.size(); i < l; ++i) {
                    frameArray[i] = _frameArray[i];
                }

                for (std::size_t i = 0, l = _timelineArray.size(); i < l; ++i) {
                    timelineArray[i] = _timelineArray[i];
                }

                data->binary = binary;
                data->intArray = intArray;
                data->floatArray = floatArray;
                data->frameIntArray = frameIntArray;
                data->frameFloatArray = frameFloatArray;
                data->frameArray = frameArray;
                data->timelineArray = timelineArray;
            }

            _defaultColorOffset = -1;
            _data = nullptr;
        }

        if (rawData.HasMember(TEXTURE_ATLAS)) {
            _rawTextureAtlases = (rapidjson::Value *)&(rawData[TEXTURE_ATLAS]);
        }

        return data;
    } else {
        DRAGONBONES_ASSERT(
            false,
            "Nonsupport data version: " + version + "\n" +
                "Please convert DragonBones data to support version.\n" +
                "Read more: https://github.com/DragonBones/Tools/");
    }

    return nullptr;
}

void JSONDataParser::_parseTextureAtlasData(const rapidjson::Value &rawData, TextureAtlasData &textureAtlasData, float scale) {
    textureAtlasData.format = _getTextureFormat(_getString(rawData, FORMAT, ""));
    textureAtlasData.width = _getNumber(rawData, WIDTH, (unsigned)0);
    textureAtlasData.height = _getNumber(rawData, HEIGHT, (unsigned)0);
    textureAtlasData.scale = scale == 1.0f ? 1.0f / _getNumber(rawData, SCALE, 1.0f) : scale;
    textureAtlasData.name = _getString(rawData, NAME, "");
    textureAtlasData.imagePath = _getString(rawData, IMAGE_PATH, "");

    if (rawData.HasMember(SUB_TEXTURE)) {
        const auto &rawTextures = rawData[SUB_TEXTURE];
        for (std::size_t i = 0, l = rawTextures.Size(); i < l; ++i) {
            const auto &rawTexture = rawTextures[i];
            const auto textureData = textureAtlasData.createTexture();
            textureData->rotated = _getBoolean(rawTexture, ROTATED, false);
            textureData->name = _getString(rawTexture, NAME, "");
            textureData->region.x = _getNumber(rawTexture, X, 0.0f);
            textureData->region.y = _getNumber(rawTexture, Y, 0.0f);
            textureData->region.width = _getNumber(rawTexture, WIDTH, 0.0f);
            textureData->region.height = _getNumber(rawTexture, HEIGHT, 0.0f);

            const auto frameWidth = _getNumber(rawTexture, FRAME_WIDTH, -1.0f);
            const auto frameHeight = _getNumber(rawTexture, FRAME_HEIGHT, -1.0f);
            if (frameWidth > 0.0f && frameHeight > 0.0f) {
                textureData->frame = TextureData::createRectangle();
                textureData->frame->x = _getNumber(rawTexture, FRAME_X, 0.0f);
                textureData->frame->y = _getNumber(rawTexture, FRAME_Y, 0.0f);
                textureData->frame->width = frameWidth;
                textureData->frame->height = frameHeight;
            }

            textureAtlasData.addTexture(textureData);
        }
    }
}

DragonBonesData *JSONDataParser::parseDragonBonesData(const char *rawData, float scale) {
    DRAGONBONES_ASSERT(rawData != nullptr, "");

    rapidjson::Document document;
    document.Parse(rawData);

    return _parseDragonBonesData(document, scale);
}

bool JSONDataParser::parseTextureAtlasData(const char *rawData, TextureAtlasData &textureAtlasData, float scale) {
    if (rawData == nullptr) {
        if (_rawTextureAtlases == nullptr || _rawTextureAtlases->Empty()) {
            return false;
        }

        const auto &rawTextureAtlas = (*_rawTextureAtlases)[_rawTextureAtlasIndex++];
        _parseTextureAtlasData(rawTextureAtlas, textureAtlasData, scale);
        if (_rawTextureAtlasIndex >= _rawTextureAtlases->Size()) {
            _rawTextureAtlasIndex = 0;
            _rawTextureAtlases = nullptr;
        }

        return true;
    }

    rapidjson::Document document;
    document.Parse(rawData);
    _parseTextureAtlasData(document, textureAtlasData, scale);

    return true;
}

DRAGONBONES_NAMESPACE_END
