import Cartoon from "./Cartoon"
import defaultData from "./defaultData"
import { jelly, randomSortByArr, showFireworks, RandomInt } from "./utils"
import { onHomeworkFinish } from "./util"


export default class GameLogic {
  g_cartoon = null;
  g_formData = null
  g_enableClick = true;
  g_canvas = null;
  g_systemOption = null;
  g_useDefaultData = false;
  g_curDragonDisplay = null;
  g_imageResList = [];
  g_audioResList = [];
  g_animaResList = [];
  g_properties = {};

  m_bubblesAll = [];
  m_currentQuestion = -1;
  m_currentQuestionIndex = -1;
  m_rightAns = 0;
  m_stopMoving = false;
  m_maxScale = 1;
  m_isPlayingAudio = false;
  m_sysIntervalId = null;

  constructor(formData, systemOption) {
    this.g_systemOption = systemOption;
    this.g_imageResList = this.g_systemOption.imageResList;
    this.g_audioResList = this.g_systemOption.audioResList;
    this.g_animaResList = this.g_systemOption.animaResList;
    this.g_properties = this.g_systemOption.properties;

    this.g_canvas = cc.find('Canvas');
    this.g_cartoon = new Cartoon(cc);

    if(!formData.contentObj.dataArray || formData.contentObj.dataArray.length == 0) {
      this.g_useDefaultData = true;
      this.g_formData = defaultData
      console.warn("User default data =>", this.g_formData)
    } else {
      this.g_formData = formData.contentObj;
      console.log("User saved data =>", this.g_formData)
    }
    
    this.disableClick("Sysyem_init")
    this.g_cartoon.initLocalAudios([])
    // this.initMaskLayer();
    
    this.initNodes();
    this.initBg();
    this.resetPosition();
    this.addEventLinstener();

    this.g_cartoon.initDragEvent()
    this.enableClick("Sysyem_init");
    
    cc.view.setResizeCallback(() => {
      this.initBg();
      this.resetPosition();
   });
  }

  initNodes() {
    this.g_cartoon.initCartoonElement("background", this.g_cartoon.getNodeByPath(`Canvas/background/background`));
    this.g_cartoon.initCartoonElement("audio", this.g_cartoon.getNodeByPath(`Canvas/speaker`));
    this.g_cartoon.initCartoonElement("back", this.g_cartoon.getNodeByPath(`Canvas/btn_back/btn_back`));
    this.g_cartoon.initCartoonElement("restart", this.g_cartoon.getNodeByPath(`Canvas/btn_restart/btn_restart`));
    this.g_cartoon.initCartoonElement("start", this.g_cartoon.getNodeByPath(`Canvas/btn_start/btn_start`));
    this.g_cartoon.initCartoonElement("lion", this.g_cartoon.getNodeByPath(`Canvas/lion/DB`));
  }

  initBg() {
    const bgMain = this.g_cartoon.getCartoonElement("background")
    bgMain.node.width = cc.winSize.width
    bgMain.node.height = cc.winSize.height
  }

  resetPosition() {
    let node = this.g_cartoon.getNodeByPath(`Canvas/speaker`)
    node.x = -cc.winSize.width / 2 + 100
    node.y = -cc.winSize.height / 2 + 100
    this.m_bubblesAll.push({node: node, move: {
      r: 50, x: node.x, y: node.y, vX: 0, vY: 0, enable: true, fix: true, scale: 1, id: 999, lastId: null
    }})

    node = this.g_cartoon.getNodeByPath(`Canvas/btn_back`)
    node.x = -cc.winSize.width / 2 + 60
    node.y = cc.winSize.height / 2 - 50
    this.m_bubblesAll.push({node: node, move: {
      r: 40, x: node.x, y: node.y, vX: 0, vY: 0, enable: true, fix: true, scale: 1, id: 998, lastId: null
    }})

    node = this.g_cartoon.getNodeByPath(`Canvas/btn_restart`)
    node.x = 0
    node.y = 20
    // this.m_bubblesAll.push({node: node, move: {
    //   r: 90, x: node.x, y: node.y, vX: 0, vY: 0, enable: true, fix: true, scale: 1
    // }})

    node = this.g_cartoon.getNodeByPath(`Canvas/lion`)
    node.x = cc.winSize.width / 2 - 170
    node.y = -cc.winSize.height / 2 + 120
    this.m_bubblesAll.push({node: node, move: {
      r: 90, x: node.x, y: node.y, vX: 0, vY: 0, enable: true, fix: true, scale: 1, id: 997, lastId: null
    }})

    node = this.g_cartoon.getNodeByPath(`Canvas/star_group`)
    node.y = -cc.winSize.height/2 - 140
  }

  addEventLinstener() {
    const btn_restart = this.g_cartoon.getCartoonElement("restart")
    this.g_cartoon.subscribeTouchEvent("restart", _=>{
      if(!this.g_enableClick) {
        return
      }
      this.g_cartoon.playAudio("按钮");
      this.g_cartoon.tweenChange(btn_restart.node, {scaleX: 0, scaleY: 0}, 0.2, _=>{
        this.enableClick("Restart")
        this.restartGame();
      })
    })

    const btn_start = this.g_cartoon.getCartoonElement("start")
    this.g_cartoon.subscribeTouchEvent("start", _=>{
      if(!this.g_enableClick) {
        return
      }
      this.g_cartoon.playAudio("按钮");
      this.g_cartoon.tweenChange(btn_start.node, {scaleX: 0, scaleY: 0}, 0.2, _=>{
        this.enableClick("start")
        if(this.nextQuestion()) {
          if(this.g_formData.dataArray.length>1) {
            this.initStar()
          }
          this.releaseBubbles()
        }
      })
    })

    const btn_lion = this.g_cartoon.getCartoonElement("lion")
    btn_lion.play = (aniName) => {
      if(aniName) {
        aniName = "play"
      } else {
        aniName = "normal"
      }

      let a = btn_lion.node.getComponent(dragonBones.ArmatureDisplay)
      a.playAnimation(aniName, 1);
      setTimeout(() => {
        a.playAnimation(aniName, -1);
      }, 3200);
    }


    // 音频播放
    const btn_speaker = this.g_cartoon.getCartoonElement("audio");
    let intervalId = null;
    btn_speaker.startAni = () => {
      const speakers = [];
      for (let index = 3; index >= 0; index--) {
        const node = cc.find(`Canvas/speaker/speaker_${index}`);
        speakers.push(node);
      }

      const _node = cc.find(`Canvas/speaker/default`);
      let currentIdx = 0;
      let num = speakers.length;
      intervalId = setInterval(() => {
        _node.active = false;
        speakers.forEach((node, index) => {
          node.active = false;
        })
        currentIdx++;
        speakers[currentIdx%num].active = true;
      }, 200);
    }

    btn_speaker.stopAni = ()=>{
      if(intervalId) {
        clearTimeout(intervalId)
        intervalId = null;
      }

      for (let index = 3; index >= 0; index--) {
        const node = cc.find(`Canvas/speaker/speaker_${index}`);
        node.active = false;
      }

      const node = cc.find(`Canvas/speaker/default`);
      node.active = true;
    }

    btn_speaker.showHide = (status) => {
      if(status) {
        btn_speaker.node.active = true;
      } else {
        btn_speaker.node.active = false;
      }
    }

    btn_speaker.playAudio = () => {
      if(btn_speaker.node.active) {
        btn_speaker.startAni()
        this.g_cartoon.playAudio(this.g_formData.dataArray[this.m_currentQuestionIndex].audio_url1, ()=>{
          this.m_isPlayingAudio = false;
          btn_speaker.stopAni();
        })
      }
    }

    this.g_cartoon.subscribeTouchEvent("audio", _=>{
      if(!this.g_enableClick) {
        return
      }
      if(this.m_isPlayingAudio) {
        this.m_isPlayingAudio = false;
        btn_speaker.stopAni();
        this.g_cartoon.stopAllAudio()
      } else {
        this.m_isPlayingAudio = true;
        btn_speaker.playAudio();
      }
    })
  }

  endGame() {
    const btn_restart = this.g_cartoon.getCartoonElement("restart")
    this.disableClick("EndGame")
    this.g_cartoon.playAudio("撒花")
    this.fireworks();
    this.g_cartoon.tweenChange(btn_restart.node, {scale: 1}, 0.3, _=>{
      this.enableClick("EndGame");
      onHomeworkFinish();
    })
  }

  restartGame() {
    this.m_currentQuestion = -1;
    this.m_currentQuestionIndex = -1;
    this.m_rightAns = 0;
    this.m_stopMoving = false;
    if(this.g_formData.dataArray.length>1) {
      this.initStar();
    }
    this.nextQuestion()
    this.releaseBubbles();
  }

  resetSystemTick() {
    if(this.m_sysIntervalId) {
      clearInterval(this.m_sysIntervalId);
      this.m_sysIntervalId = null;
    }
    this.systemTick()
  }

  systemTick() {
    let maxNum = this.m_bubblesAll.length;
    let maxX = cc.winSize.width / 2;
    let maxY = cc.winSize.height / 2;
    this.m_sysIntervalId = setInterval(()=>{
      this.m_bubblesAll.forEach((bubble, index_x)=>{
        if(!this.m_stopMoving && bubble.move.enable && !bubble.move.fix) {
          bubble.move.x += bubble.move.vX
          bubble.move.y += bubble.move.vY

          if (bubble.move.x >= maxX - bubble.move.r * this.m_maxScale) {
            bubble.move.x = maxX - bubble.move.r * this.m_maxScale;
            bubble.move.vX = -bubble.move.vX;
            // bubble.move.lastId = null;
          }
          if (bubble.move.x <= -maxX + bubble.move.r * this.m_maxScale) {
            bubble.move.x = -maxX + bubble.move.r * this.m_maxScale;
            bubble.move.vX = -bubble.move.vX;
            // bubble.move.lastId = null;
          }
          if (bubble.move.y >= maxY - bubble.move.r * this.m_maxScale) {
            bubble.move.y = maxY - bubble.move.r * this.m_maxScale;
            bubble.move.vY = -bubble.move.vY;
            // bubble.move.lastId = null;
          }
          if (bubble.move.y <= -maxY + bubble.move.r * this.m_maxScale) {
            bubble.move.y = -maxY + bubble.move.r * this.m_maxScale;
            bubble.move.vY = -	bubble.move.vY;
            // bubble.move.lastId = null;
          }
  
          bubble.node.x = bubble.move.x
          bubble.node.y = bubble.move.y
          
          for (var j = 0; j < maxNum; j++) {
            if (j !== index_x) {
              if (Math.round(Math.pow(this.m_bubblesAll[index_x].node.x - this.m_bubblesAll[j].node.x, 2) +
                Math.pow(this.m_bubblesAll[index_x].node.y - this.m_bubblesAll[j].node.y, 2)) <=
                Math.round(Math.pow(this.m_bubblesAll[index_x].move.r * this.m_maxScale + this.m_bubblesAll[j].move.r * this.m_maxScale, 2))) {
  
                var tempX = this.m_bubblesAll[index_x].move.vX;
                var tempY = this.m_bubblesAll[index_x].move.vY;
                if(this.m_bubblesAll[index_x].move.lastId != this.m_bubblesAll[j].move.id) {
                  this.m_bubblesAll[index_x].move.lastId = this.m_bubblesAll[j].move.id;
                  this.m_bubblesAll[j].move.lastId = this.m_bubblesAll[index_x].move.id;
                  if(this.m_bubblesAll[j].move.fix) {
                    this.m_bubblesAll[index_x].move.vX = -this.m_bubblesAll[index_x].move.vX;
                    this.m_bubblesAll[index_x].move.vY = -this.m_bubblesAll[index_x].move.vY;
                  } else {
                    this.m_bubblesAll[index_x].move.vX = this.m_bubblesAll[j].move.vX;
                    this.m_bubblesAll[j].move.vX = tempX;
                    this.m_bubblesAll[index_x].move.vY = this.m_bubblesAll[j].move.vY;
                    this.m_bubblesAll[j].move.vY = tempY;
                  }
                }
              }
            }
          }
        }
      })
    }, 30)
  }

  initStar() {
    // const starGroup = this.g_cartoon.getNodeByPath(`Canvas/star_group`)
    // const scoreGroup = this.g_cartoon.getNodeByPath(`Canvas/score_group`)
    // const scoreAll = []
    const num = this.g_formData.dataArray.length;
    // if(num>9) {
    //   scoreGroup.scale = 0.6
    // }

    // for(let i=0; i<this.g_formData.dataArray.length; i++) {
    //   const star = cc.instantiate(this.g_systemOption.prefabs.star);
    //   star.parent = starGroup;
    //   this.g_cartoon.setCartoonElement(`star_${i}`, star)

    //   const score = cc.instantiate(this.g_systemOption.prefabs.score);
    //   score.parent = scoreGroup;
    //   this.g_cartoon.setCartoonElement(`score_${i}`, score)
    //   scoreAll.push(score)
    // }

    // let startY = (scoreAll.length * 66) / 2;
    // scoreAll.forEach((score, index)=>{
    //   score.y = startY
    //   startY -= 66 + 10
    // })

    this.setStart(num)
  }

  // addStar(callback) {
  //   if(this.g_formData.dataArray.length<=1) {
  //     if(callback) {
  //       callback()
  //     }
  //     return;
  //   }

  //   const star = this.g_cartoon.getCartoonElement(`star_${this.m_currentQuestionIndex}`);
  //   const score = this.g_cartoon.getCartoonElement(`score_${this.m_currentQuestionIndex}`);
  //   const score_b = score.node.getChildByName("b")
  //   const starGroup = this.g_cartoon.getNodeByPath(`Canvas/star_group`);
  //   const scoreGroup = this.g_cartoon.getNodeByPath(`Canvas/score_group`);

  //   this.g_cartoon.playAudio("大星")
  //   this.g_cartoon.tweenChange(star.node, {y: 380}, 1, ()=>{
  //     setTimeout(() => {
  //       const worldPos = starGroup.convertToWorldSpaceAR(cc.v2(star.node.x, star.node.y));
  //       const localPos = scoreGroup.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y));

  //       star.node.parent = scoreGroup;
  //       star.node.x = localPos.x;
  //       star.node.y = localPos.y;

  //       this.g_cartoon.tweenChange(star.node, {x: score.node.x, y: score.node.y, scale: 0.2}, 0.8, _=>{
  //         score_b.active = true;
  //         score_b.scaleX = 0;
  //         score_b.scaleY = 0;
  //         star.node.destroy();
  //         this.g_cartoon.tweenChange(score_b, {scale: 1}, 0.1, _=>{
  //           if(callback) {
  //             callback()
  //           }
  //         })
  //       })
  //     }, 1000);
  //   })
  // }

  nextQuestion() {
    this.m_currentQuestionIndex++;
    // 检查全局音乐
    const btn_speaker = this.g_cartoon.getCartoonElement("audio");
    if(this.g_formData.dataArray[this.m_currentQuestionIndex] && this.g_formData.dataArray[this.m_currentQuestionIndex].audio_url1) {
      btn_speaker.showHide(true);
    } else {
      btn_speaker.showHide(false);
    }

    if(this.m_currentQuestionIndex >= this.g_formData.dataArray.length) {
      btn_speaker.showHide(false);
      return false;
    }

    const btn_lion = this.g_cartoon.getCartoonElement("lion")
    btn_lion.play(false);

    const rightNum = this.g_formData.dataArray[this.m_currentQuestionIndex].correct.length;
    const wrongNum = this.g_formData.dataArray[this.m_currentQuestionIndex].incorrect.length;
    const bubbleNum = rightNum + wrongNum;
    const bubbleParent = this.g_cartoon.getNodeByPath(`Canvas/bubble_group`)
    this.m_bubblesAll = JSON.parse(JSON.stringify([]));
    this.m_rightAns = 0;

    const scale = bubbleNum<=15?1:0.7;
    for(let i=0; i<bubbleNum; i++) {
      const bubble = cc.instantiate(this.g_systemOption.prefabs.bubble);
      bubble.parent = bubbleParent;
      this.m_maxScale = scale;
      let bubb = this.g_cartoon.setCartoonElement(`bubble_${i}`, bubble)
      bubb.boom = () => {
        let _p = []
        bubb.node.getChildByName("image_container").active = false;
        bubb.node.getChildByName("text_container").active = false;
        bubb.node.getChildByName("image").active = false;
        for(let i=0; i<8; i++) {
          _p.push(new Promise((resolve,reject) => {
            setTimeout(() => {
              if(i>1) {
                bubb.node.getChildByName(`bubble_ani (${i})`).active = false;
              }
              bubb.node.getChildByName(`bubble_ani (${i+1})`).active = true;
              resolve()
            }, 30 * i);
          }))
        }
        return Promise.all(_p)
      }

      bubb.shake = (callback) => {
        let _x = bubb.node.x;
        bubb.node.getChildByName("color").active = true;
        this.g_cartoon.tweenChange(bubb.node, {x: _x-5}, 0.05, _=>{
          this.g_cartoon.tweenChange(bubb.node, {x: _x+5}, 0.1, _=>{
            this.g_cartoon.tweenChange(bubb.node, {x: _x-5}, 0.1, _=>{
              this.g_cartoon.tweenChange(bubb.node, {x: _x}, 0.05, _=>{
                bubb.node.getChildByName("color").active = false;
                if(callback) {
                  callback()
                }
              })
            })
          })
        })
      }
    }

    for(let i=0; i<bubbleNum; i++) {
      const bubble = this.g_cartoon.getCartoonElement(`bubble_${i}`)
      let item = i<rightNum?this.g_formData.dataArray[this.m_currentQuestionIndex].correct[i]:this.g_formData.dataArray[this.m_currentQuestionIndex].incorrect[i-rightNum]
      bubble.isRight = i<rightNum?true:false;
      bubble.move = {
        r: 95, x: 0, y: 0, vX: 1, vY: 1.3, enable: false, fix: false, scale: 0, id: i, lastId: null
      }

      if(item.type == 'Image') {
        let image = bubble.node.getChildByName("image_container")
        image.active = true;
        this.g_cartoon.mountImageToNode(item.image_url, image, 160, 80).then(([width, height]) => {
          let s = width / height
          let r = 165
          let h = Math.floor(Math.sqrt(r*r / (s*s + 1)))
          let w = s * h
          image.scaleX = w / width;
          image.scaleY = h / height;
        })

      } else {
        let text = bubble.node.getChildByName("text_container")
        text.active = true;
        var tLabel = text.getComponent(cc.Label)
        tLabel.string = item.text
      }

      bubble.node.scaleX = 0;
      bubble.node.scaleY = 0;
      bubble.node.x = 0;
      bubble.node.y = -2000;
      this.m_bubblesAll.push(bubble)

      this.g_cartoon.subscribeTouchEvent(`bubble_${i}`, ()=>{
        if(!this.g_enableClick) {
          return
        }
        this.disableClick(`bubble_${i}`)
        let clickBubble = this.g_cartoon.getCartoonElement(`bubble_${i}`)
        const btn_speaker = this.g_cartoon.getCartoonElement("audio");
        this.g_cartoon.stopAllAudio();
        btn_speaker.stopAni();
        if(bubble.isRight) {
          this.g_cartoon.playAudio("正确")
          clickBubble.boom().then(_=>{
            bubble.node.active = false;
            if(item.audio_url) {
              this.g_cartoon.playAudio(item.audio_url, _=>{
                this.m_rightAns++;
                if(this.m_rightAns>=rightNum) {
                  this.m_stopMoving = true;
                  this.showStar(this.m_currentQuestionIndex, _=>{
                    this.destroyBubbles().then(_=>{
                      if(!this.nextQuestion()) {
                        this.endGame()
                      } else {
                        this.releaseBubbles();
                      }
                    }) 
                  })
                } else {
                  this.enableClick("Destroy")
                }
              })
            } else {
              this.m_rightAns++;
              if(this.m_rightAns>=rightNum) {
                this.m_stopMoving = true;
                this.showStar(this.m_currentQuestionIndex, _=>{
                  this.destroyBubbles().then(_=>{
                    if(!this.nextQuestion()) {
                      this.endGame()
                    } else {
                      this.releaseBubbles();
                    }
                  }) 
                })
              } else {
                this.enableClick("Destroy")
              }
            }
          })
        } else {
          this.m_stopMoving = true;
          clickBubble.shake(_=>{
            this.m_stopMoving = false;
            this.resetSystemTick();
            this.enableClick(`bubble_${i}`)
          })
          this.g_cartoon.playAudio("错误泡泡爆炸");
        }
      }, true)
    }
    return true;
  }

  destroyBubbles() {
    this.disableClick("DestroyBubbles")
    this.save = []
    this.m_bubblesAll.forEach(item=>{
      if(!item.move.fix) {
        this.g_cartoon.tweenChange(item.node, {y: item.node.y + cc.winSize.height}, 1, ()=>{
          item.node.destroy()
        })
      } else {
        this.save.push(item)
      }
    })
    this.m_bubblesAll = this.save;

    return new Promise((resolve, reject)=>{
      setTimeout(() => {
        resolve();
        this.enableClick("DestroyBubbles")
      }, 1100);
    })
  }

  releaseBubbles() {
    const pos = [
      [
        {x: -280, y: 24}, {x: 0, y: 5}, {x: -172, y: -172}, {x: 34, y: -220}, {x: 235, y: -37},
        {x: -119, y: 191}, {x: 287, y: 186}, {x: -320, y: 252}, {x: -486, y: -45}, {x: 452, y: 30},
        {x: -380, y: -247}, {x: 87, y: 252}, {x: 258, y: -244}, {x: 505, y: 236}, {x: -500, y: 160},
      ],
      [
        {x: -328.374, y: 110.355}, {x: 216.211, y: 7.251}, {x: -121.121, y: -134.579}, {x: 119.35, y: -114.68}, {x: -37.682, y: 152.075},
        {x: 0, y: -13.458}, {x: 297.835, y: 125.057}, {x: -277.234, y: -48.448}, {x: -201.869, y: 180.336}, {x: 17.41, y: -217.592},
        {x: -427.963, y: 2.691}, {x: 270.477, y: -136.746}, {x: 111.701, y: 117.084}, {x: -423.925, y: 218.018}, {x: -259.738, y: -215.326},
        {x: 216.778, y: 263.332}, {x: -157.458, y: 43.066}, {x: 375.715, y: -17.987}, {x: -563.888, y: -67.289}, {x: -91.514, y: 283.963},
        {x: 456.774, y: 131.414}, {x: -557.159, y: 275.888}, {x: 149.026, y: -275.623}, {x: -405.084, y: -141.308}, {x: 385.252, y: 271.279},
        {x: -549.084, y: 106.318}, {x: 299.425, y: -283.412}, {x: -410.468, y: -278.579}, {x: 64.598, y: 279.925}, {x: 558.494, y: 242.67},
        {x: -292.038, y: 282.617}, {x: -131.889, y: -283.962}, {x: 547.368, y: 2.675},
      ]
    ]
    
    let scaleIndex = this.m_bubblesAll.length<=15?0:1;
    let startx = cc.winSize.width / 2 - 150
    let starty = -cc.winSize.height / 2 + 195
    const vArr = [
      {vX: -0.5, vY: 0.5 }, {vX: 0.5, vY: -0.5 }, {vX: -0.5, vY: -0.5 }, {vX: 0.5, vY: 0.5 }
    ]
    let pAll = []
    let count = 0;
    this.m_bubblesAll.forEach((bubble, index) => {
      if(!bubble.move.fix) {
        bubble.node.x = startx;
        bubble.node.y = starty;
        bubble.node.scale = 0;
        let _p = new Promise((resolve, reject) => {
          bubble.move.x = pos[scaleIndex][count].x
          bubble.move.y = pos[scaleIndex][count].y
          bubble.move.vX = vArr[count%4].vX
          bubble.move.vY = vArr[count%4].vY
          count++;
          setTimeout(() => {
            this.g_cartoon.playAudio("出现")
            this.g_cartoon.tweenChange(bubble.node, {scale: this.m_maxScale, x: bubble.move.x, y: bubble.move.y}, 3, _=>{
              resolve()
            })
          }, 200 * index);
        })
        pAll.push(_p)
      }
    })

    const btn_lion = this.g_cartoon.getCartoonElement("lion")
    btn_lion.play(true);

    const btn_speaker = this.g_cartoon.getCartoonElement("audio");

    Promise.all(pAll).then(_=>{
      this.m_stopMoving = false;
      btn_lion.play(false);
      btn_speaker.playAudio();
      this.m_bubblesAll.forEach(bubble=>{
        if(!bubble.move.fix) {
          bubble.move.enable = true;
        }
      })
      this.resetSystemTick();
    })
  }

  fireworks() {
    this.showFireworks(
      cc.find('Canvas/background/background'),
        cc.find('Canvas/RibbonNodeBase').children,
        cc.v2(0, -cc.winSize.height/2), cc.v2(0, cc.winSize.height * 0.85), 200, 200, 4
    );
    this.showFireworks(
      cc.find('Canvas/background/background'),
        cc.find('Canvas/RibbonNodeBase').children,
        cc.v2(-600, -cc.winSize.height/2), cc.v2(200, cc.winSize.height * 0.85), 200, 200, 4
    );
    this.showFireworks(
        cc.find('Canvas/background/background'),
        cc.find('Canvas/RibbonNodeBase').children,
        cc.v2(600, -cc.winSize.height/2), cc.v2(-200, cc.winSize.height * 0.85), 200, 200, 4
    );
  }

  asyncTweenBy(node, duration, obj, ease = undefined) {
    return new Promise((resolve, reject) => {
      cc.tween(node)
        .by(duration, obj, ease)
        .call(() => {
          resolve();
        })
        .start();
    });
  }
  
  
  rabbonFall(node) {
    const time = 1 + Math.random();
    const offsetX = RandomInt(-200, 200) * time;
    this.asyncTweenBy(node, time, { x: offsetX, angle: offsetX * 60 / 200 }).then(_ => {
      this.rabbonFall(node);
    })
  }
  
  asyncDelay(time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve();
      }, time * 1000);
    })
  }
  
  showFireworks(baseNode, nodeList, pos = cc.v2(0, 0), side = cc.v2(0, 100), range = 50, number = 100) {
    new Array(number).fill(' ').forEach((_, i) => {
      let rabbonNode = new cc.Node();
      rabbonNode.parent = baseNode;
      rabbonNode.x = pos.x;
      rabbonNode.y = pos.y;
      rabbonNode.angle = 60 * Math.random() - 30;
  
      let node = cc.instantiate(nodeList[RandomInt(nodeList.length)]);
      node.parent = rabbonNode;
      node.active = true;
      node.x = 0;
      node.y = 0;
      node.angle = 0;
  
      const rate = Math.random();
      const angle = Math.PI * (Math.random() * 2 - 1);
  
      this.asyncTweenBy(rabbonNode, 0.3, {
        x: side.x * rate + Math.cos(angle) * range * rate,
        y: side.y * rate + Math.sin(angle) * range * rate
      }, {
        easing: 'quadIn'
      }).then(_ => {
        cc.tween(rabbonNode)
          .by(8, { y: -2000 })
          .start();
  
        this.rabbonFall(rabbonNode);
  
        this.asyncDelay(Math.random()).then(_ => {
          cc.tween(node)
            .by(0.15, { x: -10, angle: -10 })
            .by(0.3, { x: 20, angle: 20 })
            .by(0.15, { x: -10, angle: -10 })
            .union()
            .repeatForever()
            .start();
  
          cc.tween(rabbonNode)
            .delay(5)
            .to(0.3, { opacity: 0 })
            .call(() => {
              node.stopAllActions();
              node.active = false;
              node.parent = null;
              node = null;
            })
            .start();
        })
      })

    });
  }















  setStart(number) {
    if (!number) {
      number = 0;
    }
    let length = number;
  
    const starLayout = cc.find('Canvas/layout');
    starLayout.removeAllChildren();
    const paddingY = starLayout.getComponent(cc.Layout).spacingY;
  
    for (let i = 0; i < length; i++) {
      const starBase = cc.instantiate(cc.find('StarBase'));
      starBase.name = `starBase_${i}`;
      starBase.scale = this.Between(0.5, (starLayout.height / length - paddingY) / starBase.height, 1);
      starBase.parent = starLayout;
    }
  }
  
  showStar(currentStarIdx, callback) {
    if(this.g_formData.dataArray.length==1) {
      if(callback) {
        callback()
      }
      return
    }

    this.g_cartoon.playAudio("大星");
    if (!currentStarIdx) {
      currentStarIdx = 0;
    }
  
    const starBase = cc.find(`Canvas/layout/starBase_${currentStarIdx}`);
    if (!starBase) {
      return;
    }
  
    const star = starBase.getChildByName('Star');
    const starBig = star.getChildByName('StarBig');
    starBig.scale = 1;
    star.active = true;
    star.scaleX = 0.7 / starBase.scale;
    star.scaleY = 1 / starBase.scale;
    star.angle = 90;
    const canvas = cc.find('Canvas');
    const startPos = this.exchangeNodePos(star.parent, canvas, cc.v2(0, -canvas.height / 2));
    const middlePos = this.exchangeNodePos(star.parent, canvas, cc.v2(0, -canvas.height / 4));
    star.x = startPos.x;
    star.y = startPos.y - starBig.height;
  
    console.log('middlePos = ' + JSON.stringify(middlePos));
    const time = 1;
    cc.tween(star)
      .to(0.3, { y: middlePos.y + 80 }, { easing: 'quadOut' })
      .to(0.1, { y: middlePos.y + 40, scaleX: 1.2 / starBase.scale, scaleY: 0.8 / starBase.scale }, { easing: 'quadOut' })
      .to(0.1, { y: middlePos.y, scaleX: 1 / starBase.scale, scaleY: 1 / starBase.scale }, { easing: 'quadOut' })
      .delay(0.1)
      .to(0.8, { angle: -720, scale: 1 })
      .start();
  
    cc.tween(star)
      .delay(0.6)
      .to(0.8, { x: 0 }, { easing: 'quadIn' })
      .start();
    cc.tween(star)
      .delay(0.6)
      .to(0.8, { y: 0 }, { easing: 'quadOut' })
      .start();
  
    cc.tween(starBig)
      .delay(0.6)
      .to(0.8, { scale: 0 }, { easing: 'quadOut' })
      .call(() => {
        // this.checkGameEnd();
        this.g_cartoon.playAudio("题数");
        if(callback) {
          callback()
        }
      })
      .start();
  }
  
  Between(a, b, c) {
    return [a, b, c].sort()[1];
  }
  
  exchangeNodePos(targetNode, baseNode, basePos) {
    return targetNode.convertToNodeSpaceAR(baseNode.convertToWorldSpaceAR(cc.v2(basePos.x, basePos.y)));
  }

















  // Common function
  maskLayer = null;
  initMaskLayer() {
    const parentNode = this.g_cartoon.getNodeByPath("MaskLayer")
    parentNode.x = cc.winSize.width / 2
    parentNode.y = cc.winSize.height / 2
    const bNode = this.getSprNodeByResName('black');
    bNode.scaleX = cc.winSize.width / bNode.width;
    bNode.scaleY = cc.winSize.height / bNode.height;
    bNode.x = 0
    bNode.y = 0
    parentNode.setSiblingIndex(255)
    bNode.parent = cc.find('MaskLayer');
    bNode.active = false;
    this.maskLayer = bNode;
  }

  showMaskLayer(cb) {
    this.maskLayer.opacity = 0;
    this.maskLayer.active = true;
    cc.tween(this.maskLayer)
      .to(0.5, {opacity: 255}, {easing: 'sineOut'})
      .call(() => {
        cb()
      })
      .start()
  }

  hideMaskLayer(cb = null) {
    this.maskLayer.opacity = 255;
    cc.tween(this.maskLayer)
    .to(0.5, {opacity: 0}, {easing: 'sineIn'})
    .call(() => {
      this.maskLayer.active = false;
      if (cb) {
        cb()
      }
    })
    .start()
  }

  startTwinkleAnime(speakerBase, path, len, speed = 0.2) {
    const speakers = [];
    for (let index = 0; index < len; index++) {
      const node = cc.find(`${path}_${index}`);
      speakers.push(node);
    }
    let currentIdx = 0;
    cc.tween(speakerBase)
      .call(() => {
        speakers.forEach(node => node.active = false);
        speakers[currentIdx % speakers.length].active = true;
        currentIdx++;
      })
      .delay(speed)
      .union()
      .repeatForever()
      .start();
  }

  stopTwinkleAnime(speakerBase, path, len) {
    speakerBase.stopAllActions();
    for (let index = 0; index < len; index++) {
      const node = cc.find(`${path}_${index}`);
      node.active = false;
    }
    const node0 = cc.find(`${path}_0`);
    node0.active = true;
  }

  // Common API
  getSprNodeByUrl(resName) {
    return new Promise((resolve, reject) => {
      cc.assetManager.loadRemote(url, (err, img) => {
        const node = new cc.Node();
        const spriteFrame = new cc.SpriteFrame(img)
        node.addComponent(cc.Sprite).spriteFrame = sf;
        resolve(node)
      })
    })
  }

  getSprNodeByResName(resName) {
    const sf = cc.find('Canvas/res/img/' + resName).getComponent(cc.Sprite).spriteFrame;
    const node = new cc.Node();
    node.addComponent(cc.Sprite).spriteFrame = sf;
    return node;
  }



  // Sysyem function
  hideAir() {
    if (window && window["air"]) {
      window["air"].hideAirClassLoading();
    }
    cc.debug.setDisplayStats(false);
  }

  checkClickEnable(eventName) {
    if(this.g_enableClick) {
      return true;
    } else {
      // console.log(`Click disabled by event [${this.g_disableClickEventName}]!`)
      return false;
    }
  }

  disableClick(eventName) {
    this.g_enableClick = false;
    this.g_disableClickEventName = eventName
    // console.log(`Click disabled! [${eventName}]`)
  }

  enableClick(eventName) {
    this.g_enableClick = true;
    this.g_disableClickEventName = ""
    // console.log(`Click enabled! [${eventName}]`)
  }

}
