import {
  Component,
  ElementRef,
  ViewChild,
  OnInit,
  Input,
  OnDestroy,
  HostListener
} from "@angular/core";

import {
  MySprite,
  getMinScale,
  ShapeRect,
  ShapeCircle,
  tweenChange,
  randomSortByArr,
  Label,
  showPopParticle,
  moveItem,
  removeItemFromArr,
  rotateItem,
  hideItem,
  showItem,
  ShapeRectNew,
  scaleItem,
  showBlingBling,
  waterWave,
  jelly
} from "./Unit";

import { localImages, localAudios } from "./resources";

import { Cartoon } from './Cartoon'
import { Subject } from "rxjs";
import { debounceTime, map, takeWhile, retry } from "rxjs/operators";
import * as _ from "lodash";
import TWEEN from "@tweenjs/tween.js";
import { callbackify } from 'util';
import { element } from 'protractor';

const defauleFormData = {
  version: "1.1",
  bgmusic: "",
  dataArray:[
    {
      audio_url:"",
      options: [
        {type:"Text", image_url:"default-image", text:"Demo_1", audio_url:"", isCorrect:true},
        {type:"Image", image_url:"default-image", text:"", audio_url:"", isCorrect:false},
        {type:"Text", image_url:"default-image", text:"Demo_1", audio_url:"", isCorrect:false},
        {type:"Image", image_url:"default-image", text:"", audio_url:"", isCorrect:false}
      ]
    }
  ]
}

const zIndexMap = {
  glassJarLeft_B: 1300,
  glassJarLeft_F: 1100,
  glassJarRight_B: 1300,
  glassJarRight_F: 1100,
  glassJarLeft_Highlight: 3299,
  glassJarRight_Highlight: 3299,
  glassJar_B: 1300,
  glassJar_F: 1100,
  glassJar_BH: 3300,
  glassJar_FH: 3100,

  CandyMachine_glass_bg: 2000,
  CandyMachine_rotate_bg: 2000,
  CandyMachine_glass_cover: 2099,
  CandyMachine_Exit_bg: 2100,
  CandyMachine_base : 2200,
  CandyMachine_decorate: 2205,
  CandyMachine_btn_go: 2210,
  CandyMachine_hightlight: 2000,

  question_candy_background: 2300,
  question_candy_btn_open: 2310,
  question_candy_unopen: 2150,

  question_candy_SugarBall: 3200,
  question_candy_wrapper: 3150,
}

@Component({
  selector: "app-play",
  templateUrl: "./play.component.html",
  styleUrls: ["./play.component.scss"]
})

export class PlayComponent implements OnInit, OnDestroy {
  
  g_cartoon = new Cartoon()

  // ------------  全局数据  ------------
  g_stage; //中心舞台
  g_background_color = "#rgb(0,0,0,0)"
  g_enableMapDown = true; // 触摸使能
  g_enableMapUp = true; // 抬起使能
  g_enableMapMove = true; // 移动ss使能
  g_canvasLeft;
  g_canvasTop;
  g_animationId: any;
  g_mapScale = 1; // 缩放比例
  g_KEY = "DataKey_East_L230";
  g_canvasWidth = 1280;
  g_canvasHeight = 720;
  g_canvasBaseW = 1280;
  g_canvasBaseH = 720;
  g_winResizeEventStream = new Subject();
  g_clickX; // 点击坐标 X
  g_clickY; // 点击坐标 Y
  g_ctx; // canvas 实例
  g_data; // 数据
  g_formData; // 核心表单数据
  g_teacherFlag = false; // 默认角色
  g_currentUser;
  g_firstTouch = true;
  // ------------------------------------

  // ------------  私有数据  ------------
  m_mapDownQueue = {} //按下事件处理队列
  m_mapDownArray = [] //按下事件处理队列
  m_mapDownObject = []
  m_mapUpQueue = {} //抬起事件处理队列
  m_mapMoveArray = [] //移动事件处理队列
  m_mapMoveObject = []
  m_endPageArr;
  m_showPetalFlag;
  m_elementPetalArr;
  m_showElementPetalFlag;
  m_PetalImage = "_scrap-pic-"  // 飘落动画
  m_renderArr // 渲染队列
  m_renderObject = [];
  m_defaultZindex = 0;
  m_setTimeoutIDs = [];
  m_setIntervalIDs = [];
  m_moveAsstantIntervalId = null;
  // ------------------------------------
  
  // ------------  游戏逻辑数据  ------------
  m_restartGameFlag = false;
  m_questionCardPos = [
    {x: -450},
    {x: -150},
    {x: 150},
    {x: 450}
  ]
  m_currentQuestion = 0;
  m_bgMusic = null
  m_questionCardPosObj = {
    "2": [
      {x: -300},
      {x: 300},
      {x: 2000},
      {x: 2000}
    ],
    "3": [
      {x: -300},
      {x: 0},
      {x: 300},
      {x: 2000}
    ],
    "4": [
      {x: -450},
      {x: -150},
      {x: 150},
      {x: 450}
    ]
  }
  m_canStopAudio = []
  m_tipsSetinterval_1 = null;
  // ------------------------------------


  // ------------  消息  ------------
  
  // ------------------------------------


  // ------------  调试变量  ------------
  g_EnableStageRuler = false; // 使能舞台背景格尺
  g_ForceChangeDefaultRole = false // 强制当前角色为默认角色
  g_EnableTestSendEvent = false // 发送模拟Web数据
  // ------------------------------------


  // 当数据加载完毕后，执行
  systemReady(){
    this.initGame()
  }
  
  // 屏幕尺寸变化后执行
  handleScreenResize(){
    this.initSystem();
    this.cleanSystemVar()
    this.cleanGameVar()
    this.initGame()
  }

  // 映射预加载图片[网路]资源 返回包含图片路径的数组
  mapToImageArray(contentObj){
    let array = []
    contentObj.dataArray.forEach(item => {
      item.options.forEach(card=>{
        if(card.image_url){
          array.push(card.image_url)
        }
      })
    });
    return array
  }

  // 映射预加载音频[网路]资源 返回包含音频路径的数组
  mapToAduioArray(contentObj){
    let array = []
    contentObj.dataArray.forEach(item => {
      array.push(item.audio_url)
      this.m_canStopAudio.push(item.audio_url)
      item.options.forEach(card=>{
        if(card.audio_url){
          array.push(card.audio_url)
          this.m_canStopAudio.push(card.audio_url)
        }
      })
    })
    return array
  }

  // ------------------------------------------------------------------------------
  // 游戏核心处理区
  // ------------------------------------------------------------------------------
  // ------------------------------------------------------------------------------
  // 
  // 
  // 
  // ------------------------------------------------------------------------------
  // ------------------------------------------------------------------------------

  initGame(){
    this.initBackground()
    this.initBaseLine()
    this.initBlackMask()
    this.initStartButton()
    this.initRestartButton()
    this.initSkier();
    this.initHotZooArrow()
    this.initMilestoneBoardTotal();
    this.initQuestionCards()
    this.initSpeaker()
    this.initQuestionIndexBoard();
    this.initHeader()
  }

  cleanGameVar(){
    this.stopEndPatal()
    this.m_currentQuestion = 0;
    if(this.m_tipsSetinterval_1){
      clearInterval(this.m_tipsSetinterval_1)
    }
    // this.g_cartoon.getCartoonElement(`play-audio`).firstClick = true;
  }

  startGame(){
    this.loadQuestion()
    this.playTips(1)
    this.g_cartoon.playAudio("skier", false ,null, true, (audio)=>{
      this.m_bgMusic = audio
      audio.volume = 0.5
    })
    this.g_cartoon.getCartoonElement(`question-index-board`).resetIndex()
    this.g_cartoon.getCartoonElement("play-audio").in()
    this.g_cartoon.getCartoonElement("skier").ready(()=>{
    })
  }

  restartGame(){
    this.cleanGameVar()
    this.startGame()
  }

  endGame(){
    this.showEndPatal()
    this.muteBgMusic(true)
    this.g_cartoon.playAudio("win", false, ()=>{
      this.muteBgMusic(false)
    })
    this.m_setTimeoutIDs.push(setTimeout(() => {
      this.stopEndPatal()
    }, 3000))
    for(let index=0; index<4; index++){
      let element = this.g_cartoon.getCartoonElement(`question-card-${index}`)
      element.ready()
    }
    this.g_cartoon.getCartoonElement("play-audio").out()
    this.g_cartoon.getCartoonElement("mask-layer").in(()=>{
      this.g_cartoon.getCartoonElement("restart-button").in(()=>{
        this.g_enableMapDown = true
      })
    })
  }

  muteBgMusic(status){
    if(status){
      this.g_cartoon.stopAudio("skier")
    }else{
      this.g_cartoon.playAudio("skier", false ,null, true, (audio)=>{
        this.m_bgMusic = audio
        audio.volume = 0.5
      })
    }
  }

  initBackground(){
    let element = this.g_cartoon.createCartoonElementImage("background-main", "background", this.g_canvasWidth, this.g_canvasHeight, this.g_canvasWidth/2, this.g_canvasHeight/2)
    
    this.render(element.ref)
  }

  initBlackMask(callback?){
    let element = this.g_cartoon.createCartoonElementImage("mask-layer", "_mask_layer_1280_720", this.g_canvasWidth, this.g_canvasHeight, this.g_canvasWidth/2, this.g_canvasHeight/2)
    element.ref.visible = true;

    element.in = (callback?)=>{
      element.ref.visible = true;
      tweenChange(element.ref, {alpha:1}, 0.1, ()=>{
        callback && callback()
      })
    }

    element.out = (callback?)=>{
      tweenChange(element.ref, {alpha:0}, 0.1, ()=>{
        element.ref.visible = false;
        callback && callback()
      })
    }
   
    this.render(element.ref, 998)
  }

  initStartButton(){
    let element = this.g_cartoon.createCartoonElementImage("start-button", "start_btn", 435, 92, this.g_canvasWidth/2, this.g_canvasHeight/2, true)

    element.out = (callback?)=>{
      tweenChange(element.ref, {x: this.g_canvasWidth+500*this.g_mapScale}, 0.3, ()=>{
        callback && callback()
      })
    }

    this.subscribeMapDownEvent(element.id, ()=>{
      element.out(()=>{
        this.g_cartoon.getCartoonElement("mask-layer").out(()=>{
          this.g_enableMapDown = true;
          this.startGame()
        })
      })
    })
    this.render(element.ref, 999)
  }

  initRestartButton(){
    let element = this.g_cartoon.createCartoonElementImage("restart-button", "restart", 436, 91, this.g_canvasWidth/2, this.g_canvasHeight/2, true)
    element.ref.visible = false;
    element.ref.y = this.g_canvasHeight + 500*this.g_mapScale;

    element.in = (callback?)=>{
      element.ref.y = this.g_canvasHeight + 500*this.g_mapScale;
      element.ref.visible = true;
      tweenChange(element.ref, {y: element.initY}, 0.3, ()=>{
        callback && callback()
      })
    }

    element.out = (callback?)=>{
      tweenChange(element.ref, {y: this.g_canvasHeight + 500*this.g_mapScale}, 0.3, ()=>{
        element.ref.visible = false;
        callback && callback()
      })
    }

    this.subscribeMapDownEvent(element.id, ()=>{
      element.out(()=>{
        this.g_cartoon.getCartoonElement("mask-layer").out(()=>{
          this.m_restartGameFlag = true;
          this.restartGame()
        })
      })
    })

    this.render(element.ref, 999)
  }

  // 小手
  initHeader(){
    let element = this.g_cartoon.createCartoonElementImageFunc(`header`, "header", (w,h)=>{
      return {
        sx: 98*this.g_mapScale / w,
        sy: 97*this.g_mapScale / h
      }
    }, (w,h)=>{
      return{
        x: this.g_canvasWidth/2,
        y: this.g_canvasHeight/2
      }
    })

    element.ref.visible = false;;
    this.render(element.ref, 999)
  }

  playTips(type?){
    let loopFunc
    let header = this.g_cartoon.getCartoonElement(`header`)
    let audio = this.g_cartoon.getCartoonElement(`play-audio`)
    let skier = this.g_cartoon.getCartoonElement(`skier`)
      
    if(!type){
      type = 3
    }

    if(type == 1){
      if(this.m_tipsSetinterval_1){
        clearInterval(this.m_tipsSetinterval_1)
      }
      loopFunc = ()=>{
        header.ref.visible = true;
        header.ref.scaleX = header.ref.scaleX * 1.5
        header.ref.scaleY = header.ref.scaleY * 1.5
        header.ref.x = audio.ref.x + this.g_canvasWidth
        header.ref.y = audio.ref.y + this.g_canvasHeight
        tweenChange(header.ref, {x: audio.ref.x+50*this.g_mapScale , y:audio.ref.y+50*this.g_mapScale, scaleX: header.initScaleX, scaleY: header.initScaleY}, 2)
        return loopFunc
      }
      this.m_tipsSetinterval_1 = setInterval(loopFunc(), 3000)
      this.m_setIntervalIDs.push(this.m_tipsSetinterval_1)
      return 
    }

    if(type == 2){
      header.ref.scaleX = header.initScaleX
      header.ref.scaleY = header.initScaleY
      header.ref.x = audio.ref.x+50*this.g_mapScale
      header.ref.y = audio.ref.y+50*this.g_mapScale
      header.ref.visible = true;
      tweenChange(header.ref, {scaleX: header.ref.scaleX * 1.2, scaleY:header.ref.scaleY * 1.2}, 0.2)
      setTimeout(() => {
        tweenChange(header.ref, {scaleX: header.initScaleX, scaleY: header.initScaleY}, 0.2)
      }, 300);
      tweenChange(header.ref, {x: skier.ref.x+50*this.g_mapScale , y:skier.ref.y-50*this.g_mapScale}, 0.5, ()=>{
        header.ref.visible = false;
        this.g_cartoon.getCartoonElement("skier").cruise()
      })
      return 
    }

    if(type == 3){
      setTimeout(()=>{
        header.ref.visible = true;
        header.ref.scaleX = header.ref.scaleX * 1.3
        header.ref.scaleY = header.ref.scaleY * 1.3
        header.ref.x = audio.ref.x + this.g_canvasWidth
        header.ref.y = audio.ref.y + this.g_canvasHeight
        setTimeout(() => {
          tweenChange(header.ref, {x: audio.ref.x+50*this.g_mapScale , y:audio.ref.y+50*this.g_mapScale, scaleX: header.initScaleX, scaleY: header.initScaleY}, 0.5, ()=>{
            setTimeout(() => {
              tweenChange(header.ref, {scaleX: header.ref.scaleX * 1.2, scaleY:header.ref.scaleY * 1.2}, 0.2)
              setTimeout(() => {
                tweenChange(header.ref, {scaleX: header.initScaleX, scaleY: header.initScaleY}, 0.2)
              }, 300);
              tweenChange(header.ref, {x: skier.ref.x+50*this.g_mapScale , y:skier.ref.y-50*this.g_mapScale}, 0.5, ()=>{
                header.ref.visible = false;
                this.g_cartoon.getCartoonElement("skier").cruise()
              })
            }, 1000);
          })
        }, 1000);
      }, 500)
      return 
    }
  }

  initBaseLine(){
    let element = this.g_cartoon.createCartoonElementImage("base-line", "base_line", this.g_canvasWidth, 5*this.g_mapScale, this.g_canvasWidth/2, this.g_canvasHeight*0.8)
    element.ref.visible = false;
    this.render(element.ref)
  }

  getBaseLine(){
    return {
      x: this.g_cartoon.getCartoonElement("base-line").initX,
      y: this.g_cartoon.getCartoonElement("base-line").initY
    }
  }
  
  initHotZooArrow(){
    for(let index=0; index<4; index++){
      let element = this.g_cartoon.createCartoonElementImageFunc(`hot-zoo-arrow-${index}`, "arrow", (w,h)=>{
        return{
          sx: 116*this.g_mapScale / w,
          sy: 261*this.g_mapScale / h
        }
      }, (w,h)=>{
        let adjstX = 0
        if(this.m_questionCardPos[index].x<0){
          adjstX = this.m_questionCardPos[index].x*this.g_mapScale
        }else{
          adjstX = this.m_questionCardPos[index].x*this.g_mapScale
        }
        return{
          x: this.g_canvasWidth/2 + adjstX,
          y: this.getBaseLine().y+20*this.g_mapScale
        }
      })

      element.enable = true;
      element.disable = (status:boolean)=>{
        element.enable = !status
        if(status){
          element.ref.x = 20000
        }else{
          element.ref.x = element.initX
        }
      }

      element.ref.visible = false
      this.render(element.ref,799)
    }
  }

  initQuestionIndexBoard(){
    let element = this.g_cartoon.createCartoonElementImageFunc(`question-index-board`, "stage_number_bg", (w,h)=>{
      return {
        sx: 200*this.g_mapScale / w, 
        sy: 52*this.g_mapScale / h 
      }
    }, (w, h)=>{
      return{
        x: this.g_canvasWidth/2,
        y: 50*this.g_mapScale
      }
    })

    element.text = this.g_cartoon.createLabel("1 | "+this.g_formData.dataArray.length, "BerlinSansFBDemi-Bold", "#FFFFFF", 50)

    element.setIndex = (index)=>{
      if(index==1){
        return
      }
      tweenChange(element.ref, {scaleX:0, scaleY:0}, 0.05, ()=>{
        removeItemFromArr(element.ref.children, element.text)
        element.text = this.g_cartoon.createLabel(index+" | "+this.g_formData.dataArray.length, "BerlinSansFBDemi-Bold", "#FFFFFF", 50)
        element.ref.addChild(element.text)
        tweenChange(element.ref, {scaleX: element.initScaleX, scaleY: element.initScaleY}, 0.2)
      })
    }

    element.resetIndex = ()=>{
      tweenChange(element.ref, {scaleX:0, scaleY:0}, 0.05, ()=>{
        removeItemFromArr(element.ref.children, element.text)
        element.text = this.g_cartoon.createLabel("1 | "+this.g_formData.dataArray.length, "BerlinSansFBDemi-Bold", "#FFFFFF", 50)
        element.ref.addChild(element.text)
        tweenChange(element.ref, {scaleX: element.initScaleX, scaleY: element.initScaleY}, 0.2)
      })
    }

    element.ref.addChild(element.text)
    this.render(element.ref)
  }

  initMilestoneBoardTotal(){
    let totlaQuestion = this.g_formData.dataArray.length
    for(let index=0; index<totlaQuestion; index++){
      let element = this.g_cartoon.createCartoonElementImageFunc(`milestone-board-${index}`, "score_bg", (w,h)=>{
        return {
          sx: 435*this.g_mapScale / w, 
          sy: 92*this.g_mapScale / h 
        }
      }, (w, h)=>{
        return{
          x: this.g_canvasWidth/2,
          y: this.g_canvasHeight/2-(totlaQuestion/2)*100*this.g_mapScale + 120*this.g_mapScale*index
        }
      })

      let text = this.g_cartoon.createLabel((index+1)+"  |  "+totlaQuestion, "BerlinSansFBDemi-Bold", "#FFFFFF", 60)
      element.ref.addChild(text)
      element.ref.visible = false;

      this.render(element.ref, 999)
    }
  }

  initSpeaker(){
    let element = this.g_cartoon.createCartoonElementImageFunc(`play-audio`, "play_audio", (w,h)=>{
      return {
        sx: 96*this.g_mapScale / w,
        sy: 96*this.g_mapScale / h
      }
    }, (w,h)=>{
      return{
        x: this.g_canvasWidth - (190/2)*this.g_mapScale,
        y: 80*this.g_mapScale + (96/2)*this.g_mapScale,
      }
    })

    element.ref.setScaleXY(0)
    element.firstClick = true;

    let audio0 = new MySprite()
    audio0.init(this.g_cartoon.images.get("play_audio_0"))
    audio0.visible = false;
    element.ref.addChild(audio0)
    element.audio_0 = audio0
    let audio1 = new MySprite()
    audio1.init(this.g_cartoon.images.get("play_audio_1"))
    audio1.visible = false;
    element.ref.addChild(audio1)
    element.audio_1 = audio1
    let audio2 = new MySprite()
    audio2.init(this.g_cartoon.images.get("play_audio_2"))
    audio2.visible = false;
    element.ref.addChild(audio2)
    element.audio_2 = audio2

    element.in = ()=>{
      tweenChange(element.ref, {scaleX: element.ref.initScaleX, scaleY: element.ref.initScaleY}, 0.2)
    }
    element.out = ()=>{
      tweenChange(element.ref, {scaleX: 0, scaleY: 0}, 0.2)
    }

    element.playIntervalID = null
    element.play = ()=>{
      let index = 0;
      if(!this.g_formData.dataArray[this.m_currentQuestion].audio_url){
        return
      }
      this.g_cartoon.stopAllAudio(this.m_canStopAudio)
      this.g_cartoon.playAudio(this.g_formData.dataArray[this.m_currentQuestion].audio_url, false, ()=>{
        element.stop();
        this.muteBgMusic(false)
      })
      element.playIntervalID = setInterval(()=>{
        for(let i=0; i<3; i++){
          if(i==index){
            element[`audio_${i}`].visible = true;
          }else{
            element[`audio_${i}`].visible = false;
          }
        }
        index++
        if(index>=3){
          index = 0;
        }
      }, 300)
      this.m_setIntervalIDs.push(element.playIntervalID)
    }
    element.stop = ()=>{
      clearInterval(element.playIntervalID)
      for(let i=0; i<3; i++){
        element[`audio_${i}`].visible = false;
      }
    }

    this.subscribeMapDownEvent(`play-audio`, ()=>{
      clearInterval(this.m_tipsSetinterval_1)
      if(element.firstClick){
        element.firstClick = false;
        this.playTips(2)
      }else{
        this.g_cartoon.getCartoonElement(`header`).ref.visible = false;
      }

      this.g_cartoon.getCartoonElement("skier").enbaleClick = true;
      if(this.g_formData.dataArray[this.m_currentQuestion].audio_url){
        this.g_cartoon.stopAllAudio(this.m_canStopAudio)
        element.play()
      }
      tweenChange(element.ref, {scaleX: element.ref.initScaleX*0.9, scaleY: element.ref.initScaleY*0.9}, 0.1, ()=>{
        tweenChange(element.ref, {scaleX: element.ref.initScaleX, scaleY: element.ref.initScaleY}, 0.1)
      })
      this.g_enableMapDown = true;
    })

    this.render(element.ref)
  }


  initQuestionCards(){
    for(let index=0; index<4; index++){
      let element = this.g_cartoon.createCartoonElementImageFunc(`question-card-${index}`, "question-container", (w,h)=>{
        return {
          sx: 250*this.g_mapScale / w,
          sy: 396*this.g_mapScale / h,
        }
      }, (w, h)=>{
        return{
          x: this.g_canvasWidth/2 + this.m_questionCardPos[index].x*this.g_mapScale,
          y: this.getBaseLine().y - 180*this.g_mapScale
        }
      })

      element.bg = this.g_cartoon.createCartoonElementImageFunc(`question-card-container`, "question_bg", (w,h)=>{
        return {
          sx: 1,
          sy: 1,
        }
      }, (w, h)=>{
        return{
          x: 0,
          y: 0
        }
      })
      element.ref.addChild(element.bg.ref)
      
      element.imageContent = this.g_cartoon.createImage("question_bg", (w, h)=>{
        return{
          sx: 230 / w,
          sy: 146 / h,
        }
      }, (w, h)=>{
        return{
          x: 0,
          y: 0
        }
      })

      element.textContent = this.g_cartoon.createLabel("DEMO", "BerlinSansFBDemi-Bold", null, 50)

      element.down = (callback?)=>{
        if(!element.enable){
          callback && callback()
          return
        }
        element.bg.ref.visible = false;
        element.animationCard = this.g_cartoon.createAnimation("card-frame", 29, 50)
        element.animationCard.setScaleXY(0.3)
        element.ref.addChild(element.animationCard)
        // tweenChange(element.animationSnow, {scaleY:0}, 0.05)
        tweenChange(element.imageContent, {scaleX: 0, scaleY: 0}, 0.05)
        tweenChange(element.textContent, {scaleX: 0, scaleY: 0}, 0.05)
        element.animationCard.play()
        this.m_setTimeoutIDs.push(setTimeout(() => {
          callback && callback()
        }, 500))
      }

      element.shake = (callback?)=>{
        if(!element.enable){
          callback && callback()
          return
        }
        tweenChange(element.ref, {rotation:3}, 0.1, ()=>{
          tweenChange(element.ref, {rotation:-3}, 0.1, ()=>{
            tweenChange(element.ref, {rotation:3}, 0.1, ()=>{
              tweenChange(element.ref, {rotation:-3}, 0.1, ()=>{
                tweenChange(element.ref, {rotation:0}, 0.05, ()=>{
                  callback && callback()
                })
              })
            })
          })
        })
      }

      element.setContent = (data)=>{
        if(!element.enable){
          return
        }
        if(data.type == "Image"){
          if(element.imageContent){
            removeItemFromArr(element.ref.children, element.imageContent)
          }
          element.imageContent = this.g_cartoon.createImage(data.image_url, (w, h)=>{
            let s = Math.min(230*0.9 / w, 146*0.9 / h)
            return{
              sx: s,
              sy: s,
            }
          }, (w, h)=>{
            return{
              x: 0,
              y: -114
            }
          })
          element.ref.addChild(element.imageContent)
          element.imageContent.visible = true;
          element.textContent.visible = false;
        }else{
          if(element.textContent){
            removeItemFromArr(element.ref.children, element.textContent)
          }
          element.textContent = this.g_cartoon.createLabel(data.text, "BerlinSansFBDemi-Bold", "#e05e14", 50, 0, -154)

          element.textContent= this.g_cartoon.createImage("text_container", (w,h)=>{
            return {
              sx: 230*0.9 / w,
              sy: 146*0.9 / h,
            }
          }, (w,h)=>{
            return {
              x: 0,
              y: -114
            }
          })
          let text = this.g_cartoon.createLabel(data.text, "BerlinSansFBDemi-Bold", "#e05e14", 50)
          let lineNum = data.text.split(" ")

          let maxLength = data.text.length//this.getMaxSubstringLength(data.text)

          if(maxLength>6 && maxLength<8){
            text.fontSize =  45
          }else if(maxLength>=8 && maxLength<10){
            text.fontSize =  40
          }else if(maxLength>=10 && maxLength<12){
            text.fontSize =  35
          }else if(maxLength>=12 && maxLength<14){
            text.fontSize = 30
          }else if(maxLength>=14 && maxLength<16){
            text.fontSize = 25
          }else if(maxLength>=16 && maxLength<18){
            text.fontSize = 18
          }else if(maxLength>=18 && maxLength<20){
            text.fontSize = 15
          }else if(maxLength>=20 && maxLength<22){
            text.fontSize = 12
          }else if(maxLength>=22){
            text.fontSize = 10
          }

          element.textContent.addChild(text)
          element.ref.addChild(element.textContent)
          element.imageContent.visible = false;
          element.textContent.visible = true;
        }
      }

      element.out = ()=>{
        if(!element.enable){
          return
        }
        tweenChange(element.ref, {y: -500*this.g_mapScale}, 0.2)
      }

      element.ready = ()=>{
        if(!element.enable){
          return
        }
        element.ref.y = -500*this.g_mapScale
      }

      element.adjustInitY = element.initY
      element.in = ()=>{
        if(!element.enable){
          return
        }
        element.ref.visible = true;
        element.bg.ref.visible = true;
        removeItemFromArr(element.ref.children, element.animationSnow)
        removeItemFromArr(element.ref.children, element.animationCard)
        this.g_cartoon.getCartoonElement("question-index-board").setIndex(this.m_currentQuestion+1)
        tweenChange(element.ref, {y: element.initY}, 0.8, ()=>{
          setTimeout(()=>element.snowBoom())
        })
      }
      
      element.snowBoom = ()=>{
        if(!element.enable){
          return
        }
        element.animationSnow = this.g_cartoon.createAnimation("snow", 5, 50)
        element.animationSnow.x = -7
        element.animationSnow.y = 170
        element.animationSnow.setScaleXY(0.4)
        element.ref.addChild(element.animationSnow)

        element.animationSnow.play()
      }

      element.enable = true;
      element.disable = (status:boolean)=>{
        element.ref.visible = !status
        element.enable = !status
        if(status){
          element.ref.x = 20000
        }else{
          element.ref.x = element.initX
        }
      }

      element.ref.addChild(element.imageContent)
      element.ref.addChild(element.textContent)

      element.ref.visible = false;
      this.render(element.ref)
    }
  }

  loadQuestion(callback?){
    let dataRandom = this.g_formData.dataArray[this.m_currentQuestion].options
    let optionsNumber = this.g_formData.dataArray[this.m_currentQuestion].options.length
    this.controlCardAndHotZoo(optionsNumber)
    dataRandom = this.randomArray_shuffle(dataRandom)
    for(let index=0; index<4; index++){
      let element = this.g_cartoon.getCartoonElement(`question-card-${index}`)
      element.ready()
      element.setContent(dataRandom[index])
      element.in()
    }
    this.m_setTimeoutIDs.push(setTimeout(() => {
      callback && callback()
    }, 800))
  }

  initSkier(){
    let element = this.g_cartoon.createCartoonElementImageFunc("skier", "skier", (w,h)=>{
      return {
        sx: 255*this.g_mapScale / w, 
        sy: 359*this.g_mapScale / h 
      }
    }, (w, h)=>{
      return{
        x: this.g_canvasWidth/2,
        y: this.getBaseLine().y - 50*this.g_mapScale
      }
    })

    element.ref.y = this.g_canvasHeight + 500*this.g_mapScale
    element.ref.scaleX = element.initScaleX * 1.2
    element.ref.scaleY = element.initScaleY * 1.2
    element.enbaleClick = false;

    let header = this.g_cartoon.createCartoonElementImageFunc(`header-inside`, "header", (w,h)=>{
      return {
        sx: 98 / w,
        sy: 97 / h
      }
    }, (w,h)=>{
      return{
        x: 50,
        y: -50
      }
    })
    header.ref.visible = false;
    element.ref.addChild(header.ref)
    element.firstClick = true;
    element.cruise = (callback?)=>{
      if(element.firstClick){
        let time = 0;
        header.ref.visible = true;
        element.cruiseSetTimeoutID = setTimeout(() => {
          let dir = false;
          let step = 10 * this.g_mapScale
          element.cruiseIntervalID = setInterval(()=>{
            if(dir){
              element.ref.x += step
            }else{
              element.ref.x -= step
            }
            if(element.ref.x>this.g_canvasWidth-300*this.g_mapScale || element.ref.x<300*this.g_mapScale){
              dir = !dir
              time++
            }
            if(time>=2 && ( element.ref.x<=(this.g_canvasWidth/2+20*this.g_mapScale) && element.ref.x>=(this.g_canvasWidth/2-20*this.g_mapScale) ) ){
              element.stopCruise()
              element.ref.x = element.ref.initX
            }
          }, 30)
          this.m_setIntervalIDs.push(element.cruiseIntervalID)
          callback && callback()
        }, 500)
        this.m_setTimeoutIDs.push(element.cruiseSetTimeoutID)
      }
    }

    element.stopCruise = ()=>{
      header.ref.visible = false;
      this.g_cartoon.getCartoonElement(`header`).ref.visible = false;
      if(element.cruiseIntervalID){
        clearInterval(element.cruiseIntervalID)
      }
      if(element.cruiseSetTimeoutID){
        clearTimeout(element.cruiseSetTimeoutID)
      }
      element.firstClick = false;
    }

    element.in =(callback?)=>{
      tweenChange(element.ref, {x: element.initX, y: element.initY, scaleX: element.initScaleX, scaleY: element.initScaleY}, 0.8, ()=>{
        callback && callback()
      })
    }

    element.out =(callback?)=>{
      tweenChange(element.ref, {x: element.initX, y: this.g_canvasHeight + 500*this.g_mapScale, scaleX: element.initScaleX * 1.2, scaleY: element.initScaleY * 1.2}, 0.8, ()=>{
        callback && callback()
      })
    }

    element.firstReady = true;
    element.ready =(callback?)=>{
      this.g_cartoon.playAudio("skier", false, ()=>{
        this.muteBgMusic(false)
      })
      tweenChange(element.ref, {x: element.initX, y: element.initY, scaleX: element.initScaleX, scaleY: element.initScaleY}, 0.2, ()=>{
        this.g_enableMapDown = true        
      })
    }

    element.go =(runWayindex, callback?)=>{
      this.g_cartoon.stopAudio(this.g_formData.dataArray[this.m_currentQuestion].audio_url)
      let targetX = this.g_cartoon.getCartoonElement(`question-card-${runWayindex}`).ref.x
      tweenChange(element.ref, {x: targetX, y: this.getBaseLine().y - 100*this.g_mapScale, scaleX: element.initScaleX*0.8, scaleY: element.initScaleY*0.8}, 0.5, ()=>{
        this.g_enableMapDown = false
        callback && callback()
      })
    }

    element.goAhead =(callback?)=>{
      tweenChange(element.ref, {y: this.getBaseLine().y-200*this.g_mapScale, scaleX: element.initScaleX*0.6, scaleY: element.initScaleY*0.6}, 0.5, ()=>{
        callback && callback()
      })
    }

    element.enableDrag = false;

    this.subscribeMapDownEvent(element.id, ()=>{
      if(!this.g_cartoon.getCartoonElementRef("mask-layer").visible && !element.enableDrag && element.enbaleClick){
        element.enableDrag = true
        element.stopCruise()
        let disX = element.ref.x - this.g_clickX
        let disY = element.ref.y - this.g_clickY
        this.g_cartoon.playAudio("move")
        this.muteBgMusic(true)
        this.enableMoveAsstant(()=>{
          element.ref.x = this.g_clickX + disX;
          element.ref.y = this.g_clickY + disY;
          this.highlightRunWay(this.calcRunWayIndex(element.ref.x, element.ref.y))
        })
      }else{
        if(!element.enbaleClick){
          this.g_enableMapDown = true;
        }
      }
    })

    this.subscribeMapUpEvent(element.id, ()=>{
      if(element.enableDrag){
        element.enableDrag = false;
        element.targetRunWay = this.calcRunWayIndex(element.ref.x, element.ref.y)
        this.muteBgMusic(false)
        element.go(element.targetRunWay, ()=>{
          if(this.gameControl(element.targetRunWay)){
            this.g_cartoon.playAudio("sm-choice-correct", false, ()=>{
              this.g_cartoon.playAudio("win")
            })
            element.goAhead(()=>{
              this.nextQuestion(()=>{
                this.loadQuestion()
                element.ready();
                this.g_cartoon.playAudio("move")
              })
            })
          }else{
            this.g_cartoon.playAudio("sm-choice-error",false,()=>{
              this.g_cartoon.playAudio("move")
            })
            this.g_cartoon.getCartoonElement(`question-card-${element.targetRunWay}`).shake(()=>{
              element.ready()
            })
            
            element.targetRunWay = -1;
          }
        })
        this.disableMoveAsstant();
        this.highlightRunWay(-1);
        this.g_enableMapUp = true;
      }else{
        this.g_enableMapUp = true;
      }
    })

    this.render(element.ref, 800)
  }

  showMMilestoneBoardTotal(index, callback?){
    for(let i=0; i<this.g_formData.dataArray.length; i++){
      if(i<index){
       this.g_cartoon.getCartoonElement(`milestone-board-${i}`).ref.visible = true;
      }else{
        this.g_cartoon.getCartoonElement(`milestone-board-${i}`).ref.visible = false;
      }
    }
    
    this.m_setTimeoutIDs.push(setTimeout(() => {
      for(let i=0; i<this.g_formData.dataArray.length; i++){
        this.g_cartoon.getCartoonElement(`milestone-board-${i}`).ref.visible = false;
      }
      callback && callback()
    }, 2000))
  }

  calcRunWayIndex(x, y){
    let x0 = this.g_cartoon.getCartoonElement(`hot-zoo-arrow-0`).ref.x
    let x1 = this.g_cartoon.getCartoonElement(`hot-zoo-arrow-1`).ref.x
    let x2 = this.g_cartoon.getCartoonElement(`hot-zoo-arrow-2`).ref.x
    let x3 = this.g_cartoon.getCartoonElement(`hot-zoo-arrow-3`).ref.x

    let dis = [
      Math.abs(x - x0),
      Math.abs(x - x1),
      Math.abs(x - x2),
      Math.abs(x - x3)
    ]
    var min = Math.min(...dis);
    return dis.indexOf(min)
  }

  highlightRunWay(index){
    for(let i=0; i<4; i++){
      if(index==i){
        this.g_cartoon.getCartoonElement(`hot-zoo-arrow-${i}`).ref.visible = true
      }else{
        this.g_cartoon.getCartoonElement(`hot-zoo-arrow-${i}`).ref.visible = false
      }
    }
  }

  gameControl(answerIndex){
    if(this.g_formData.dataArray[this.m_currentQuestion].options[answerIndex].isCorrect){
      this.g_cartoon.getCartoonElement(`question-card-${answerIndex}`).down()
      return true
    }else{
      return false
    }
  }

  nextQuestion(callback?){
    this.m_setTimeoutIDs.push(setTimeout(() => {
      if(this.m_currentQuestion>=this.g_formData.dataArray.length-1){
        this.endGame()
      }else{
        this.m_currentQuestion++
        for(let index=0; index<4; index++){
          this.g_cartoon.getCartoonElement(`question-card-${index}`).ref.visible = false;
        }
        this.showEndPatal()
        this.m_setTimeoutIDs.push(setTimeout(() => {
          this.stopEndPatal()
          this.playTips(1)
          this.g_cartoon.getCartoonElement("skier").enbaleClick = false
          callback && callback()
        }, 3000))
        
      }
    }, 1000))
  }

  controlCardAndHotZoo(num){
    let allQuestionCards = []
    let hotZooArrows = []
    for(let index=0; index<4; index++){
      allQuestionCards.push(this.g_cartoon.getCartoonElement(`question-card-${index}`))
      hotZooArrows.push(this.g_cartoon.getCartoonElement(`hot-zoo-arrow-${index}`))
    }
    for(let index=0; index<4; index++){
      if(index<num){
        allQuestionCards[index].disable(false)
        hotZooArrows[index].disable(false)
        allQuestionCards[index].ref.x = this.g_canvasWidth/2 + this.m_questionCardPosObj[num][index].x*this.g_mapScale
        hotZooArrows[index].ref.x = this.g_canvasWidth/2 + this.m_questionCardPosObj[num][index].x*this.g_mapScale
      }else{
        allQuestionCards[index].disable(true)
        hotZooArrows[index].disable(true)
      }
    }
  }
  





































  // --------------------------------------------------
  // --------------  Template function  ---------------
  // --------------------------------------------------
  // --------------------------------------------------
  // --------------------------------------------------
  // 
  //                                  _________
  //                                 /         /.
  //           .-------------.      /_________/ |
  //         /             / |      |         | |
  //        /+============+\ |      | |====|  | |
  //        ||C:\>        || |      |         | |
  //        ||            || |      | |====|  | |
  //        ||            || |      |   ___   | |
  //        ||            || |      |  |166|  | |
  //        ||            ||/@@@    |   ---   | |
  //        \+============+/    @   |_________|./.
  //                          @          ..  ....'
  //        ..................@     __.'.'  ''
  //      /oooooooooooooooo//     ///
  //      /................//     /_/
  //      ------------------
  //
  // --------------------------------------------------

  @ViewChild("canvas", {static: true }) canvas: ElementRef;
  @ViewChild("wrap", {static: true }) wrap: ElementRef;
  @HostListener("window:resize", ["$event"])
  onResize(event) {
    this.g_winResizeEventStream.next();
  }
  ngOnDestroy() {
    window["curCtx"] = null;
    window.cancelAnimationFrame(this.g_animationId);
  }
  ngOnInit() {
    const getData = (<any>window).courseware.getData;
    getData(data => {
      // if (window['air'].airClassInfo.user.classRole == 'tea') {
      //   if(!this.g_ForceChangeDefaultRole){
      //     this.g_teacherFlag = true;
      //   }
      // }
      // this.g_currentUser = window['air'].airClassInfo.user;
      if (data && typeof data == "object") {
        this.g_data = data;
        this.g_formData = data.contentObj;
      } else {
        this.g_data = {};
      }

      if (!this.g_data.contentObj) {
        this.g_data.contentObj = {};
        this.g_formData = {};
      }
     
      this.initDefaultData();
      this.initAudio();
      this.initImg();
      
       // 预加载资源
       this.g_cartoon.loadResources().then(() => {
        window["air"].hideAirClassLoading(this.g_KEY, this.g_data);
        this.initSystem();
        this.update();
        this.systemReady()
      });

      this.initListener();
    }, this.g_KEY);
  }

  // ----------------------------------
  // 初始化默认数据
  // ----------------------------------
  initDefaultData() {
    if ( Object.keys(this.g_formData).length===0 || defauleFormData.version!= this.g_formData.version || this.g_formData.dataArray.length==0) {
      // console.log("Init default form data")
      this.g_formData = defauleFormData;
    }
  }


  // ----------------------------------
  // 初始化音乐
  // ----------------------------------
  initAudio() {
    const contentObj = this.g_formData;
    if (!contentObj) {
      return;
    }
    // 添加用户上传音效
    let images:Array<string> = this.mapToAduioArray(contentObj)
    images.forEach(image => {
      this.g_cartoon.addAudio( image, image );
    });

    // 添加本地音效
    for( var key in localAudios ){
      this.g_cartoon.addAudio( key, localAudios[key] );
    }
  }

  // ----------------------------------
  // 初始化图片
  // ----------------------------------
  initImg() {
    const contentObj = this.g_formData;
    if (contentObj) {
      const addPicUrl = url => {
        if (url) {
          this.g_cartoon.addImage(url, url);
        }
      };
      let images:Array<string> = this.mapToImageArray(contentObj)
      images.forEach(image => {
        addPicUrl(image)
      });
    }
    // 添加本地图片
    for( var key in localImages ){
     this.g_cartoon.addImage( key, localImages[key] );
    }
  }


  mapDown(event) {
    let myStopPropagation = false;
    if (!this.g_enableMapDown) {
      return;
    }

    this.m_mapDownArray.forEach((item)=>{
      if(!myStopPropagation){
        if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(item.id))) {
          this.g_enableMapDown = false;
          if(item.callback()){
            myStopPropagation = true;
          }
        }
      }
    })


  }

  mapMove(event) {
    let myStopPropagation = false;
    if (!this.g_enableMapMove) {
      return;
    }

    this.m_mapMoveArray.forEach((item)=>{
      if(!myStopPropagation){
        if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(item.id))) {
          this.g_enableMapMove = false;
          if(item.callback()){
            myStopPropagation = true;
          }
        }
      }
    })
  }

  mapUp(event) {
    if (!this.g_enableMapUp) {
      return;
    }
    for(let cartoonID in this.m_mapUpQueue){
      if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(cartoonID))) {
        this.g_enableMapUp = false;
        this.m_mapUpQueue[cartoonID]();
      }
    }
  }


  update() {
    this.g_animationId = window.requestAnimationFrame(this.update.bind(this));
    // 清除画布内容
    this.g_ctx.clearRect(0, 0, this.g_canvasWidth, this.g_canvasHeight);
    TWEEN.update();
    this.updateArr(this.m_renderArr);
    this.updateArr(this.m_endPageArr);
    this.updateArr(this.m_elementPetalArr);
  }


  updateItem(item) {
    if (item) {
      item.update();
    }
  }


  updateArr(arr) {
    if (!arr) {
      return;
    }
    for (let i = 0; i < arr.length; i++) {
      arr[i].update(this);
    }
  }


  initListener() {
    const element = this.canvas.nativeElement;
    this.g_winResizeEventStream.pipe(debounceTime(500)).subscribe(data => {
      this.renderAfterResize();
    });
    
    const addTouchListener = () => {
      element.addEventListener('touchstart', touchDownFunc);
      element.addEventListener('touchmove', touchMoveFunc);
      element.addEventListener('touchend', touchUpFunc);
      element.addEventListener('touchcancel', touchUpFunc);
    };
    const removeTouchListener = () => {
      element.removeEventListener('touchstart', touchDownFunc);
      element.removeEventListener('touchmove', touchMoveFunc);
      element.removeEventListener('touchend', touchUpFunc);
      element.removeEventListener('touchcancel', touchUpFunc);
    };

    const addMouseListener = () => {
      element.addEventListener('mousedown', mouseDownFunc);
      element.addEventListener('mousemove', mouseMoveFunc);
      element.addEventListener('mouseup', mouseUpFunc);
    };
    const removeMouseListener = () => {
      element.removeEventListener('mousedown', mouseDownFunc);
      element.removeEventListener('mousemove', mouseMoveFunc);
      element.removeEventListener('mouseup', mouseUpFunc);
    };

    const touchDownFunc = (e) => {
      if (this.g_firstTouch) {
        this.g_firstTouch = false;
        removeMouseListener();
      }
      setMxMyByTouch(e);
      this.mapDown(e);
    };
    const touchMoveFunc = (e) => {
      setMxMyByTouch(e);
      this.mapMove(e);
    };
    const touchUpFunc = (e) => {
      setMxMyByTouch(e);
      this.mapUp(e);
    };

    const mouseDownFunc = (e) => {
      if (this.g_firstTouch) {
        this.g_firstTouch = false;
        removeTouchListener();
      }
      setMxMyByMouse(e);
      this.mapDown(e);
    };
    const mouseMoveFunc = (e) => {
      setMxMyByMouse(e);
      this.mapMove(e);
    };
    const mouseUpFunc = (e) => {
      setMxMyByMouse(e);
      this.mapUp(e);
    };

    const setMxMyByTouch = event => {
      if (event.touches.length <= 0) {
        return;
      }
      if (this.g_canvasLeft == null) {
        setParentOffset();
      }
      this.g_clickX = event.touches[0].pageX - this.g_canvasLeft;
      this.g_clickY = event.touches[0].pageY - this.g_canvasTop;
    };

    const setParentOffset = () => {
      const rect = this.canvas.nativeElement.getBoundingClientRect();
      this.g_canvasLeft = rect.left;
      this.g_canvasTop = rect.top;
    };

    const setMxMyByMouse = (event) => {
      this.g_clickX = event.offsetX;
      this.g_clickY = event.offsetY;
    };

    addMouseListener();
    addTouchListener();
  }

  showArr(arr) {
    if (!arr) {
      return;
    }
    for (let i = 0; i < arr.length; i++) {
      arr[i].visible = true;
    }
  }

  hideArr(arr) {
    if (!arr) {
      return;
    }
    for (let i = 0; i < arr.length; i++) {
      arr[i].visible = false;
    }
  }

  IsPC() {
    if (window["ELECTRON"]) {
      return false; // 封装客户端标记
    }
    if (
      document.body.ontouchmove !== undefined &&
      document.body.ontouchmove !== undefined
    ) {
      return false;
    } else {
      return true;
    }
  }

  renderAfterResize() {
    this.g_canvasWidth = this.wrap.nativeElement.clientWidth;
    this.g_canvasHeight = this.wrap.nativeElement.clientHeight;
    this.update();
    this.handleScreenResize()
  }

  checkClickTarget(target) {
    if (!target) {
      return false;
    }
    const rect = target.getBoundingBox();
    if (this.checkPointInRect(this.g_clickX, this.g_clickY, rect)) {
      return true;
    }
    return false;
  }


  getWorlRect(target) {
    let rect = target.getBoundingBox();
    if (target.parent) {
      const pRect = this.getWorlRect(target.parent);
      rect.x += pRect.x;
      rect.y += pRect.y;
    }
    return rect;
  }

  checkPointInRect(x, y, rect) {
    if (x >= rect.x && x <= rect.x + rect.width) {
      if (y >= rect.y && y <= rect.y + rect.height) {
        return true;
      }
    }
    return false;
  }


  getPosByAngle(angle, len) {
    const radian = (angle * Math.PI) / 180;
    const x = Math.sin(radian) * len;
    const y = Math.cos(radian) * len;
    return { x, y };
  }


  getPosDistance(sx, sy, ex, ey) {
    const _x = ex - sx;
    const _y = ey - sy;
    const len = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
    return len;
  }


  subscribeMapDownEvent(id,callback, zIndex?){
    zIndex = zIndex?zIndex:0
    this.m_mapDownObject.push({id:id, zIndex:zIndex?zIndex:1, callback:callback})
    this.m_mapDownObject.sort((a,b)=>{
      return b.zIndex-a.zIndex
    })
    this.m_mapDownArray = []
    this.m_mapDownObject.forEach(item=>{
      this.m_mapDownArray.push(item)
    })
  }

  subscribeMapUpEvent(id,callback){
    this.m_mapUpQueue[id] = callback
  }

  subscribeMapMoveEvent(id, callback, zIndex?){
    zIndex = zIndex?zIndex:0
    this.m_mapMoveObject.push({id:id, zIndex:zIndex?zIndex:1, callback:callback})
    this.m_mapMoveObject.sort((a,b)=>{
      return a.zIndex - b.zIndex
    })
    this.m_mapMoveArray = []
    this.m_mapMoveObject.forEach(item=>{
      this.m_mapMoveArray.push(item)
    })
  }


  // 全局初始化入口，当资源加载完毕后执行
  initSystem() {
    
    this.g_canvasWidth = this.wrap.nativeElement.clientWidth;
    this.g_canvasHeight = this.wrap.nativeElement.clientHeight;

    const sx = this.g_canvasWidth / this.g_canvasBaseW;
    const sy = this.g_canvasHeight / this.g_canvasBaseH;
    const s = Math.min(sx, sy);

    this.g_mapScale = s;
    this.g_cartoon.mapScale = this.g_mapScale;
    this.g_cartoon.clientWidth = this.wrap.nativeElement.clientWidth;
    this.g_cartoon.clientHeight = this.wrap.nativeElement.clientHeight;

    this.m_renderArr = [];
    this.m_renderObject = [];
    this.g_enableMapDown = true;

    this.g_ctx = this.canvas.nativeElement.getContext("2d");
    this.canvas.nativeElement.width = this.g_canvasWidth;
    this.canvas.nativeElement.height = this.g_canvasHeight;
    window["curCtx"] = this.g_ctx;

    // 初始化舞台
    this.initStage();
    this.update();
  }
  

  initStage() {
    const bgWidth = 1280;
    const bgHeight = 720;
    this.g_cartoon.stageWidth = bgWidth*this.g_mapScale;
    this.g_cartoon.stageHeight= bgHeight*this.g_mapScale;

    const imageColor = this.g_cartoon.createCartoonElement("background-color", "ShapeRect").ref;
    // console.log( "stageWidth: " + this.g_cartoon.stageWidth + " stageHeight" + this.g_cartoon.stageHeight)
    // console.log( "clientWidth: " + this.g_cartoon.clientWidth + " clientHeight" + this.g_cartoon.clientHeight)
    // console.log( "canvasWidth: " + this.g_canvasWidth + " canvasHeight" + this.g_canvasHeight)
    // console.log( "mapScale: " + this.g_mapScale )
    imageColor.x = this.g_cartoon.clientWidth / 2 ;
    imageColor.y = this.g_cartoon.clientHeight / 2 ;
    imageColor.setSize(this.g_cartoon.clientWidth, this.g_cartoon.clientHeight);
    imageColor.init();
    imageColor.fillColor = this.g_background_color;
    this.render(imageColor, -999999)

    const image = this.g_cartoon.createCartoonElement("bg_1280_720_Ruler", "MySprite").ref;
    image.init(this.g_cartoon.images.get("_bg_1280_720_Ruler"));
    image.x = this.g_canvasWidth / 2;
    image.y = this.g_canvasHeight /2;
    image.visible = this.g_EnableStageRuler
    this.g_cartoon.setOrigin( image.x-(bgWidth/2*this.g_mapScale), image.y-(bgHeight/2*this.g_mapScale)  )
    this.g_cartoon.setRelativeOrigin( -bgWidth/2, -bgHeight/2)
    image.setScaleXY(this.g_mapScale);
    this.render(image, -999999)
    this.g_stage = image
  }

  render(ele, zIndex?:number){
    this.m_renderObject.push({element:ele, zIndex:zIndex?zIndex:1})
    this.m_renderObject.sort((a,b)=>{
      return a.zIndex - b.zIndex
    })
    this.m_renderArr = []
    this.m_renderObject.forEach(item=>{
      this.m_renderArr.push(item.element)
    })    
  }

  sendServerEvent(key, data) {
    const c = (<any> window).courseware;
    c.sendEvent(key, JSON.stringify(data));
  }

  addServerListener(msg_key, callback) {
    const c = (<any> window).courseware;
    c.onEvent(msg_key, (data,next) => {
      callback(JSON.parse(data))
      next && next();
    });
  }

  randomArray_shuffle(array) {
    var input = array;
    for (var i = input.length-1; i >=0; i--) {
        var randomIndex = Math.floor(Math.random()*(i+1));
        var itemAtIndex = input[randomIndex];
        input[randomIndex] = input[i];
        input[i] = itemAtIndex;
    }
    return input;
  }

  paginationArray(pageNo, pageSize, array) {
    var offset = (pageNo - 1) * pageSize;
    return (offset + pageSize >= array.length) ? array.slice(offset, array.length) : array.slice(offset, offset + pageSize);
  }

  stopAllTimeout(){
    this.m_setTimeoutIDs.forEach(id=>clearTimeout(id))
    this.m_setTimeoutIDs = []
  }

  stopAllInterval(){
    this.m_setIntervalIDs.forEach(id=>clearInterval(id))
    this.m_setIntervalIDs = []
  }

  topOfRenderArray(element){
    let index = this.m_renderArr.indexOf(element)
    if(index !=-1){
      this.m_renderArr.splice(index, 1)
      this.m_renderArr.push(element)
    }   
  }

  setRenderZIndex(element, zIndex){
    let index = null;
    for(let i=0; i<this.m_renderObject.length; i++){
      if( this.m_renderObject[i].element.id == element.id ){
        index = i
      }
    }
    if(index){
      this.m_renderObject.splice(index, 1)
      this.m_renderObject.push({element:element, zIndex:zIndex?zIndex:1})
      this.m_renderObject.sort((a,b)=>{
        return a.zIndex - b.zIndex
      })
  
      this.m_renderArr = []
      this.m_renderObject.forEach(item=>{
        this.m_renderArr.push(item.element)
      })
    }
  }
  
  deleteElementInRender(id){
    let index = null;
    for(let i=0; i<this.m_renderObject.length; i++){
      if( this.m_renderObject[i].element.id == id ){
        index = i
      }
    }
    if(index){
      this.m_renderObject.splice(index, 1)
      this.m_renderObject.sort((a,b)=>{
        return a.zIndex - b.zIndex
      })
      this.m_renderArr = []
      this.m_renderObject.forEach(item=>{
        this.m_renderArr.push(item.element)
      })
    }
  }

  enableMoveAsstant(callback){
    if(this.m_moveAsstantIntervalId){
      clearInterval(this.m_moveAsstantIntervalId)
    }
    this.m_moveAsstantIntervalId = setInterval(()=>{
      callback()
    },50)
  }

  disableMoveAsstant(){
    clearInterval(this.m_moveAsstantIntervalId)
  }

  cleanSystemVar(){
    this.m_mapDownQueue = {}
    this.m_mapDownArray = []
    this.m_mapDownObject = []
    this.m_mapUpQueue = {}
    this.m_mapMoveArray = []
    this.m_mapMoveObject = []
    this.stopAllInterval();
    this.stopAllTimeout()
    this.g_cartoon.stopAllAudio()
  }

  getMaxSubstringLength(str){
    let maxLength = 0;
    let subSubstring = str.split(" ")
    for (let index=0; index<subSubstring.length; index++) {
      if(subSubstring[index].length > maxLength){
        maxLength = subSubstring[index].length;
      }
    }
    return maxLength
  }













  













  












  // --------------------------------------------------
  // --------------  Template function  ---------------
  // --------------------------------------------------
  // --------------------------------------------------
  // --------------------------------------------------
  // 
  //                .-~~~~~~~~~-._       _.-~~~~~~~~~-.
  //            __.'              ~.   .~              `.__
  //          .'//                  \./                  \\`.
  //        .'//                     |                     \\`.
  //      .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
  //    .'//.-"                 `-.  |  .-'                 "-.\\`.
  //  .'//______.============-..   \ | /   ..-============.______\\`.
  //.'______________________________\|/______________________________`.
  //
  // --------------------------------------------------
  // showParticle( element_id )                 泡泡效果
  // --------------------------------------------------
  // showEndPatal()  /  stopEndPatal()  花瓣飘落结束动画 
  // --------------------------------------------------
  // showCorrectPatal()                 指定元素上面飘花
  // --------------------------------------------------
  // convertPercentToRadian()   将百分比转换为弧长 第一个参数是百分比，第二个参数是方向 true为逆时针，false为顺时针。用于ShapeCircle换圆弧
  // --------------------------------------------------
  // movePaoWuxian()                        元素抛物线跳
  // --------------------------------------------------
  // showJellyAnimation()------------------------------
  // showBlingStar()----------------------显示星星效果--
  // --------------------------------------------------
  


  // 泡泡
  showParticle(card) {
    let myCard = this.g_cartoon.getCartoonElementRelativePosition(card.id)
    showPopParticle(this.g_cartoon.images.get("_bubble"), { x: myCard.x , y: myCard.y }, this.g_stage);
  }

  // 选择正确动画
  showCorrectPatal(card_id, showTime, callback?) {
    this.m_elementPetalArr = [];
    this.m_showElementPetalFlag = true;
    this.addCorrectPetal(card_id);
    setTimeout(()=>{
      this.m_elementPetalArr = [];
      this.m_showElementPetalFlag = false;
      callback && callback()
    },showTime)
  }
  stopAllCorrectPatal(){
    this.m_elementPetalArr = [];
    this.m_showElementPetalFlag = false;
  }

  addCorrectPetal(card_id) {
    if (!this.m_showElementPetalFlag) {
      return;
    }

    let element = this.g_cartoon.getCartoonElement(card_id)
    const petal = new MySprite(this.g_ctx);

    const id = Math.ceil(Math.random() * 3);
    petal.init(this.g_cartoon.images.get(this.m_PetalImage + id));

    const randomS = (Math.random() * 0.4 + 0.6) * this.g_mapScale * 0.5;
    petal.setScaleXY(randomS);

    const randomR = Math.random() * 360;
    petal.rotation = randomR;

    const randomX = Math.random() * element.ref.width * this.g_mapScale;

    petal.x = element.ref.x - (element.ref.width * this.g_mapScale / 2) + randomX;
    petal.y = element.ref.y - (element.ref.height * this.g_mapScale / 2);

    const randomT = 1 + Math.random() * 2;
    petal["time"] = randomT;

    let randomTR = 360 * Math.random(); // - 180;
    if (Math.random() < 0.5) {
      randomTR *= -1;
    }
    petal["tr"] = randomTR;

    this.m_elementPetalArr.push(petal);
    moveItem(
      petal,
      petal.x,
      element.ref.y + (element.ref.height * this.g_mapScale / 2),
      petal["time"],
      () => {
        removeItemFromArr(this.m_elementPetalArr, petal);
      }
    );
    rotateItem(petal, petal["tr"], petal["time"]);
    setTimeout(() => {
      this.addCorrectPetal(card_id);
    }, 200);
  }


  // 结束动画花瓣飘落
  showEndPatal() {
    this.m_endPageArr = [];
    this.m_showPetalFlag = true;
    this.addPetal();
  }

  stopEndPatal() {
    this.m_endPageArr = [];
    this.m_showPetalFlag = false;
  }

  addPetal() {
    if (!this.m_showPetalFlag) {
      return;
    }
    const petal = this.getPetal();
    this.m_endPageArr.push(petal);
    moveItem(
      petal,
      petal.x,
      this.g_canvasHeight + petal.height * petal.scaleY,
      petal["time"],
      () => {
        removeItemFromArr(this.m_endPageArr, petal);
      }
    );
    rotateItem(petal, petal["tr"], petal["time"]);
    setTimeout(() => {
      this.addPetal();
    }, 100);
  }

  getPetal() {
    const petal = new MySprite(this.g_ctx);

    const id = Math.ceil(Math.random() * 3);
    petal.init(this.g_cartoon.images.get(this.m_PetalImage + id));

    const randomS = (Math.random() * 0.4 + 0.6) * this.g_mapScale;
    petal.setScaleXY(randomS);

    const randomR = Math.random() * 360;
    petal.rotation = randomR;

    const randomX = Math.random() * this.g_canvasWidth;
    petal.x = randomX;
    petal.y = (-petal.height / 2) * petal.scaleY;

    const randomT = 2 + Math.random() * 5;
    petal["time"] = randomT;

    let randomTR = 360 * Math.random(); // - 180;
    if (Math.random() < 0.5) {
      randomTR *= -1;
    }
    petal["tr"] = randomTR;

    return petal;
  }


  showJellyAnimation(element_id){
    let element = this.g_cartoon.getCartoonElement(element_id).ref
    tweenChange(element,{ scaleX: this.g_mapScale * 1.1 , scaleY: this.g_mapScale * 1.1 }, 0.1, ()=>{
      tweenChange(element,{ scaleX: this.g_mapScale * 0.9 , scaleY: this.g_mapScale * 0.9 }, 0.1, ()=>{
        tweenChange(element,{ scaleX: this.g_mapScale , scaleY: this.g_mapScale }, 0.1 )
      })
    })
  }

  showShakeAnimation(element_id){
    let element = this.g_cartoon.getCartoonElement(element_id).ref
    let originX = element.x
    tweenChange(element,{ x: originX - 10*this.g_mapScale }, 0.1, ()=>{
      tweenChange(element,{ x: originX + 10*this.g_mapScale }, 0.1, ()=>{
        tweenChange(element,{ x: originX }, 0.1 )
      })
    })
  }

  convertPercentToRadian(per, direction){
    let radian = 0;
    if(direction) { //逆时针
      if(per*360 > 270){
        radian = 361 - (per*360 - 270)
      }else{
        radian = 270 - per*360
      }
    }else{ //顺时针
      if(per*360 > 90){
        radian = per*360 - 90
      }else{
        radian = 270 + per*360
      }
    }
    return (radian * Math.PI) / 180;
  }

  showBlingStar(element){
    let rect =  element.getBoundingBox()
    showBlingBling(this.g_cartoon.images.get('_star'), rect, element, 0.5, 1, 0.08, 2);
    setTimeout(()=>{
      showBlingBling(this.g_cartoon.images.get('_star'), rect, element, 0.5, 1, 0.08, 1.8);
    },200)
    setTimeout(()=>{
      showBlingBling(this.g_cartoon.images.get('_star'), rect, element, 0.5, 1, 0.08, 1.6);
    },400)
  }

  movePaoWuxian(element,dis,time,callback?){
    let disX = dis.x - element.x
    let disY = (element.y - dis.y) 
    let count = 0
    let runTime = time/5
    let vx = disX/runTime
    let a = -0.05
    let vy0 = disY/runTime - 0.5*a*runTime
    let id = setInterval(()=>{
      element.x += vx
      element.y -= vy0 + a*count
      count++;
      if(count>runTime){
        clearInterval(id)
        callback && callback()
      }
    }, 5);
  }
}

