text: /****************************************************************************
   Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
  
   http://www.cocos.com
  
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated engine source code (the "Software"), a limited,
    worldwide, royalty-free, non-assignable, revocable and non-exclusive license
   to use Cocos Creator solely to develop games on your target platforms. You shall
    not use Cocos Creator software for developing other software or tools that's
    used for developing games. You are not granted to publish, distribute,
    sublicense, and/or sell copies of Cocos Creator.
  
   The software or tools in this License Agreement are licensed, not sold.
   Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
  
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
   ****************************************************************************/
  
  (function(){
      if (window.sp === undefined || window.spine === undefined || window.middleware === undefined) return;
  
      sp.VertexEffectDelegate = spine.VertexEffectDelegate;
      jsb.generateGetSet(spine);
  
      // spine global time scale
      Object.defineProperty(sp, 'timeScale', {
          get () {
              return this._timeScale;
          },
          set (value) {
              this._timeScale = value;
              spine.SkeletonAnimation.setGlobalTimeScale(value);
          },
          configurable: true,
      });
      
      let _slotColor = cc.color(0, 0, 255, 255);
      let _boneColor = cc.color(255, 0, 0, 255);
      let _meshColor = cc.color(255, 255, 0, 255);
      let _originColor = cc.color(0, 255, 0, 255);
      
      let skeletonDataProto = sp.SkeletonData.prototype;
      let _gTextureIdx = 1;
      let _textureKeyMap = {};
      let _textureMap = new WeakMap();
  
      let skeletonDataMgr = spine.SkeletonDataMgr.getInstance();
      spine.skeletonDataMgr = skeletonDataMgr;
      skeletonDataMgr.setDestroyCallback(function (textureIndex) { 
          if (!textureIndex) return;
          let texKey = _textureKeyMap[textureIndex];
          if (texKey && _textureMap.has(texKey)) {
              _textureMap.delete(texKey);
              delete _textureKeyMap[textureIndex];
          }
      });
  
      let skeletonCacheMgr = spine.SkeletonCacheMgr.getInstance();
      spine.skeletonCacheMgr = skeletonCacheMgr;
      skeletonDataProto.destroy = function () {
          if (this._skeletonCache) {
              spine.disposeSkeletonData(this._uuid);
              this._jsbTextures = null;
              this._skeletonCache = null;
          }
          skeletonCacheMgr.removeSkeletonCache(this._uuid);
          cc.Asset.prototype.destroy.call(this);
      };
  
      skeletonDataProto.getRuntimeData = function () {
          if (!this._skeletonCache) {
              this.init();
          }
          return this._skeletonCache;
      };
  
      skeletonDataProto.init = function () {
          if (this._skeletonCache) return;
  
          let uuid = this._uuid;
          if (!uuid) {
              cc.errorID(7504);
              return;
          }
  
          let skeletonCache = spine.retainSkeletonData(uuid);
          if (skeletonCache) {
              this._skeletonCache = skeletonCache;
              return;
          }
  
          let atlasText = this.atlasText;
          if (!atlasText) {
              cc.errorID(7508, this.name);
              return;
          }
  
          let textures = this.textures;
          let textureNames = this.textureNames;
          if (!(textures && textures.length > 0 && textureNames && textureNames.length > 0)) {
              cc.errorID(7507, this.name);
              return;
          }
  
          let jsbTextures = {};
          for (let i = 0; i < textures.length; ++i) {
              let texture = textures[i];
              let textureIdx = this.recordTexture(texture);
              let spTex = new middleware.Texture2D();
              spTex.setRealTextureIndex(textureIdx);
              spTex.setPixelsWide(texture.width);
              spTex.setPixelsHigh(texture.height);
              spTex.setTexParamCallback(function(texIdx,minFilter,magFilter,wrapS,warpT){
                  let tex = this.getTextureByIndex(texIdx);
                  tex.setFilters(minFilter, magFilter);
                  tex.setWrapMode(wrapS, warpT);
              }.bind(this));
              spTex.setNativeTexture(texture.getImpl());
              jsbTextures[textureNames[i]] = spTex;
          }
          this._jsbTextures = jsbTextures;
  
          let filePath = null;
          if (this.skeletonJsonStr) {
              filePath = this.skeletonJsonStr;
          } else {
              filePath = cc.loader.md5Pipe ? cc.loader.md5Pipe.transformURL(this.nativeUrl) : this.nativeUrl;
          }
          this._skeletonCache = spine.initSkeletonData(uuid, filePath, atlasText, jsbTextures, this.scale);
      };
  
      skeletonDataProto.recordTexture = function (texture) {
          let index = _gTextureIdx;
          let texKey = _textureKeyMap[index] = {key:index};
          _textureMap.set(texKey, texture);
          _gTextureIdx++;
          return index;
      };
  
      skeletonDataProto.getTextureByIndex = function (textureIdx) {
          let texKey = _textureKeyMap[textureIdx];
          if (!texKey) return;
          return _textureMap.get(texKey);
      };
  
      let renderCompProto = cc.RenderComponent.prototype;
  
      let animation = spine.SkeletonAnimation.prototype;
      // The methods are added to be compatibility with old versions.
      animation.setCompleteListener = function (listener) {
          this._compeleteListener = listener;
          this.setCompleteListenerNative(function (trackEntry) {
              let loopCount = Math.floor(trackEntry.trackTime / trackEntry.animationEnd);
              this._compeleteListener && this._compeleteListener(trackEntry, loopCount);
          });
      };
  
      // Temporary solution before upgrade the Spine API
      animation.setAnimationListener = function (target, callback) {
          this._target = target;
          this._callback = callback;
  
          this.setStartListener(function (trackEntry) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.START, null, 0);
              }
          });
  
          this.setInterruptListener(function (trackEntry) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.INTERRUPT, null, 0);
              }
          });
  
          this.setEndListener(function (trackEntry) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.END, null, 0);
              }
          });
  
          this.setDisposeListener(function (trackEntry) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.DISPOSE, null, 0);
              }
          });
  
          this.setCompleteListener(function (trackEntry, loopCount) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.COMPLETE, null, loopCount);
              }
          });
  
          this.setEventListener(function (trackEntry, event) {
              if (this._target && this._callback) {
                  this._callback.call(this._target, this, trackEntry, sp.AnimationEventType.EVENT, event, 0);
              }
          });
      };
  
      sp.Skeleton._assembler = null;
      let skeleton = sp.Skeleton.prototype;
      const AnimationCacheMode = sp.Skeleton.AnimationCacheMode;
      Object.defineProperty(skeleton, 'paused', {
          get () {
              return this._paused || false;
          },
          set (value) {
              this._paused = value;
              if (this._nativeSkeleton) {
                  this._nativeSkeleton.paused(value);
              }
          }
      });
  
      Object.defineProperty(skeleton, "premultipliedAlpha", {
          get () {
              if (this._premultipliedAlpha === undefined){
                  return true;
              }
              return this._premultipliedAlpha;
          },
          set (value) {
              this._premultipliedAlpha = value;
              if (this._nativeSkeleton) {
                  this._nativeSkeleton.setOpacityModifyRGB(this._premultipliedAlpha);
              }
          }
      });
  
      Object.defineProperty(skeleton, "timeScale", {
          get () {
              if (this._timeScale === undefined) return 1.0;
              return this._timeScale;
          },
          set (value) {
              this._timeScale = value;
              if (this._nativeSkeleton) {
                  this._nativeSkeleton.setTimeScale(this._timeScale);
              }
          }
      });
  
      let _updateDebugDraw = skeleton._updateDebugDraw;
      skeleton._updateDebugDraw = function () {
          _updateDebugDraw.call(this);
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setDebugMeshEnabled(this.debugMesh);
              this._nativeSkeleton.setDebugSlotsEnabled(this.debugSlots);
              this._nativeSkeleton.setDebugBonesEnabled(this.debugBones);
          }
      };
  
      let _updateUseTint = skeleton._updateUseTint;
      skeleton._updateUseTint = function () {
          _updateUseTint.call(this);
          if (this._nativeSkeleton) {
              this._nativeSkeleton.setUseTint(this.useTint);
          }
          this._assembler && this._assembler.clearEffect();
      };
  
      let _updateBatch = skeleton._updateBatch;
      skeleton._updateBatch = function () {
          _updateBatch.call(this);
          if (this._nativeSkeleton) {
              this._nativeSkeleton.setBatchEnabled(this.enableBatch);
          }
          this._assembler && this._assembler.clearEffect();
      };
  
      let _onLoad = skeleton.onLoad;
      skeleton.onLoad = function () {
          if (_onLoad) {
              _onLoad.call(this);
          }
      };
  
      skeleton._resetAssembler = function () {
          this._assembler = new renderer.CustomAssembler();
          this.node._proxy.setAssembler(this._assembler);
      };
  
      let _setMaterial = skeleton.setMaterial;
      skeleton.setMaterial = function(index, material) {
          _setMaterial.call(this, index, material);
          this._assembler && this._assembler.clearEffect();
          if (this._nativeSkeleton) {
              let nativeEffect = material.effect._nativeObj;
              this._nativeSkeleton.setEffect(nativeEffect);
          }
      };
  
      skeleton.setSkeletonData = function (skeletonData) {
          null != skeletonData.width && null != skeletonData.height && this.node.setContentSize(skeletonData.width, skeletonData.height);
  
          let uuid = skeletonData._uuid;
          if (!uuid) {
              cc.errorID(7504);
              return;
          }
  
          let texValues = skeletonData.textures;
          let texKeys = skeletonData.textureNames;
          if (!(texValues && texValues.length > 0 && texKeys && texKeys.length > 0)) {
              cc.errorID(7507, skeletonData.name);
              return;
          }
  
          if (this._nativeSkeleton) {
              this._nativeSkeleton.stopSchedule();
              this._nativeSkeleton._comp = null;
              this._nativeSkeleton = null;
          }
  
          let nativeSkeleton = null;
          if (this.isAnimationCached()) {
              nativeSkeleton = new spine.SkeletonCacheAnimation(uuid, this._cacheMode == AnimationCacheMode.SHARED_CACHE);
          } else {
              nativeSkeleton = new spine.SkeletonAnimation();
              try {
                  spine.initSkeletonRenderer(nativeSkeleton, uuid);
              } catch (e) {
                  cc._throw(e);
                  return;
              }
              nativeSkeleton.setDebugSlotsEnabled(this.debugSlots);
              nativeSkeleton.setDebugMeshEnabled(this.debugMesh);
              nativeSkeleton.setDebugBonesEnabled(this.debugBones);
          }
  
          this._nativeSkeleton = nativeSkeleton;
          nativeSkeleton._comp = this;
  
          nativeSkeleton.setUseTint(this.useTint);
          nativeSkeleton.setOpacityModifyRGB(this.premultipliedAlpha);
          nativeSkeleton.setTimeScale(this.timeScale);
          nativeSkeleton.setBatchEnabled(this.enableBatch);
          nativeSkeleton.bindNodeProxy(this.node._proxy);
          this._skeleton = nativeSkeleton.getSkeleton();
  
          // init skeleton listener
          this._startListener && this.setStartListener(this._startListener);
          this._endListener && this.setEndListener(this._endListener);
          this._completeListener && this.setCompleteListener(this._completeListener);
          this._eventListener && this.setEventListener(this._eventListener);
          this._interruptListener && this.setInterruptListener(this._interruptListener);
          this._disposeListener && this.setDisposeListener(this._disposeListener);
  
          this._activateMaterial();
      };
  
      skeleton.setAnimationStateData = function (stateData) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._stateData = stateData;
              return this._nativeSkeleton.setAnimationStateData(stateData);
          }
      };
  
      skeleton._prepareToRender = function (material) {
          let texValues = this.skeletonData.textures;
          material.setProperty('texture', texValues[0]);
          this.setMaterial(0, material);
          if (this.node && this.node._renderComponent == this) {
              this.markForRender(true);
          }
      };
  
      skeleton.onEnable = function () {
          renderCompProto.onEnable.call(this);
          if (this._nativeSkeleton) {
              this._nativeSkeleton.onEnable();
          }
          this._activateMaterial();
      };
  
      skeleton.onDisable = function () {
          renderCompProto.onDisable.call(this);
          if (this._nativeSkeleton) {
              this._nativeSkeleton.onDisable();
          }
      };
  
      skeleton.setVertexEffectDelegate = function (effectDelegate) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setVertexEffectDelegate(effectDelegate);
          }
      };
  
      skeleton.update = function () {
          let nativeSkeleton = this._nativeSkeleton;
          if (!nativeSkeleton) return;
  
          let node = this.node;
          if (!node) return;
  
          if (this.__preColor__ === undefined || !node.color.equals(this.__preColor__)) {
              nativeSkeleton.setColor(node.color);
              this.__preColor__ = node.color;
          }
          
          if (!this.isAnimationCached() && (this.debugBones || this.debugSlots || this.debugMesh) && this._debugRenderer) {
              
              let graphics = this._debugRenderer;
              graphics.clear();
              graphics.lineWidth = 2;
              
              let debugData = this._debugData || nativeSkeleton.getDebugData();
              if (!debugData) return;
              let debugIdx = 0, debugType = 0, debugLen = 0;
      
              while (true) {
                  debugType = debugData[debugIdx++];
                  if (debugType == 0) break;
                  debugLen = debugData[debugIdx++];
  
                  switch (debugType) {
                      case 1: // slots
                          graphics.strokeColor = _slotColor;
                          for(let i = 0; i < debugLen; i += 8){
                              graphics.moveTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.lineTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.lineTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.lineTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.close();
                              graphics.stroke();
                          }
                      break;
                      case 2: // mesh
                          graphics.strokeColor = _meshColor;
                          for(let i = 0; i < debugLen; i += 6) {
                              graphics.moveTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.lineTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.lineTo(debugData[debugIdx++], debugData[debugIdx++]);
                              graphics.close();
                              graphics.stroke();
                          }
                      break;
                      case 3: // bones
                          graphics.strokeColor = _boneColor;
                          graphics.fillColor = _slotColor; // Root bone color is same as slot color.
                          for (let i = 0; i < debugLen; i += 4) {
                              let bx = debugData[debugIdx++];
                              let by = debugData[debugIdx++];
                              let x = debugData[debugIdx++];
                              let y = debugData[debugIdx++];
              
                              // Bone lengths.
                              graphics.moveTo(bx, by);
                              graphics.lineTo(x, y);
                              graphics.stroke();
              
                              // Bone origins.
                              graphics.circle(bx, by, Math.PI * 1.5);
                              graphics.fill();
                              if (i === 0) {
                                  graphics.fillColor = _originColor;
                              }
                          }
                      break;
                      default:
                      return;
                  }
              }
          }
      };
  
      skeleton.updateWorldTransform = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.updateWorldTransform();
          }
      };
  
      skeleton.setToSetupPose = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setToSetupPose();
          }
      };
  
      skeleton.setBonesToSetupPose = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setBonesToSetupPose();
          }
      };
  
      skeleton.setSlotsToSetupPose = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setSlotsToSetupPose();
          }
      };
  
      skeleton.setSlotsRange = function (startSlotIndex, endSlotIndex) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setSlotsRange(startSlotIndex, endSlotIndex);
          }
      };
  
      skeleton.updateAnimationCache = function (animName) {
          if (!this.isAnimationCached()) return;
          if (this._nativeSkeleton) {
              if (animName) {
                  this._nativeSkeleton.updateAnimationCache(animName);
              } else {
                  this._nativeSkeleton.updateAllAnimationCache();
              }
          }
      };
  
      skeleton.invalidAnimationCache = function () {
          if (!this.isAnimationCached()) return;
          if (this._nativeSkeleton) {
              this._nativeSkeleton.updateAllAnimationCache();
          }
      };
  
      skeleton.findBone = function (boneName) {
          if (this._nativeSkeleton) return this._nativeSkeleton.findBone(boneName);
          return null;
      };
  
      skeleton.findSlot = function (slotName) {
          if (this._nativeSkeleton) return this._nativeSkeleton.findSlot(slotName);
          return null;
      };
  
      skeleton.setSkin = function (skinName) {
          if (this._nativeSkeleton) return this._nativeSkeleton.setSkin(skinName);
          return null;
      };
  
      skeleton.getAttachment = function (slotName, attachmentName) {
          if (this._nativeSkeleton) return this._nativeSkeleton.getAttachment(slotName, attachmentName);
          return null;
      };
  
      skeleton.setAttachment = function (slotName, attachmentName) {
          this._nativeSkeleton && this._nativeSkeleton.setAttachment(slotName, attachmentName);
      };
  
      skeleton.getTextureAtlas = function (regionAttachment) {
          cc.warn("sp.Skeleton getTextureAtlas not support in native");
          return null;
      };
  
      skeleton.setMix = function (fromAnimation, toAnimation, duration) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setMix(fromAnimation, toAnimation, duration);
          }
      };
  
      skeleton.setAnimation = function (trackIndex, name, loop) {
          if (this._nativeSkeleton) {
              if (this.isAnimationCached()) {
                  return this._nativeSkeleton.setAnimation(name, loop);
              } else {
                  return this._nativeSkeleton.setAnimation(trackIndex, name, loop);
              }
          }
          return null;
      };
  
      skeleton.addAnimation = function (trackIndex, name, loop, delay) {
          if (this._nativeSkeleton) {
              delay = delay || 0;
              if (this.isAnimationCached()) {
                  return this._nativeSkeleton.addAnimation(name, loop, delay);
              } else {
                  return this._nativeSkeleton.addAnimation(trackIndex, name, loop, delay);
              }
          }
          return null;
      };
  
      skeleton.findAnimation = function (name) {
          if (this._nativeSkeleton) return this._nativeSkeleton.findAnimation(name);
          return null;
      };
  
      skeleton.getCurrent = function (trackIndex) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              return this._nativeSkeleton.getCurrent(trackIndex);
          }
          return null;
      };
  
      skeleton.clearTracks = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.clearTracks();
          }
      };
  
      skeleton.clearTrack = function (trackIndex) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.clearTrack(trackIndex);
          }
      };
  
      skeleton.setStartListener = function (listener) {
          this._startListener = listener;
          if (this._nativeSkeleton) {
              this._nativeSkeleton.setStartListener(listener);
          }
      };
  
      skeleton.setInterruptListener = function (listener) {
          this._interruptListener = listener;
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setInterruptListener(listener);
          }
      };
  
      skeleton.setEndListener = function (listener) {
          this._endListener = listener;
          if (this._nativeSkeleton) {
              this._nativeSkeleton.setEndListener(listener);
          }
      };
  
      skeleton.setDisposeListener = function (listener) {
          this._disposeListener = listener;
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setDisposeListener(listener);
          }
      };
  
      skeleton.setCompleteListener = function (listener) {
          this._completeListener = listener;
          if (this._nativeSkeleton) {
              this._nativeSkeleton.setCompleteListener(listener);
          }
      };
  
      skeleton.setEventListener = function (listener) {
          this._eventListener = listener;
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setEventListener(listener);
          }
      };
  
      skeleton.setTrackStartListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackStartListener(entry, listener);
          }
      };
  
      skeleton.setTrackInterruptListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackInterruptListener(entry, listener);
          }
      };
  
      skeleton.setTrackEndListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackEndListener(entry, listener);
          }
      };
  
      skeleton.setTrackDisposeListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackDisposeListener(entry, listener);
          }
      };
  
      skeleton.setTrackCompleteListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackCompleteListener(entry, listener);
          }
      };
  
      skeleton.setTrackEventListener = function (entry, listener) {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              this._nativeSkeleton.setTrackEventListener(entry, listener);
          }
      };
  
      skeleton.getState = function () {
          if (this._nativeSkeleton && !this.isAnimationCached()) {
              return this._nativeSkeleton.getState();
          }
      };
  
      skeleton._ensureListener = function () {
          cc.warn("sp.Skeleton _ensureListener not need in native");
      };
  
      skeleton._updateSkeletonData = function () {
          if (this.skeletonData) {
              this.skeletonData.init();
              this.setSkeletonData(this.skeletonData);
              this.defaultSkin && this._nativeSkeleton.setSkin(this.defaultSkin);
              this.animation = this.defaultAnimation;
          } else {
              if (this._nativeSkeleton) {
                  this._nativeSkeleton.stopSchedule();
                  this._nativeSkeleton._comp = null;
                  this._nativeSkeleton = null;
              }
          }
      };
  
      let _onDestroy = skeleton.onDestroy;
      skeleton.onDestroy = function(){
          _onDestroy.call(this);
          if (this._nativeSkeleton) {
              this._nativeSkeleton.stopSchedule();
              this._nativeSkeleton._comp = null;
              this._nativeSkeleton = null;
          }
          this._stateData = null;
          this._materialCache = null;
      };
  
  })();
  