/******************************************************************************
 * Spine Runtimes License Agreement
 * Last updated January 1, 2020. Replaces all prior versions.
 *
 * Copyright (c) 2013-2020, Esoteric Software LLC
 *
 * Integration of the Spine Runtimes into software or otherwise creating
 * derivative works of the Spine Runtimes is permitted under the terms and
 * conditions of Section 2 of the Spine Editor License Agreement:
 * http://esotericsoftware.com/spine-editor-license
 *
 * Otherwise, it is permitted to integrate the Spine Runtimes into software
 * or otherwise create derivative works of the Spine Runtimes (collectively,
 * "Products"), provided that each user of the Products must obtain their own
 * Spine Editor license and redistribution of the Products in any form must
 * include this license and copyright notice.
 *
 * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
 * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

#ifdef SPINE_UE4
    #include "SpinePluginPrivatePCH.h"
#endif

#include <spine/SkeletonJson.h>

#include <spine/Atlas.h>
#include <spine/AtlasAttachmentLoader.h>
#include <spine/CurveTimeline.h>
#include <spine/Json.h>
#include <spine/LinkedMesh.h>
#include <spine/SkeletonData.h>
#include <spine/VertexAttachment.h>

#include <spine/AttachmentTimeline.h>
#include <spine/AttachmentType.h>
#include <spine/BoneData.h>
#include <spine/BoundingBoxAttachment.h>
#include <spine/ClippingAttachment.h>
#include <spine/ColorTimeline.h>
#include <spine/ContainerUtil.h>
#include <spine/DeformTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/Event.h>
#include <spine/EventData.h>
#include <spine/EventTimeline.h>
#include <spine/IkConstraintData.h>
#include <spine/IkConstraintTimeline.h>
#include <spine/MeshAttachment.h>
#include <spine/PathAttachment.h>
#include <spine/PathConstraintData.h>
#include <spine/PathConstraintMixTimeline.h>
#include <spine/PathConstraintPositionTimeline.h>
#include <spine/PathConstraintSpacingTimeline.h>
#include <spine/PointAttachment.h>
#include <spine/RegionAttachment.h>
#include <spine/RotateTimeline.h>
#include <spine/ScaleTimeline.h>
#include <spine/ShearTimeline.h>
#include <spine/Skin.h>
#include <spine/SlotData.h>
#include <spine/TransformConstraintData.h>
#include <spine/TransformConstraintTimeline.h>
#include <spine/TranslateTimeline.h>
#include <spine/TwoColorTimeline.h>
#include <spine/Vertices.h>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
    #define strdup _strdup
#endif

using namespace spine;

SkeletonJson::SkeletonJson(Atlas *atlas) : _attachmentLoader(new (__FILE__, __LINE__) AtlasAttachmentLoader(atlas)),
                                           _scale(1),
                                           _ownsLoader(true) {}

SkeletonJson::SkeletonJson(AttachmentLoader *attachmentLoader) : _attachmentLoader(attachmentLoader), _scale(1), _ownsLoader(false) {
    assert(_attachmentLoader != NULL);
}

SkeletonJson::~SkeletonJson() {
    ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);

    if (_ownsLoader) delete _attachmentLoader;
}

SkeletonData *SkeletonJson::readSkeletonDataFile(const String &path) {
    int length;
    SkeletonData *skeletonData;
    const char *json = SpineExtension::readFile(path, &length);
    if (length == 0 || !json) {
        setError(NULL, "Unable to read skeleton file: ", path);
        return NULL;
    }

    skeletonData = readSkeletonData(json);

    SpineExtension::free(json, __FILE__, __LINE__);

    return skeletonData;
}

SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
    int i, ii;
    SkeletonData *skeletonData;
    Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events;

    _error = "";
    _linkedMeshes.clear();

    root = new Json(json);

    if (!root) {
        setError(NULL, "Invalid skeleton JSON: ", Json::getError());
        return NULL;
    }

    skeletonData = new (__FILE__, __LINE__) SkeletonData();

    skeleton = Json::getItem(root, "skeleton");
    if (skeleton) {
        skeletonData->_hash = Json::getString(skeleton, "hash", 0);
        skeletonData->_version = Json::getString(skeleton, "spine", 0);
        skeletonData->_x = Json::getFloat(skeleton, "x", 0);
        skeletonData->_y = Json::getFloat(skeleton, "y", 0);
        skeletonData->_width = Json::getFloat(skeleton, "width", 0);
        skeletonData->_height = Json::getFloat(skeleton, "height", 0);
        skeletonData->_fps = Json::getFloat(skeleton, "fps", 30);
        skeletonData->_audioPath = Json::getString(skeleton, "audio", 0);
        skeletonData->_imagesPath = Json::getString(skeleton, "images", 0);
    }

    /* Bones. */
    bones = Json::getItem(root, "bones");
    skeletonData->_bones.setSize(bones->_size, 0);
    int bonesCount = 0;
    for (boneMap = bones->_child, i = 0; boneMap; boneMap = boneMap->_next, ++i) {
        BoneData *data;
        const char *transformMode;

        BoneData *parent = 0;
        const char *parentName = Json::getString(boneMap, "parent", 0);
        if (parentName) {
            parent = skeletonData->findBone(parentName);
            if (!parent) {
                delete skeletonData;
                setError(root, "Parent bone not found: ", parentName);
                return NULL;
            }
        }

        data = new (__FILE__, __LINE__) BoneData(bonesCount, Json::getString(boneMap, "name", 0), parent);

        data->_length = Json::getFloat(boneMap, "length", 0) * _scale;
        data->_x = Json::getFloat(boneMap, "x", 0) * _scale;
        data->_y = Json::getFloat(boneMap, "y", 0) * _scale;
        data->_rotation = Json::getFloat(boneMap, "rotation", 0);
        data->_scaleX = Json::getFloat(boneMap, "scaleX", 1);
        data->_scaleY = Json::getFloat(boneMap, "scaleY", 1);
        data->_shearX = Json::getFloat(boneMap, "shearX", 0);
        data->_shearY = Json::getFloat(boneMap, "shearY", 0);
        transformMode = Json::getString(boneMap, "transform", "normal");
        data->_transformMode = TransformMode_Normal;
        if (strcmp(transformMode, "normal") == 0)
            data->_transformMode = TransformMode_Normal;
        else if (strcmp(transformMode, "onlyTranslation") == 0)
            data->_transformMode = TransformMode_OnlyTranslation;
        else if (strcmp(transformMode, "noRotationOrReflection") == 0)
            data->_transformMode = TransformMode_NoRotationOrReflection;
        else if (strcmp(transformMode, "noScale") == 0)
            data->_transformMode = TransformMode_NoScale;
        else if (strcmp(transformMode, "noScaleOrReflection") == 0)
            data->_transformMode = TransformMode_NoScaleOrReflection;
        data->_skinRequired = Json::getBoolean(boneMap, "skin", false);

        skeletonData->_bones[i] = data;
        bonesCount++;
    }

    /* Slots. */
    slots = Json::getItem(root, "slots");
    if (slots) {
        Json *slotMap;
        skeletonData->_slots.ensureCapacity(slots->_size);
        skeletonData->_slots.setSize(slots->_size, 0);
        for (slotMap = slots->_child, i = 0; slotMap; slotMap = slotMap->_next, ++i) {
            SlotData *data;
            const char *color;
            const char *dark;
            Json *item;

            const char *boneName = Json::getString(slotMap, "bone", 0);
            BoneData *boneData = skeletonData->findBone(boneName);
            if (!boneData) {
                delete skeletonData;
                setError(root, "Slot bone not found: ", boneName);
                return NULL;
            }

            data = new (__FILE__, __LINE__) SlotData(i, Json::getString(slotMap, "name", 0), *boneData);

            color = Json::getString(slotMap, "color", 0);
            if (color) {
                Color &c = data->getColor();
                c.r = toColor(color, 0);
                c.g = toColor(color, 1);
                c.b = toColor(color, 2);
                c.a = toColor(color, 3);
            }

            dark = Json::getString(slotMap, "dark", 0);
            if (dark) {
                Color &darkColor = data->getDarkColor();
                darkColor.r = toColor(dark, 0);
                darkColor.g = toColor(dark, 1);
                darkColor.b = toColor(dark, 2);
                darkColor.a = 1;
                data->setHasDarkColor(true);
            }

            item = Json::getItem(slotMap, "attachment");
            if (item) data->setAttachmentName(item->_valueString);

            item = Json::getItem(slotMap, "blend");
            if (item) {
                if (strcmp(item->_valueString, "additive") == 0)
                    data->_blendMode = BlendMode_Additive;
                else if (strcmp(item->_valueString, "multiply") == 0)
                    data->_blendMode = BlendMode_Multiply;
                else if (strcmp(item->_valueString, "screen") == 0)
                    data->_blendMode = BlendMode_Screen;
            }

            skeletonData->_slots[i] = data;
        }
    }

    /* IK constraints. */
    ik = Json::getItem(root, "ik");
    if (ik) {
        Json *constraintMap;
        skeletonData->_ikConstraints.ensureCapacity(ik->_size);
        skeletonData->_ikConstraints.setSize(ik->_size, 0);
        for (constraintMap = ik->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) {
            const char *targetName;

            IkConstraintData *data = new (__FILE__, __LINE__) IkConstraintData(Json::getString(constraintMap, "name", 0));
            data->setOrder(Json::getInt(constraintMap, "order", 0));
            data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));

            boneMap = Json::getItem(constraintMap, "bones");
            data->_bones.ensureCapacity(boneMap->_size);
            data->_bones.setSize(boneMap->_size, 0);
            for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) {
                data->_bones[ii] = skeletonData->findBone(boneMap->_valueString);
                if (!data->_bones[ii]) {
                    delete skeletonData;
                    setError(root, "IK bone not found: ", boneMap->_valueString);
                    return NULL;
                }
            }

            targetName = Json::getString(constraintMap, "target", 0);
            data->_target = skeletonData->findBone(targetName);
            if (!data->_target) {
                delete skeletonData;
                setError(root, "Target bone not found: ", targetName);
                return NULL;
            }

            data->_mix = Json::getFloat(constraintMap, "mix", 1);
            data->_softness = Json::getFloat(constraintMap, "softness", 0) * _scale;
            data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1;
            data->_compress = Json::getInt(constraintMap, "compress", 0) ? true : false;
            data->_stretch = Json::getInt(constraintMap, "stretch", 0) ? true : false;
            data->_uniform = Json::getInt(constraintMap, "uniform", 0) ? true : false;

            skeletonData->_ikConstraints[i] = data;
        }
    }

    /* Transform constraints. */
    transform = Json::getItem(root, "transform");
    if (transform) {
        Json *constraintMap;
        skeletonData->_transformConstraints.ensureCapacity(transform->_size);
        skeletonData->_transformConstraints.setSize(transform->_size, 0);
        for (constraintMap = transform->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) {
            const char *name;

            TransformConstraintData *data = new (__FILE__, __LINE__) TransformConstraintData(Json::getString(constraintMap, "name", 0));
            data->setOrder(Json::getInt(constraintMap, "order", 0));
            data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));

            boneMap = Json::getItem(constraintMap, "bones");
            data->_bones.ensureCapacity(boneMap->_size);
            data->_bones.setSize(boneMap->_size, 0);
            for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) {
                data->_bones[ii] = skeletonData->findBone(boneMap->_valueString);
                if (!data->_bones[ii]) {
                    delete skeletonData;
                    setError(root, "Transform bone not found: ", boneMap->_valueString);
                    return NULL;
                }
            }

            name = Json::getString(constraintMap, "target", 0);
            data->_target = skeletonData->findBone(name);
            if (!data->_target) {
                delete skeletonData;
                setError(root, "Target bone not found: ", name);
                return NULL;
            }

            data->_local = Json::getInt(constraintMap, "local", 0) ? true : false;
            data->_relative = Json::getInt(constraintMap, "relative", 0) ? true : false;
            data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0);
            data->_offsetX = Json::getFloat(constraintMap, "x", 0) * _scale;
            data->_offsetY = Json::getFloat(constraintMap, "y", 0) * _scale;
            data->_offsetScaleX = Json::getFloat(constraintMap, "scaleX", 0);
            data->_offsetScaleY = Json::getFloat(constraintMap, "scaleY", 0);
            data->_offsetShearY = Json::getFloat(constraintMap, "shearY", 0);

            data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1);
            data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1);
            data->_scaleMix = Json::getFloat(constraintMap, "scaleMix", 1);
            data->_shearMix = Json::getFloat(constraintMap, "shearMix", 1);

            skeletonData->_transformConstraints[i] = data;
        }
    }

    /* Path constraints */
    path = Json::getItem(root, "path");
    if (path) {
        Json *constraintMap;
        skeletonData->_pathConstraints.ensureCapacity(path->_size);
        skeletonData->_pathConstraints.setSize(path->_size, 0);
        for (constraintMap = path->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) {
            const char *name;
            const char *item;

            PathConstraintData *data = new (__FILE__, __LINE__) PathConstraintData(Json::getString(constraintMap, "name", 0));
            data->setOrder(Json::getInt(constraintMap, "order", 0));
            data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));

            boneMap = Json::getItem(constraintMap, "bones");
            data->_bones.ensureCapacity(boneMap->_size);
            data->_bones.setSize(boneMap->_size, 0);
            for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) {
                data->_bones[ii] = skeletonData->findBone(boneMap->_valueString);
                if (!data->_bones[ii]) {
                    delete skeletonData;
                    setError(root, "Path bone not found: ", boneMap->_valueString);
                    return NULL;
                }
            }

            name = Json::getString(constraintMap, "target", 0);
            data->_target = skeletonData->findSlot(name);
            if (!data->_target) {
                delete skeletonData;
                setError(root, "Target slot not found: ", name);
                return NULL;
            }

            item = Json::getString(constraintMap, "positionMode", "percent");
            if (strcmp(item, "fixed") == 0) {
                data->_positionMode = PositionMode_Fixed;
            } else if (strcmp(item, "percent") == 0) {
                data->_positionMode = PositionMode_Percent;
            }

            item = Json::getString(constraintMap, "spacingMode", "length");
            if (strcmp(item, "length") == 0)
                data->_spacingMode = SpacingMode_Length;
            else if (strcmp(item, "fixed") == 0)
                data->_spacingMode = SpacingMode_Fixed;
            else if (strcmp(item, "percent") == 0)
                data->_spacingMode = SpacingMode_Percent;

            item = Json::getString(constraintMap, "rotateMode", "tangent");
            if (strcmp(item, "tangent") == 0)
                data->_rotateMode = RotateMode_Tangent;
            else if (strcmp(item, "chain") == 0)
                data->_rotateMode = RotateMode_Chain;
            else if (strcmp(item, "chainScale") == 0)
                data->_rotateMode = RotateMode_ChainScale;

            data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0);
            data->_position = Json::getFloat(constraintMap, "position", 0);
            if (data->_positionMode == PositionMode_Fixed) data->_position *= _scale;
            data->_spacing = Json::getFloat(constraintMap, "spacing", 0);
            if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) data->_spacing *= _scale;
            data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1);
            data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1);

            skeletonData->_pathConstraints[i] = data;
        }
    }

    /* Skins. */
    skins = Json::getItem(root, "skins");
    if (skins) {
        Json *skinMap;
        skeletonData->_skins.ensureCapacity(skins->_size);
        skeletonData->_skins.setSize(skins->_size, 0);
        int skinsIndex = 0;
        for (skinMap = skins->_child, i = 0; skinMap; skinMap = skinMap->_next, ++i) {
            Json *attachmentsMap;
            Json *curves;

            Skin *skin = nullptr;
            auto skinName = Json::getString(skinMap, "name", "");
            if (strlen(skinName) == 0) {
                skinName = skinMap->_name;
            }
            skin = new (__FILE__, __LINE__) Skin(skinName);

            Json *item = Json::getItem(skinMap, "bones");
            if (item) {
                for (item = item->_child; item; item = item->_next) {
                    BoneData *data = skeletonData->findBone(item->_valueString);
                    if (!data) {
                        delete skeletonData;
                        setError(root, String("Skin bone not found: "), item->_valueString);
                        return NULL;
                    }
                    skin->getBones().add(data);
                }
            }

            item = Json::getItem(skinMap, "ik");
            if (item) {
                for (item = item->_child; item; item = item->_next) {
                    IkConstraintData *data = skeletonData->findIkConstraint(item->_valueString);
                    if (!data) {
                        delete skeletonData;
                        setError(root, String("Skin IK constraint not found: "), item->_valueString);
                        return NULL;
                    }
                    skin->getConstraints().add(data);
                }
            }

            item = Json::getItem(skinMap, "transform");
            if (item) {
                for (item = item->_child; item; item = item->_next) {
                    TransformConstraintData *data = skeletonData->findTransformConstraint(item->_valueString);
                    if (!data) {
                        delete skeletonData;
                        setError(root, String("Skin transform constraint not found: "), item->_valueString);
                        return NULL;
                    }
                    skin->getConstraints().add(data);
                }
            }

            item = Json::getItem(skinMap, "path");
            if (item) {
                for (item = item->_child; item; item = item->_next) {
                    PathConstraintData *data = skeletonData->findPathConstraint(item->_valueString);
                    if (!data) {
                        delete skeletonData;
                        setError(root, String("Skin path constraint not found: "), item->_valueString);
                        return NULL;
                    }
                    skin->getConstraints().add(data);
                }
            }

            skeletonData->_skins[skinsIndex++] = skin;
            if (strcmp(skinName, "default") == 0) {
                skeletonData->_defaultSkin = skin;
            }

            auto attachmentsData = Json::getItem(skinMap, "attachments");
            if (!attachmentsData) {
                attachmentsData = skinMap;
            }
            for (attachmentsMap = attachmentsData->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) {
                SlotData *slot = skeletonData->findSlot(attachmentsMap->_name);
                Json *attachmentMap;

                for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) {
                    Attachment *attachment = NULL;
                    const char *skinAttachmentName = attachmentMap->_name;
                    const char *attachmentName = Json::getString(attachmentMap, "name", skinAttachmentName);
                    const char *attachmentPath = Json::getString(attachmentMap, "path", attachmentName);
                    const char *color;
                    Json *entry;

                    const char *typeString = Json::getString(attachmentMap, "type", "region");
                    AttachmentType type;
                    if (strcmp(typeString, "region") == 0)
                        type = AttachmentType_Region;
                    else if (strcmp(typeString, "mesh") == 0)
                        type = AttachmentType_Mesh;
                    else if (strcmp(typeString, "linkedmesh") == 0)
                        type = AttachmentType_Linkedmesh;
                    else if (strcmp(typeString, "boundingbox") == 0)
                        type = AttachmentType_Boundingbox;
                    else if (strcmp(typeString, "path") == 0)
                        type = AttachmentType_Path;
                    else if (strcmp(typeString, "clipping") == 0)
                        type = AttachmentType_Clipping;
                    else if (strcmp(typeString, "point") == 0)
                        type = AttachmentType_Point;
                    else {
                        delete skeletonData;
                        setError(root, "Unknown attachment type: ", typeString);
                        return NULL;
                    }

                    switch (type) {
                        case AttachmentType_Region: {
                            attachment = _attachmentLoader->newRegionAttachment(*skin, attachmentName, attachmentPath);
                            if (!attachment) {
                                // delete skeletonData;
                                // setError(root, "Error reading attachment: ", skinAttachmentName);
                                // return NULL;
                                continue;
                            }

                            RegionAttachment *region = static_cast<RegionAttachment *>(attachment);
                            region->_path = attachmentPath;

                            region->_x = Json::getFloat(attachmentMap, "x", 0) * _scale;
                            region->_y = Json::getFloat(attachmentMap, "y", 0) * _scale;
                            region->_scaleX = Json::getFloat(attachmentMap, "scaleX", 1);
                            region->_scaleY = Json::getFloat(attachmentMap, "scaleY", 1);
                            region->_rotation = Json::getFloat(attachmentMap, "rotation", 0);
                            region->_width = Json::getFloat(attachmentMap, "width", 32) * _scale;
                            region->_height = Json::getFloat(attachmentMap, "height", 32) * _scale;

                            color = Json::getString(attachmentMap, "color", 0);
                            if (color) {
                                region->getColor().r = toColor(color, 0);
                                region->getColor().g = toColor(color, 1);
                                region->getColor().b = toColor(color, 2);
                                region->getColor().a = toColor(color, 3);
                            }

                            region->updateOffset();
                            _attachmentLoader->configureAttachment(region);
                            break;
                        }
                        case AttachmentType_Mesh:
                        case AttachmentType_Linkedmesh: {
                            attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath);

                            if (!attachment) {
                                // delete skeletonData;
                                // setError(root, "Error reading attachment: ", skinAttachmentName);
                                // return NULL;
                                continue;
                            }

                            MeshAttachment *mesh = static_cast<MeshAttachment *>(attachment);
                            mesh->_path = attachmentPath;

                            color = Json::getString(attachmentMap, "color", 0);
                            if (color) {
                                mesh->getColor().r = toColor(color, 0);
                                mesh->getColor().g = toColor(color, 1);
                                mesh->getColor().b = toColor(color, 2);
                                mesh->getColor().a = toColor(color, 3);
                            }

                            mesh->_width = Json::getFloat(attachmentMap, "width", 32) * _scale;
                            mesh->_height = Json::getFloat(attachmentMap, "height", 32) * _scale;

                            entry = Json::getItem(attachmentMap, "parent");
                            if (!entry) {
                                int verticesLength;
                                entry = Json::getItem(attachmentMap, "triangles");
                                mesh->_triangles.ensureCapacity(entry->_size);
                                mesh->_triangles.setSize(entry->_size, 0);
                                for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii)
                                    mesh->_triangles[ii] = (unsigned short)entry->_valueInt;

                                entry = Json::getItem(attachmentMap, "uvs");
                                verticesLength = entry->_size;
                                mesh->_regionUVs.ensureCapacity(verticesLength);
                                mesh->_regionUVs.setSize(verticesLength, 0);
                                for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii)
                                    mesh->_regionUVs[ii] = entry->_valueFloat;

                                readVertices(attachmentMap, mesh, verticesLength);

                                mesh->updateUVs();

                                mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0);

                                entry = Json::getItem(attachmentMap, "edges");
                                if (entry) {
                                    mesh->_edges.ensureCapacity(entry->_size);
                                    mesh->_edges.setSize(entry->_size, 0);
                                    for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii)
                                        mesh->_edges[ii] = entry->_valueInt;
                                }
                                _attachmentLoader->configureAttachment(mesh);
                            } else {
                                bool inheritDeform = Json::getInt(attachmentMap, "deform", 1) ? true : false;
                                LinkedMesh *linkedMesh = new (__FILE__, __LINE__) LinkedMesh(mesh,
                                                                                             String(Json::getString(attachmentMap, "skin", 0)), slot->getIndex(), String(entry->_valueString),
                                                                                             inheritDeform);
                                _linkedMeshes.add(linkedMesh);
                            }
                            break;
                        }
                        case AttachmentType_Boundingbox: {
                            attachment = _attachmentLoader->newBoundingBoxAttachment(*skin, attachmentName);

                            BoundingBoxAttachment *box = static_cast<BoundingBoxAttachment *>(attachment);

                            int vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1;
                            readVertices(attachmentMap, box, vertexCount);
                            _attachmentLoader->configureAttachment(attachment);
                            break;
                        }
                        case AttachmentType_Path: {
                            attachment = _attachmentLoader->newPathAttachment(*skin, attachmentName);

                            PathAttachment *pathAttatchment = static_cast<PathAttachment *>(attachment);

                            int vertexCount = 0;
                            pathAttatchment->_closed = Json::getInt(attachmentMap, "closed", 0) ? true : false;
                            pathAttatchment->_constantSpeed = Json::getInt(attachmentMap, "constantSpeed", 1) ? true : false;
                            vertexCount = Json::getInt(attachmentMap, "vertexCount", 0);
                            readVertices(attachmentMap, pathAttatchment, vertexCount << 1);

                            pathAttatchment->_lengths.ensureCapacity(vertexCount / 3);
                            pathAttatchment->_lengths.setSize(vertexCount / 3, 0);

                            curves = Json::getItem(attachmentMap, "lengths");
                            for (curves = curves->_child, ii = 0; curves; curves = curves->_next, ++ii)
                                pathAttatchment->_lengths[ii] = curves->_valueFloat * _scale;
                            _attachmentLoader->configureAttachment(attachment);
                            break;
                        }
                        case AttachmentType_Point: {
                            attachment = _attachmentLoader->newPointAttachment(*skin, attachmentName);

                            PointAttachment *point = static_cast<PointAttachment *>(attachment);

                            point->_x = Json::getFloat(attachmentMap, "x", 0) * _scale;
                            point->_y = Json::getFloat(attachmentMap, "y", 0) * _scale;
                            point->_rotation = Json::getFloat(attachmentMap, "rotation", 0);
                            _attachmentLoader->configureAttachment(attachment);
                            break;
                        }
                        case AttachmentType_Clipping: {
                            attachment = _attachmentLoader->newClippingAttachment(*skin, attachmentName);

                            ClippingAttachment *clip = static_cast<ClippingAttachment *>(attachment);

                            int vertexCount = 0;
                            const char *end = Json::getString(attachmentMap, "end", 0);
                            if (end) clip->_endSlot = skeletonData->findSlot(end);
                            vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1;
                            readVertices(attachmentMap, clip, vertexCount);
                            _attachmentLoader->configureAttachment(attachment);
                            break;
                        }
                    }

                    skin->setAttachment(slot->getIndex(), skinAttachmentName, attachment);
                }
            }
        }
    }

    /* Linked meshes. */
    int n = _linkedMeshes.size();
    for (i = 0; i < n; ++i) {
        LinkedMesh *linkedMesh = _linkedMeshes[i];
        Skin *skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin);
        if (skin == NULL) {
            // delete skeletonData;
            // setError(root, "Skin not found: ", linkedMesh->_skin.buffer());
            // return NULL;
            continue;
        }
        Attachment *parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent);
        if (parent == NULL) {
            // delete skeletonData;
            // setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer());
            // return NULL;
            continue;
        }
        linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent) : linkedMesh->_mesh;
        linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
        linkedMesh->_mesh->updateUVs();
        _attachmentLoader->configureAttachment(linkedMesh->_mesh);
    }
    ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);
    _linkedMeshes.clear();

    /* Events. */
    events = Json::getItem(root, "events");
    if (events) {
        Json *eventMap;
        skeletonData->_events.ensureCapacity(events->_size);
        skeletonData->_events.setSize(events->_size, 0);
        for (eventMap = events->_child, i = 0; eventMap; eventMap = eventMap->_next, ++i) {
            EventData *eventData = new (__FILE__, __LINE__) EventData(String(eventMap->_name));

            eventData->_intValue = Json::getInt(eventMap, "int", 0);
            eventData->_floatValue = Json::getFloat(eventMap, "float", 0);
            const char *stringValue = Json::getString(eventMap, "string", 0);
            eventData->_stringValue = stringValue;
            const char *audioPath = Json::getString(eventMap, "audio", 0);
            eventData->_audioPath = audioPath;
            if (audioPath) {
                eventData->_volume = Json::getFloat(eventMap, "volume", 1);
                eventData->_balance = Json::getFloat(eventMap, "balance", 0);
            }
            skeletonData->_events[i] = eventData;
        }
    }

    /* Animations. */
    animations = Json::getItem(root, "animations");
    if (animations) {
        Json *animationMap;
        skeletonData->_animations.ensureCapacity(animations->_size);
        skeletonData->_animations.setSize(animations->_size, 0);
        int animationsIndex = 0;
        for (animationMap = animations->_child; animationMap; animationMap = animationMap->_next) {
            Animation *animation = readAnimation(animationMap, skeletonData);
            if (!animation) {
                // delete skeletonData;
                // delete root;
                // return NULL;
                continue;
            }
            skeletonData->_animations[animationsIndex++] = animation;
        }
    }

    delete root;

    return skeletonData;
}

float SkeletonJson::toColor(const char *value, size_t index) {
    char digits[3];
    char *error;
    int color;

    if (index >= strlen(value) / 2) return -1;

    value += index * 2;

    digits[0] = *value;
    digits[1] = *(value + 1);
    digits[2] = '\0';
    color = (int)strtoul(digits, &error, 16);
    if (*error != 0) return -1;

    return color / (float)255;
}

void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameIndex) {
    Json *curve = Json::getItem(frame, "curve");
    if (!curve) return;
    if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0)
        timeline->setStepped(frameIndex);
    else {
        float c1 = Json::getFloat(frame, "curve", 0);
        float c2 = Json::getFloat(frame, "c2", 0);
        float c3 = Json::getFloat(frame, "c3", 1);
        float c4 = Json::getFloat(frame, "c4", 1);
        timeline->setCurve(frameIndex, c1, c2, c3, c4);
    }
}

Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
    Vector<Timeline *> timelines;
    float duration = 0;

    size_t frameIndex;
    Json *valueMap;
    int timelinesCount = 0;

    Json *bones = Json::getItem(root, "bones");
    Json *slots = Json::getItem(root, "slots");
    Json *ik = Json::getItem(root, "ik");
    Json *transform = Json::getItem(root, "transform");
    Json *paths = Json::getItem(root, "path");
    if (paths == NULL) {
        paths = Json::getItem(root, "paths");
    }
    Json *deform = Json::getItem(root, "deform");
    Json *drawOrder = Json::getItem(root, "drawOrder");
    Json *events = Json::getItem(root, "events");
    Json *boneMap, *slotMap, *constraintMap;
    if (!drawOrder) drawOrder = Json::getItem(root, "draworder");

    for (boneMap = bones ? bones->_child : NULL; boneMap; boneMap = boneMap->_next)
        timelinesCount += boneMap->_size;

    for (slotMap = slots ? slots->_child : NULL; slotMap; slotMap = slotMap->_next)
        timelinesCount += slotMap->_size;

    timelinesCount += ik ? ik->_size : 0;
    timelinesCount += transform ? transform->_size : 0;

    for (constraintMap = paths ? paths->_child : NULL; constraintMap; constraintMap = constraintMap->_next)
        timelinesCount += constraintMap->_size;

    for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next)
        for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next)
            timelinesCount += slotMap->_size;

    if (drawOrder) ++timelinesCount;

    if (events) ++timelinesCount;

    /** Slot timelines. */
    for (slotMap = slots ? slots->_child : 0; slotMap; slotMap = slotMap->_next) {
        Json *timelineMap;

        int slotIndex = skeletonData->findSlotIndex(slotMap->_name);
        if (slotIndex == -1) {
            ContainerUtil::cleanUpVectorOfPointers(timelines);
            setError(NULL, "Slot not found: ", slotMap->_name);
            return NULL;
        }

        for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
            if (strcmp(timelineMap->_name, "attachment") == 0) {
                AttachmentTimeline *timeline = new (__FILE__, __LINE__) AttachmentTimeline(timelineMap->_size);

                timeline->_slotIndex = slotIndex;

                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    Json *name = Json::getItem(valueMap, "name");
                    String attachmentName = name->_type == Json::JSON_NULL ? "" : name->_valueString;
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), attachmentName);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]);

            } else if (strcmp(timelineMap->_name, "color") == 0) {
                ColorTimeline *timeline = new (__FILE__, __LINE__) ColorTimeline(timelineMap->_size);

                timeline->_slotIndex = slotIndex;

                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    const char *s = Json::getString(valueMap, "color", 0);
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1),
                                       toColor(s, 2), toColor(s, 3));
                    readCurve(valueMap, timeline, frameIndex);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * ColorTimeline::ENTRIES]);

            } else if (strcmp(timelineMap->_name, "twoColor") == 0) {
                TwoColorTimeline *timeline = new (__FILE__, __LINE__) TwoColorTimeline(timelineMap->_size);

                timeline->_slotIndex = slotIndex;

                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    const char *s = Json::getString(valueMap, "light", 0);
                    const char *ds = Json::getString(valueMap, "dark", 0);
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1),
                                       toColor(s, 2), toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2));
                    readCurve(valueMap, timeline, frameIndex);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration,
                                         timeline->_frames[(timelineMap->_size - 1) * TwoColorTimeline::ENTRIES]);
            } else {
                ContainerUtil::cleanUpVectorOfPointers(timelines);
                setError(NULL, "Invalid timeline type for a slot: ", timelineMap->_name);
                return NULL;
            }
        }
    }

    /** Bone timelines. */
    for (boneMap = bones ? bones->_child : 0; boneMap; boneMap = boneMap->_next) {
        Json *timelineMap;

        int boneIndex = skeletonData->findBoneIndex(boneMap->_name);
        if (boneIndex == -1) {
            ContainerUtil::cleanUpVectorOfPointers(timelines);
            setError(NULL, "Bone not found: ", boneMap->_name);
            return NULL;
        }

        for (timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) {
            if (strcmp(timelineMap->_name, "rotate") == 0) {
                RotateTimeline *timeline = new (__FILE__, __LINE__) RotateTimeline(timelineMap->_size);

                timeline->_boneIndex = boneIndex;

                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "angle", 0));
                    readCurve(valueMap, timeline, frameIndex);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * RotateTimeline::ENTRIES]);
            } else {
                int isScale = strcmp(timelineMap->_name, "scale") == 0;
                int isTranslate = strcmp(timelineMap->_name, "translate") == 0;
                int isShear = strcmp(timelineMap->_name, "shear") == 0;
                if (isScale || isTranslate || isShear) {
                    float timelineScale = isTranslate ? _scale : 1;
                    float defaultValue = 0;
                    TranslateTimeline *timeline = 0;
                    if (isScale) {
                        timeline = new (__FILE__, __LINE__) ScaleTimeline(timelineMap->_size);
                        defaultValue = 1;
                    } else if (isTranslate) {
                        timeline = new (__FILE__, __LINE__) TranslateTimeline(timelineMap->_size);
                    } else if (isShear) {
                        timeline = new (__FILE__, __LINE__) ShearTimeline(timelineMap->_size);
                    }
                    timeline->_boneIndex = boneIndex;

                    for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                        timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
                                           Json::getFloat(valueMap, "x", defaultValue) * timelineScale,
                                           Json::getFloat(valueMap, "y", defaultValue) * timelineScale);
                        readCurve(valueMap, timeline, frameIndex);
                    }

                    timelines.add(timeline);
                    timelinesCount++;
                    duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * TranslateTimeline::ENTRIES]);
                } else {
                    ContainerUtil::cleanUpVectorOfPointers(timelines);
                    setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name);
                    return NULL;
                }
            }
        }
    }

    /** IK constraint timelines. */
    for (constraintMap = ik ? ik->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
        IkConstraintData *constraint = skeletonData->findIkConstraint(constraintMap->_name);
        IkConstraintTimeline *timeline = new (__FILE__, __LINE__) IkConstraintTimeline(constraintMap->_size);

        for (frameIndex = 0; frameIndex < skeletonData->_ikConstraints.size(); ++frameIndex) {
            if (constraint == skeletonData->_ikConstraints[frameIndex]) {
                timeline->_ikConstraintIndex = frameIndex;
                break;
            }
        }
        for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
            timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1),
                               Json::getFloat(valueMap, "softness", 0) * _scale, Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1,
                               Json::getInt(valueMap, "compress", 0) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false);
            readCurve(valueMap, timeline, frameIndex);
        }
        timelines.add(timeline);
        timelinesCount++;
        duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * IkConstraintTimeline::ENTRIES]);
    }

    /** Transform constraint timelines. */
    for (constraintMap = transform ? transform->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
        TransformConstraintData *constraint = skeletonData->findTransformConstraint(constraintMap->_name);
        TransformConstraintTimeline *timeline = new (__FILE__, __LINE__) TransformConstraintTimeline(constraintMap->_size);

        for (frameIndex = 0; frameIndex < skeletonData->_transformConstraints.size(); ++frameIndex) {
            if (constraint == skeletonData->_transformConstraints[frameIndex]) {
                timeline->_transformConstraintIndex = frameIndex;
                break;
            }
        }
        for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
            timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
                               Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1),
                               Json::getFloat(valueMap, "scaleMix", 1), Json::getFloat(valueMap, "shearMix", 1));
            readCurve(valueMap, timeline, frameIndex);
        }
        timelines.add(timeline);
        timelinesCount++;
        duration = MathUtil::max(duration, timeline->_frames[(constraintMap->_size - 1) * TransformConstraintTimeline::ENTRIES]);
    }

    /** Path constraint timelines. */
    for (constraintMap = paths ? paths->_child : 0; constraintMap; constraintMap = constraintMap->_next) {
        size_t constraintIndex = 0, i;
        Json *timelineMap;

        PathConstraintData *data = skeletonData->findPathConstraint(constraintMap->_name);
        if (!data) {
            ContainerUtil::cleanUpVectorOfPointers(timelines);
            setError(NULL, "Path constraint not found: ", constraintMap->_name);
            return NULL;
        }

        for (i = 0; i < skeletonData->_pathConstraints.size(); i++) {
            if (skeletonData->_pathConstraints[i] == data) {
                constraintIndex = i;
                break;
            }
        }

        for (timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) {
            const char *timelineName = timelineMap->_name;
            if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) {
                PathConstraintPositionTimeline *timeline;
                float timelineScale = 1;
                if (strcmp(timelineName, "spacing") == 0) {
                    timeline = new (__FILE__, __LINE__) PathConstraintSpacingTimeline(timelineMap->_size);

                    if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) {
                        timelineScale = _scale;
                    }
                } else {
                    timeline = new (__FILE__, __LINE__) PathConstraintPositionTimeline(timelineMap->_size);

                    if (data->_positionMode == PositionMode_Fixed) {
                        timelineScale = _scale;
                    }
                }

                timeline->_pathConstraintIndex = constraintIndex;
                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
                                       Json::getFloat(valueMap, timelineName, 0) * timelineScale);
                    readCurve(valueMap, timeline, frameIndex);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) *
                                                                     PathConstraintPositionTimeline::ENTRIES]);
            } else if (strcmp(timelineName, "mix") == 0) {
                PathConstraintMixTimeline *timeline = new (__FILE__, __LINE__) PathConstraintMixTimeline(timelineMap->_size);
                timeline->_pathConstraintIndex = constraintIndex;
                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
                                       Json::getFloat(valueMap, "rotateMix", 1),
                                       Json::getFloat(valueMap, "translateMix", 1));
                    readCurve(valueMap, timeline, frameIndex);
                }
                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintMixTimeline::ENTRIES]);
            }
        }
    }

    /** Deform timelines. */
    for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) {
        Skin *skin = skeletonData->findSkin(constraintMap->_name);
        for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) {
            int slotIndex = skeletonData->findSlotIndex(slotMap->_name);
            Json *timelineMap;
            for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) {
                DeformTimeline *timeline;
                int weighted, deformLength;

                Attachment *baseAttachment = skin->getAttachment(slotIndex, timelineMap->_name);

                if (!baseAttachment) {
                    ContainerUtil::cleanUpVectorOfPointers(timelines);
                    // setError(NULL, "Attachment not found: ", timelineMap->_name);
                    // return NULL;
                    continue;
                }

                VertexAttachment *attachment = static_cast<VertexAttachment *>(baseAttachment);

                weighted = attachment->_bones.size() != 0;
                Vector<float> &verts = attachment->_vertices;
                deformLength = weighted ? verts.size() / 3 * 2 : verts.size();

                timeline = new (__FILE__, __LINE__) DeformTimeline(timelineMap->_size);

                timeline->_slotIndex = slotIndex;
                timeline->_attachment = attachment;

                for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
                    Json *vertices = Json::getItem(valueMap, "vertices");
                    Vector<float> deformed;
                    if (!vertices) {
                        if (weighted) {
                            deformed.setSize(deformLength, 0);
                        } else {
                            deformed.clearAndAddAll(attachment->_vertices);
                        }
                    } else {
                        int v, start = Json::getInt(valueMap, "offset", 0);
                        Json *vertex;
                        deformed.setSize(deformLength, 0);
                        if (_scale == 1) {
                            for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) {
                                deformed[v] = vertex->_valueFloat;
                            }
                        } else {
                            for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) {
                                deformed[v] = vertex->_valueFloat * _scale;
                            }
                        }
                        if (!weighted) {
                            Vector<float> &verticesAttachment = attachment->_vertices;
                            for (v = 0; v < deformLength; ++v) {
                                deformed[v] += verticesAttachment[v];
                            }
                        }
                    }
                    timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), deformed);
                    readCurve(valueMap, timeline, frameIndex);
                }

                timelines.add(timeline);
                timelinesCount++;
                duration = MathUtil::max(duration, timeline->_frames[timelineMap->_size - 1]);
            }
        }
    }

    /** Draw order timeline. */
    if (drawOrder) {
        DrawOrderTimeline *timeline = new (__FILE__, __LINE__) DrawOrderTimeline(drawOrder->_size);

        for (valueMap = drawOrder->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
            int ii;
            Vector<int> drawOrder2;
            Json *offsets = Json::getItem(valueMap, "offsets");
            if (offsets) {
                Json *offsetMap;
                Vector<int> unchanged;
                unchanged.ensureCapacity(skeletonData->_slots.size() - offsets->_size);
                unchanged.setSize(skeletonData->_slots.size() - offsets->_size, 0);
                size_t originalIndex = 0, unchangedIndex = 0;

                drawOrder2.ensureCapacity(skeletonData->_slots.size());
                drawOrder2.setSize(skeletonData->_slots.size(), 0);
                for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; --ii)
                    drawOrder2[ii] = -1;

                for (offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) {
                    int slotIndex = skeletonData->findSlotIndex(Json::getString(offsetMap, "slot", 0));
                    if (slotIndex == -1) {
                        ContainerUtil::cleanUpVectorOfPointers(timelines);
                        setError(NULL, "Slot not found: ", Json::getString(offsetMap, "slot", 0));
                        return NULL;
                    }
                    /* Collect unchanged items. */
                    while (originalIndex != (size_t)slotIndex)
                        unchanged[unchangedIndex++] = originalIndex++;
                    /* Set changed items. */
                    drawOrder2[originalIndex + Json::getInt(offsetMap, "offset", 0)] = originalIndex;
                    originalIndex++;
                }
                /* Collect remaining unchanged items. */
                while (originalIndex < skeletonData->_slots.size())
                    unchanged[unchangedIndex++] = originalIndex++;
                /* Fill in unchanged items. */
                for (ii = (int)skeletonData->_slots.size() - 1; ii >= 0; ii--)
                    if (drawOrder2[ii] == -1) drawOrder2[ii] = unchanged[--unchangedIndex];
            }
            timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), drawOrder2);
        }
        timelines.add(timeline);
        timelinesCount++;
        duration = MathUtil::max(duration, timeline->_frames[drawOrder->_size - 1]);
    }

    /** Event timeline. */
    if (events) {
        EventTimeline *timeline = new (__FILE__, __LINE__) EventTimeline(events->_size);

        for (valueMap = events->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
            Event *event;
            EventData *eventData = skeletonData->findEvent(Json::getString(valueMap, "name", 0));
            if (!eventData) {
                ContainerUtil::cleanUpVectorOfPointers(timelines);
                setError(NULL, "Event not found: ", Json::getString(valueMap, "name", 0));
                return NULL;
            }

            event = new (__FILE__, __LINE__) Event(Json::getFloat(valueMap, "time", 0), *eventData);
            event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue);
            event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue);
            event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.buffer());
            if (!eventData->_audioPath.isEmpty()) {
                event->_volume = Json::getFloat(valueMap, "volume", 1);
                event->_balance = Json::getFloat(valueMap, "balance", 0);
            }
            timeline->setFrame(frameIndex, event);
        }
        timelines.add(timeline);
        timelinesCount++;
        duration = MathUtil::max(duration, timeline->_frames[events->_size - 1]);
    }

    return new (__FILE__, __LINE__) Animation(String(root->_name), timelines, duration);
}

void SkeletonJson::readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength) {
    Json *entry;
    size_t i, n, nn, entrySize;
    Vector<float> vertices;

    attachment->setWorldVerticesLength(verticesLength);

    entry = Json::getItem(attachmentMap, "vertices");
    entrySize = entry->_size;
    vertices.ensureCapacity(entrySize);
    vertices.setSize(entrySize, 0);
    for (entry = entry->_child, i = 0; entry; entry = entry->_next, ++i)
        vertices[i] = entry->_valueFloat;

    if (verticesLength == entrySize) {
        if (_scale != 1) {
            for (i = 0; i < entrySize; ++i)
                vertices[i] *= _scale;
        }

        attachment->getVertices().clearAndAddAll(vertices);
        return;
    }

    Vertices bonesAndWeights;
    bonesAndWeights._bones.ensureCapacity(verticesLength * 3);
    bonesAndWeights._vertices.ensureCapacity(verticesLength * 3 * 3);

    for (i = 0, n = entrySize; i < n;) {
        int boneCount = (int)vertices[i++];
        bonesAndWeights._bones.add(boneCount);
        for (nn = i + boneCount * 4; i < nn; i += 4) {
            bonesAndWeights._bones.add((int)vertices[i]);
            bonesAndWeights._vertices.add(vertices[i + 1] * _scale);
            bonesAndWeights._vertices.add(vertices[i + 2] * _scale);
            bonesAndWeights._vertices.add(vertices[i + 3]);
        }
    }

    attachment->getVertices().clearAndAddAll(bonesAndWeights._vertices);
    attachment->getBones().clearAndAddAll(bonesAndWeights._bones);
}

void SkeletonJson::setError(Json *root, const String &value1, const String &value2) {
    _error = String(value1).append(value2);
    delete root;
}
