import { getDistance } from "./util";

export default class Cartoon {
  elements = {

  }

  touchEventCallback = {

  }

  dragEventCallback = {

  }

  audioCallback = {

  }

  constructor() {

  }

  audios = {}
  audioIDs = {}
  _curDragItem = null;

  initCartoonElement(key, node) {
    if(!node) {
      console.error("Can not find node for key:[" + key + "]")
    }
    this.elements[key] = {};
    this.elements[key].key = key;
    this.elements[key].node = node;
    this.elements[key].node.key = key;
    this.elements[key].initX = node.x;
    this.elements[key].initY = node.y;
    this.elements[key].initScaleX = node.scaleX;
    this.elements[key].initScaleY = node.scaleY;
    this.elements[key].dragonBones = new DragonBones(node);

    this.elements[key]["setLocation"] = (x, y) => {
      if(x) {
        this.elements[key].initX = x
        this.elements[key].node.x = x
      }
      if(y) {
        this.elements[key].initY = y
        this.elements[key].node.y = y
      }
    }

    return this.elements[key]
  }

  getCartoonElement(key) {
    let node = this.elements[key]
    if(!node) {
      console.error("Can't find '" + key + "' node.")
    }
    return node
  }

  setCartoonElement(key, node) {
    this.elements[key] = {};
    this.elements[key].node = node;
    this.elements[key].node.key = key;
    return this.elements[key]
  }

  getSpritNode(nodeName, nameSpace = "") {
    let node = cc.find(`${nameSpace}${nodeName}`)
    return node?node:null
  }

  getNodeByName(nodeName, parentNodeName) {
    let node = null
    if(parentNodeName) {
      node = this.getSpritNode(parentNodeName).getChildByName(nodeName)
    } else {
      node = this.getSpritNode(nodeName)
    }
    return node?node:null
  }

  getNodeByPath(path) {
    let node = cc.find(path)
    if(!node) {
      console.warn("Can not fount node.(PATH:" + path + ")")
    }
    return node?node:null
  }

  getColor(hex) {
    let color = new cc.color()
    color.fromHEX(hex);
    return color
  }

  getFontFamily(fontName) {
    let font = cc.find(`Canvas/res/font/${fontName}`).getComponent('cc.Label').font;
    return font
  }

  newLabelNode(text, fontFamily, fontSize, color) {
    const labelNode = new cc.Node();
    labelNode.color = this.getColor(color);
    const label = labelNode.addComponent(cc.Label);
    label.string = text;
    label.fontSize = fontSize;
    label.lineHeight = fontSize;
    label.font = this.getFontFamily(fontFamily);
    return labelNode
  }

  creatSpritNode(name, spriteFrame, parentNode) {
    if(!name) {
      name = new Date().toString();
    }
    var node = new cc.Node(name + "");
    var sp = node.addComponent(cc.Sprite);
    sp.spriteFrame = spriteFrame;
    node.parent = parentNode

    return node
  }

  createFrameAni(imagePath, len) {
    let frameAni = new cc.Sprite(imagePath.replace(/_NUM_/g, 1));
    frameAni.setPosition(cc.p(cc.winSize.width*0.3, cc.winSize.height/2));
    this.addChild(frameAni);

    //创建帧动画序列，名词形式
    var animation = new cc.Animation();
    for (var i = 1; i <= len; i++) {
        animation.addSpriteFrameWithFile(imagePath.replace(/_NUM_/g, i));
    }
    //设置帧动画属性
    animation.setDelayPerUnit(2.0 / 4);       //每一帧停留的时间
    animation.setRestoreOriginalFrame(true);   //播放完后回到第一帧
    var animate = new cc.Animate(animation);
    frameAni.run = () => {
      frameAni.runAction(new cc.RepeatForever(animate));
    }
  }
  
  cloneNode(targetNode) {
    var node = cc.instantiate(targetNode);
    return node
  }

  subscribeTouchEvent(key, callBack, newEvent) {
    if(!this.touchEventCallback[key] || newEvent) {
      this.touchEventCallback[key] = callBack
      this.elements[key].node.off(cc.Node.EventType.TOUCH_START)
      this.elements[key].node.on(cc.Node.EventType.TOUCH_START, ()=>{
        if(this.touchEventCallback[key]) {
          this.touchEventCallback[key]()
        }
      })
    } else {
      this.touchEventCallback[key] = callBack
    }
  }
  
  subscribeDragEvent(key, triggerCallback, moveCallback, releaseCallback) {
    this.dragEventCallback[key] = {}
    this.dragEventCallback[key].triggerCallback = triggerCallback;
    this.dragEventCallback[key].moveCallback = moveCallback;
    this.dragEventCallback[key].releaseCallback = releaseCallback;

    this.elements[key].node.off(cc.Node.EventType.TOUCH_START)
      this.elements[key].node.on(cc.Node.EventType.TOUCH_START, ()=>{
        if(this.dragEventCallback[key].triggerCallback && !this.dragEventCallback[key].triggerCallback()) {
          this._curDragItem = this.elements[key].node;
        }
      })
  }

  initDragEvent() {
    const canvas = cc.find('Canvas');
    canvas.width = cc.winSize.width;
    canvas.height = cc.winSize.height;
    if (canvas.hasEventListener(cc.Node.EventType.TOUCH_MOVE)) {
      return;
    }
    canvas.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
      this.canvasTouchMove(e)
    },)
    canvas.on(cc.Node.EventType.TOUCH_CANCEL, (e) => {
      this.canvasTouchEnd(e);
    })
    canvas.on(cc.Node.EventType.TOUCH_END, (e) => {
      this.canvasTouchEnd(e);
    })
  }

  canvasTouchMove(e) {
		if (!this._curDragItem) {
			return;
		}
    if(this.dragEventCallback[this._curDragItem.key].moveCallback && !this.dragEventCallback[this._curDragItem.key].moveCallback(e)) {
      const pos = e.getLocation();
      const worldPos = e.currentTarget.parent.convertToWorldSpaceAR(cc.v2(pos.x, pos.y));
      const localPos = this._curDragItem.parent.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y));
      this._curDragItem.x = localPos.x;
      this._curDragItem.y = localPos.y;
    }
	}

	canvasTouchEnd(e) {
		if (!this._curDragItem) {
			return;
    }
    if(this.dragEventCallback[this._curDragItem.key].releaseCallback) {
      this.dragEventCallback[this._curDragItem.key].releaseCallback(e, this.checkOnTargetBox(e))
    }
		this._curDragItem = null;
	}

	checkOnTargetBox(e) {
		const checkLen = 50 // * this._cocosScale;
    let minLen = checkLen, curBox;
    let targetNodes = []
    for(var key in this.elements) {
      const worldStartPos = this._curDragItem.parent.convertToWorldSpaceAR(this._curDragItem);
			const tmpP = cc.v2(this.elements[key].node.x, this.elements[key].node.y)
			const worldEndPos = this.elements[key].node.parent.convertToWorldSpaceAR(tmpP);
      const tmpLen = getDistance(worldStartPos, worldEndPos)
      if(
        worldStartPos.x <= (worldEndPos.x + this.elements[key].node.width / 2) &&
        worldStartPos.x >= (worldEndPos.x - this.elements[key].node.width / 2) &&
        worldStartPos.y <= (worldEndPos.y + this.elements[key].node.height / 2) &&
        worldStartPos.y >= (worldEndPos.y - this.elements[key].node.height / 2)
      ){
        curBox = key;
        minLen = tmpLen;
        targetNodes.push({
          key: key,
          minLen: tmpLen
        })
      }
    }

    targetNodes = targetNodes.sort((a, b)=>{
      return a.minLen - b.minLen
    })

    return targetNodes
  }
  
  initLocalAudios(audioNames = []) {
    const audioNode = cc.find('Canvas/res/audio');
    const getAudioByResName = (resName) => {
      return audioNode.getChildByName(resName).getComponent(cc.AudioSource);
    }
    audioNames.forEach(audioName=>{
      this.audios[audioName] = getAudioByResName(audioName);
    })
  }

  playLocalAudio(audioName, cb) {
    let audio = cc.audioEngine.play(this.audios[audioName].clip, false, 0.2)
    if (cb) {
      this.audioCallback[audio_url] = cb
      cc.audioEngine.setFinishCallback(audio, () => {
        delete this.audioCallback[audio_url]
        cb();
      });
    }
  }

  // 挂载精灵帧到节点
  mountRemoteImageToNode(url, node, width, height, fillType) {
    // console.log(`Get remote image from [${url}]`)
    return new Promise((resovle, reject) => {
      this.getSpriteFrimeByUrl(url).then((fr)=>{
        let relWidth = fr.getRect().width;
        let relHeight = fr.getRect().height;
        let sx = width / relWidth;
        let sy = height / relHeight;
        let s = 1;
        if(fillType) {
          s = Math.max(sx, sy)
        } else {
          s = Math.min(sx, sy)
        }
        node.setScale(s)
        // fr.width = s * fr.getRect().width;
        // fr.height = s * fr.getRect().height;
        node.getComponent(cc.Sprite).spriteFrame = fr;
        resovle([relWidth, relHeight])
      })
    })
  }

  mountLocalImageToNode(name, node, width, height, fillType) {
    // console.log(`Get local image from [Canvas/res/img/${name}]`)
    let relWidth = null;
    let relHeight = null;
    if(name) {
      const sf = cc.find('Canvas/res/img/' + name).getComponent(cc.Sprite).spriteFrame;
      relWidth = fr.getRect().width;
      relHeight = fr.getRect().height;

      let sx = width / sf.getRect().width;
      let sy = height / sf.getRect().height;
      let s = 1;
      if(fillType) {
        s = Math.max(sx, sy)
      } else {
        s = Math.min(sx, sy)
      }
      node.setScale(s)
      node.getComponent(cc.Sprite).spriteFrame = sf;
    }
    
    return new Promise((resolve, reject) => {
      resovle([relWidth, relHeight])
    });
  }

  mountImageToNode(url, node, width, height, fillType) {
    var urlreg=/^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*([\?&]\w+=\w*)*$/
    if(urlreg.test(url)) {
      return this.mountRemoteImageToNode(url, node, width, height, fillType)
    } else {
      return this.mountLocalImageToNode(url, node, width, height, fillType)
    }
  }


  getSpriteFrimeByUrl(url) {
    return new Promise((resolve, reject) => {
      if(!url) {
        reject()
      } else {
        cc.assetManager.loadRemote(url, (err, img) => {
          if(err) {
            reject(err)
          } else {
            const spriteFrame = new cc.SpriteFrame(img)
            resolve(spriteFrame)
          }
        })
      }
    })
  }

  getSpriteFrimeByResName(name) {
    const sf = cc.find('Canvas/res/img/' + name).getComponent(cc.Sprite).spriteFrame;
    return sf;
  }

  // Video
  getVideo(video_url){
    return new Promise((resolve, reject)=>{
      cc.assetManager.loadRemote(video_url, (err, videoClip) => {
        resolve(videoClip)
      });
    })
  }

  stopVideo(videoPlayer) {
    if(videoPlayer.clip && videoPlayer.clip.loaded) {
      videoPlayer.stop()
    }
  }

  playVideo(videoPlayer, callback) {
    if(videoPlayer && videoPlayer.isPlaying) {
      videoPlayer.stop()
    }

    if(videoPlayer) {
      setTimeout(()=>{
        videoPlayer.play()
        videoPlayer.node.off("completed")
        videoPlayer.node.on("completed", ()=>{
          if(callback) {
            callback()
          }
        })
      }, 50)
    }
  }

  // Audio
  playAudio(audio_url, cb = null) {
    return new Promise((resolve, reject)=>{
      if (audio_url) {
        var urlreg=/^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*([\?&]\w+=\w*)*$/
        if(urlreg.test(audio_url)) {
          cc.assetManager.loadRemote(audio_url, (err, audioClip) => {
            if(cb) {
              this.audioCallback[audio_url] = cb
            }
            const audioId = cc.audioEngine.play(audioClip, false, 1);
            if (cb) {
              cc.audioEngine.setFinishCallback(audioId, () => {
                delete this.audioCallback[audio_url]
                cb();
              });
            }
            this.audioIDs[audio_url] = audioId
            resolve(audioId)
          });
        } else {
          const audioNode = cc.find('Canvas/res/audio').getChildByName(audio_url).getComponent(cc.AudioSource);
          let audioId = cc.audioEngine.play(audioNode.clip, false, 1)
          if (cb) {
            this.audioCallback[audio_url] = cb
            cc.audioEngine.setFinishCallback(audioId, () => {
              delete this.audioCallback[audio_url]
              cb();
            });
          }
          this.audioIDs[audio_url] = audioId
          resolve(audioId)
        }
      } else{
        return new Promise((resolve, reject)=>{
          cb()
          resolve()
        })
      }
    })
  }

  stopAllAudio(exceptUrls = []) {
    for(let key in this.audioIDs) {
      if(exceptUrls.indexOf(key) == -1) {
        if(this.audioCallback[key]) {
          this.audioCallback[key]()
        }
        if(this.audioIDs[key]) {
          cc.audioEngine.stop(this.audioIDs[key])
          delete this.audioIDs[key];
        }
      }
    }
  }

  // Tweenchange
  tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
    const tween = cc.tween(item).to(time, obj).call(()=>{
      if(callBack) {
        callBack()
      }
    });
    easing && tween.easing(easing);
    update && tween.onUpdate((a, b) => { update(a, b); });
  
    tween.start();
    return tween;
  }

  // Common Ani
  creatSpeaker(parentNode, nodeName1, nodeName2) {
    const sprNode = this.cloneNode(this.getNodeByPath(`Canvas/res/img/${nodeName1}`));
    sprNode.parent = parentNode;
    sprNode.x = 0;
    sprNode.y = 0;
    sprNode.zIndex = 2;
    parentNode.icon = sprNode;

    const sprNode2 = this.cloneNode(this.getNodeByPath(`Canvas/res/img/${nodeName2}`));
    sprNode2.parent = sprNode.parent;
    sprNode2.x = sprNode.x;
    sprNode2.y = sprNode.y;
    sprNode2.opacity = 0;
    sprNode.spr2 = sprNode2;

    // sprNode.addComponent(cc.Button)
    // sprNode.on('click', () => {
    //   this.showSoundAnima(sprNode);
    //   if(clickCB) {
    //     clickCB(this)
    //   }
    //   playAudioByUrl( item.data.audio_url , () => {
    //     this.stopSoundAnima(sprNode);
    //   });
    // });

    return sprNode
  }

  showSoundAnima(iconNode, time = 0.4) {
    iconNode.isRunAnima = true;
    iconNode.spr2.opacity = 255;
    iconNode.opacity = 0;

    cc.tween(iconNode)
      .delay(time / 2)
      .call(() => {
        iconNode.spr2.opacity = 0;
        iconNode.opacity = 255;
      })
      .delay(time / 2)
      .call(() => {
        if (iconNode.isRunAnima) {
          this.showSoundAnima(iconNode);
        }
      })
      .start()
  }

  stopSoundAnima(iconNode) {
    iconNode.isRunAnima = false;
  }
}


class DragonBones {
  parentNode = null
  _animationLoaded = null
  constructor(parentNode) {
    this.parentNode = parentNode
  }

  resetDragonBones() {
    const picNode = this.parentNode
    if (picNode.animaNode) {
      picNode.animaNode.removeFromParent();
      this._animationLoaded = null
    }
  }

  setDragonBones(dragonBonesConfig, armatureName, callback, onClickCB) {
    const parentNode = this.parentNode
    if (parentNode.animaNode) {
      parentNode.animaNode.removeFromParent();
    }
    const animaNode = new cc.Node();
    animaNode.parent = parentNode;
    parentNode.animaNode = animaNode;

    
    const dragonDisplay = animaNode.addComponent(dragonBones.ArmatureDisplay);

    this._animationLoaded = dragonDisplay;
    
    // const image = "http://staging-teach.cdn.ireadabc.com/5a0c5af3e952acd0ac83a6e5c6f03ff1.png";
    // const ske = "http://staging-teach.cdn.ireadabc.com/bd825dcceea298d1146be8067ae3d17f.json";
    // const atlas = "http://staging-teach.cdn.ireadabc.com/246f8991b729fa3e5a7f4802de828efa.json";
    
    const image = dragonBonesConfig.texPngData.url
    const ske = dragonBonesConfig.skeJsonData.url
    const atlas = dragonBonesConfig.texJsonData.url

    cc.loader.load(image, (error, texture) => {
      cc.loader.load({url: atlas, type: 'txt'}, (error, atlasJson) => {
        cc.loader.load({url: ske, type: 'txt'}, (error, dragonBonesJson) => {
          const atlas = new dragonBones.DragonBonesAtlasAsset();
          atlas.atlasJson = (atlasJson);
          atlas.texture = texture;

          const asset = new dragonBones.DragonBonesAsset();
          asset.dragonBonesJson = (dragonBonesJson);

          dragonDisplay.dragonAtlasAsset = atlas;
          dragonDisplay.dragonAsset = asset;

          let json = JSON.parse(asset.dragonBonesJson);
          let armatures = json["armature"];
          let armatureNames = [];
          for (let i = 0; i < armatures.length; i++) {   
            armatureNames.push(armatures[i].name);
          }

          let armatureNameIndex = armatureNames.indexOf(armatureName)
          if(armatureNameIndex == -1) {
            armatureNameIndex = 0;
          }

          if (armatureNames.length > 0) {
            let defaultArmatureName = armatureNames[armatureNameIndex];
            dragonDisplay.armatureName = defaultArmatureName;

            let defaultArmature = armatures[armatureNameIndex];
            let animations = defaultArmature.animation;
            let animationNames = [];
            for (let i = 0; i < animations.length; i++) {
              animationNames.push(animations[i].name);
            }
            
            dragonDisplay.animaNames = animationNames;
            animaNode.addComponent(cc.Button)
            animaNode.on('click', ()=>{
              if(onClickCB) {
                onClickCB(this)
              }
            })
          }

          let sx = parentNode.width / animaNode.width;
          let sy = parentNode.height / animaNode.height;
          animaNode.setScale(Math.min(sx, sy));
          if(callback) {
            callback(this)
          }
        });
      });
    });
  }

  deleteDragonBones() {
    const parentNode = this.parentNode
    if (parentNode.animaNode) {
      parentNode.animaNode.removeFromParent();
    }
  }

  stopDragonBones() {
    if(this._animationLoaded) {
      this._animationLoaded.armature().animation.stop();
    }
  }

  showDragonBones(times = 1, animaName, callBack) {
    let animaNameIndex = this._animationLoaded.animaNames.indexOf(animaName)
    if (this._animationLoaded && this._animationLoaded.animaNames.length > 0) {
      if(!animaName || animaNameIndex==-1) {
        this._animationLoaded.playAnimation(this._animationLoaded.animaNames[0], times);
      } else {
        this._animationLoaded.playAnimation(animaName, times);
      }
      // this._animationLoaded.on(this._animationLoaded.EventObject.COMPLETE, callback)
    }
  }
}