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
} from "./Unit";

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

import { Cartoon } from './Cartoon'
import { Subject } from "rxjs";
import { debounceTime } from "rxjs/operators";
import * as _ from "lodash";
import TWEEN from "@tweenjs/tween.js";

const defauleFormData = {
  dataArray:[
    {
      time: 15,
      question:{
        text:"This is Demo, please set question in form page",
        image_url:"question-image",
        audio_url:""
      },
      choice:{
        correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
        incorrect:[
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
        ]
      }
    },
    {
      time: 15,
      question:{
        text:"This is Demo, please set question in form page",
        image_url:"",
        audio_url:"sm-display"
      },
      choice:{
        correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
        incorrect:[
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
        ]
      }
    },
    {
      time: 15,
      question:{
        text:"",
        image_url:"question-image",
        audio_url:"sm-display"
      },
      choice:{
        correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
        incorrect:[
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
        ]
      }
    },
    {
      time: 15,
      question:{
        text:"This is Demo, please set question in form page",
        image_url:"",
        audio_url:""
      },
      choice:{
        correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
        incorrect:[
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
          { isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
        ]
      }
    }
  ]
}

@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_enableTouch = true; // 触摸使能
  g_canvasLeft;
  g_canvasTop;
  g_animationId: any;
  g_mapScale = 1; // 缩放比例
  g_KEY = "DataKey_MultipleChoice";
  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;
  // ------------------------------------

  // ------------  私有数据  ------------
  m_clickQueue = {} //点击事件处理队列
  m_endPageArr;
  m_showPetalFlag;
  m_elementPetalArr;
  m_showElementPetalFlag;
  m_PetalImage = "petal_"  // 飘落动画
  m_renderArr // 渲染队列
  m_currentQuestion = 0;
  m_totalQuestion = 0;
  m_isGameEnd = true;
  m_correctNumber = 0;
  m_totalTime = 0;
  m_autoPlay;
  // ------------------------------------


  // ------------  消息  ------------
  m_Message_Start = "message_start"
  m_Message_Complete = "message_complete"
  m_Message_NextQuestion = "message_nextQuestion"
  // ------------------------------------


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


  g_serverAllUser;
  g_startBtn;

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

  restartGame() {
    this.g_cartoon.getCartoonElement("timer").stop()
    this.g_cartoon.stopAllAudio()
    this.stopEndPatal()
    this.initGame()
  }



  // ------------------------------------------------------------------------------
  // 游戏核心处理区
  // ------------------------------------------------------------------------------
  // ------------------------------------------------------------------------------
  // initGame 初始化游戏
  // startGame_Student  在收到开始信息后开始执行
  // startGame_Teacher  教师点击开始按钮后执行
  // startAnswer 开始答题
  // endAnswer 结束答题
  // showRankingList 显示排行榜
  // initTestEvent 用于在测试时模拟发送消息
  // ------------------------------------------------------------------------------
  // ------------------------------------------------------------------------------

  initGame(){
    this.stopEndPatal();
    if(!this.g_EnableStageRuler){
      this.initBackground();
    }
    this.m_currentQuestion = 0;
    this.m_totalQuestion = 0;
    this.m_isGameEnd = true;
    this.m_correctNumber = 0;
    this.m_totalTime = 0;
    this.initStartPage();
    this.initAnswer();
    this.initReadyGoBackground();
    this.initReadyGoContainer();
    this.initRankingList();
    this.g_cartoon.getCartoonElement("timer").subscribeTimeup(()=>{
      this.handleTimeup();
    });
    
    if(this.g_EnableTestSendEvent){
      this.initTestEvent();
    }

    this.syncServerPage();

  }


  syncServerPage() {
    const allUser = this.g_serverAllUser;
    if (allUser) {
      console.log('allUser: ', allUser);

      for (let i = 0; i < allUser.length; i++) {
        const userData = allUser[i];
        if (userData.user.classRole == 'tea') {
          if (userData.aspect) {
            const aspect = JSON.parse( JSON.parse(userData.aspect) );
            console.log('aspect: ', aspect);
            if (aspect.startFlag) {

              if (this.g_teacherFlag) {
                this.g_cartoon.playAudio("sm-choice-in")
                this.startGame_Teacher();
                // this.sendServerEvent(this.m_Message_Start, {startFlag: true})
                // this.g_startBtn.hide(()=>{
                //   this.g_enableTouch = true;
                // })
                // setTimeout(() => {
                //   this.g_enableTouch = true;
                // }, 2000);

                this.g_startBtn.alpha = 0;
                this.g_enableTouch = true;
                this.fillTeacherPageData();
              } else {

                this.startGame_Student();
                this.g_enableTouch = true;
              }

            }

          }
          break;
        }
      }




    }
  }

  fillTeacherPageData() {
    const allUser = this.g_serverAllUser;
    if (allUser) {
      // console.log('allUser 2 : ', allUser);
      for (let i = 0; i < allUser.length; i++) {
        const userData = allUser[i];
        if (userData.user.classRole == 'stu') {

          if (userData.aspect) {
            const aspect =  JSON.parse( JSON.parse(userData.aspect) );

            this.g_cartoon.getCartoonElement("Teacher_RankList").handleGetData(aspect);

            // console.log('aspect: ', aspect);
            // this.addTeacherPageData(aspect);
          }
          break;
        }
      }
    }
  }





  startGame_Student(){
    this.g_cartoon.playAudio("sm-choice-in")
    this.g_cartoon.getCartoonElement("cover").hide()
    this.startAnimation(()=>{
      this.startAnswer();
    })
  }

  startGame_Teacher(){
    this.initTeacherPage()
    this.startAnimation(()=>{
      this.g_cartoon.getCartoonElement("Teacher_RankList").in()
    })
  }

  startAnimation(callback?){
    this.g_cartoon.getCartoonElement("title").out()
    this.g_cartoon.getCartoonElement("group").out()
    callback && setTimeout(()=>{
      callback()
    },600)
  }

  startAnswer(){
    this.g_cartoon.getCartoonElement("question-container").in()
    this.g_cartoon.getCartoonElement("question-container").setContent("image", "question-image")
    this.g_cartoon.getCartoonElement("drop-down").in()
    this.g_cartoon.getCartoonElement("timer").in()
    this.nextQuestion(false)
    this.g_cartoon.getCartoonElement("ready-go-container").in(()=>{
      this.goQuestion();
    })
    
  }

  endAnswer(callback?){
    callback && setTimeout(()=>{
      callback()
    },100)
  }

  showRankingList(){
    this.g_cartoon.getCartoonElement("ranking-list").setList({},0)
    this.g_cartoon.getCartoonElement("ranking-list").setList({},1)
    this.g_cartoon.getCartoonElement("ranking-list").setList({},2)
    this.g_cartoon.getCartoonElement("ranking-list").setList({},3)
    this.g_cartoon.getCartoonElement("ranking-list").setMeIcon(2)
    this.g_cartoon.playAudio("sm-choice-complete")
    this.g_cartoon.getCartoonElement("ranking-list").in()
    this.showEndPatal();
  }

  loadQuestion(){
    let max = this.g_formData.dataArray.length;
    
    if(this.m_currentQuestion == max){
      return {
        currentIndex:this.m_currentQuestion,
        totalIndex:max,
        theEnd:true
      }
    }
    let time = this.g_formData.dataArray[this.m_currentQuestion].time?this.g_formData.dataArray[this.m_currentQuestion].time:1
    let questionText = this.g_formData.dataArray[this.m_currentQuestion].question.text;
    let questionImage = this.g_formData.dataArray[this.m_currentQuestion].question.image_url;
    let questionAudio = this.g_formData.dataArray[this.m_currentQuestion].question.audio_url;
    let choiceCard = []
    choiceCard.push({
      answer: true,
      isText: this.g_formData.dataArray[this.m_currentQuestion].choice.correct.isText,
      text: this.g_formData.dataArray[this.m_currentQuestion].choice.correct.text,
      image_url: this.g_formData.dataArray[this.m_currentQuestion].choice.correct.image_url
      
    })

    this.g_formData.dataArray[this.m_currentQuestion].choice.incorrect.forEach(item => {
      choiceCard.push({
        answer: false,
        isEmpty: false,
        isText: item.isText,
        text: item.text,
        image_url: item.image_url
      })
    });
    choiceCard = this.randomArray_shuffle(choiceCard)
    // cc
    let mainContent = {}
    let dropDownContent ={}

    if( questionText !== "" && questionImage ==="" && questionAudio===""){
      // 文字
      mainContent = {
        type: "text",
        key: questionText
      };
    }else if(questionText === "" && questionImage !=="" && questionAudio===""){
      // 图片
      mainContent = {
        type: "image",
        key: questionImage
      };
    }else if(questionText === "" && questionImage ==="" && questionAudio!==""){
      // 音频
      mainContent = {
        type: "audio",
        key: questionAudio
      };
    }else if( questionText !== "" && questionImage !=="" && questionAudio===""){
      // 文字 图片
      mainContent = {
        type: "text",
        key: questionText
      };
      dropDownContent = {
        type: "image",
        key: questionImage
      };
    }else if(questionText !== "" && questionImage ==="" && questionAudio!==""){
      // 文字 音频
      mainContent = {
        type: "text",
        key: questionText
      };
      dropDownContent = {
        type: "audio",
        key: questionAudio
      };
    }else if(questionText === "" && questionImage !=="" && questionAudio!==""){
      // 图片 音频
      mainContent = {
        type: "audio",
        key: questionAudio
      };
      dropDownContent = {
        type: "image",
        key: questionImage
      };
    }else if(questionText !== "" && questionImage !=="" && questionAudio!==""){
      // 文字 图片 音频
      mainContent = {
        type: "text",
        key: questionText,
        additionalAudio: questionAudio
      };
      dropDownContent = {
        type: "image",
        key: questionImage
      };
    }

    this.m_currentQuestion++;
    return {
      currentIndex:this.m_currentQuestion,
      totalIndex:max,
      time: time,
      theEnd:false,
      mainContent: mainContent,
      dropDownContent: dropDownContent,
      questionText:questionText,
      questionImage: questionImage,
      questionAudio: questionAudio,
      choiceCards: choiceCard
    }
  }

  setQuestion(showData, startNow:boolean= true){
    if(showData.theEnd){
      return false;
    }

    if(showData.mainContent.type){
      this.g_cartoon.getCartoonElement("question-container").setContent(showData.mainContent.type, showData.mainContent.key)
    }
    if(showData.mainContent.additionalAudio){
      this.g_cartoon.getCartoonElement( "additional-speaker").setAduio(showData.mainContent.additionalAudio)
      this.g_cartoon.getCartoonElement( "additional-speaker").show()
    }else{
      this.g_cartoon.getCartoonElement( "additional-speaker").hidden()
    }

    if(showData.dropDownContent.type){
      this.g_cartoon.getCartoonElement("drop-down").setContent(showData.dropDownContent.type, showData.dropDownContent.key)
      if(showData.mainContent.additionalAudio){
        this.g_cartoon.getCartoonElement("drop-down").down_type2()
        this.g_cartoon.getCartoonElement("question-container").contentDown_type2()
      }else{
        this.g_cartoon.getCartoonElement("drop-down").down()
        this.g_cartoon.getCartoonElement("question-container").contentDown()
      }
      
    }else{
      this.g_cartoon.getCartoonElement("drop-down").up()
      this.g_cartoon.getCartoonElement("question-container").contentUp()
    }
    this.g_cartoon.getCartoonElement("drop-down").setTitleNumber(showData.currentIndex, showData.totalIndex)

    this.g_cartoon.getCartoonElement( "ChoiceCard1").out()
    this.g_cartoon.getCartoonElement( "ChoiceCard2").out()
    this.g_cartoon.getCartoonElement( "ChoiceCard3").out()
    this.g_cartoon.getCartoonElement( "ChoiceCard4").out()
    showData.choiceCards.forEach((item,index)=>{
      let i = index + 1
      this.g_cartoon.getCartoonElement( "ChoiceCard" + i ).in()
      this.g_cartoon.getCartoonElement( "ChoiceCard" + i ).setChoice(item)    
    })

    this.g_cartoon.getCartoonElement("timer").reset(showData.time);

    if(startNow){
      this.goQuestion();
    }
    
    this.g_enableTouch = true;
    return true
  }

  goQuestion(){
    setTimeout(()=>{
      this.g_enableTouch = true;
      this.autoPlayQuestionAudio()
      this.g_cartoon.getCartoonElement("timer").run()
    },100)
  }

  autoPlayQuestionAudio(){
    this.m_autoPlay && this.m_autoPlay()
    this.m_autoPlay = null;
  }
  setAutoPlayQuestionAudio(f:Function){
    this.m_autoPlay = f
  }

  nextQuestion(startNow:boolean = true){
    let showData = this.loadQuestion()
    let score_data = {
      user: this.g_currentUser,
      correctNumber: this.m_correctNumber,
      totalNumber: showData.totalIndex,
      isComplete: showData.theEnd,
      totalTime: this.m_totalTime
    }
    this.sendServerEvent(this.m_Message_NextQuestion, score_data)
    return this.setQuestion(showData, startNow)
  }

  handleClickChoice( targetId, isCorrectAnswer){
    this.g_cartoon.stopAllAudio()
    let waitingTime = 3000;
    let totalID = ["ChoiceCard1","ChoiceCard2","ChoiceCard3","ChoiceCard4"]
    this.g_cartoon.getCartoonElement("timer").stop()
    this.g_cartoon.stopAudio()
    if(isCorrectAnswer){ //选择正确
      this.m_correctNumber++;
      this.g_cartoon.playAudio("sm-choice-correct")
      this.showJellyAnimation(targetId)
      this.g_cartoon.getCartoonElement(targetId).correct()
      let index = totalID.indexOf(targetId);
      totalID.splice(index, 1)
      totalID.forEach((item)=>{
        this.g_cartoon.getCartoonElement(item).normal()
      })
    }else{ //选择错误
      this.g_cartoon.playAudio("sm-choice-incorrect")
      this.showShakeAnimation(targetId)
      this.g_cartoon.getCartoonElement(targetId).error()
      let index = totalID.indexOf(targetId);
      totalID.splice(index, 1)
      totalID.forEach((item)=>{
        this.g_cartoon.getCartoonElement(item).showAnswer()
      })
    }
    setTimeout(()=>{
      if(!this.nextQuestion()){
        this.endAnswer(()=>{
          this.g_cartoon.playAudio("sm-choice-complete")
          this.showEndPatal();
          // this.showRankingList()
        })
      }else{
        let totalID = ["ChoiceCard1","ChoiceCard2","ChoiceCard3","ChoiceCard4"]
        totalID.forEach((item)=>{
          this.g_cartoon.getCartoonElement(item).ready()
        })
      }
    }, waitingTime)
  }

  handleTimeup(){
    this.g_cartoon.stopAllAudio()
    let waitingTime = 3000;
    this.g_enableTouch = false;
    this.g_cartoon.playAudio("sm-choice-timeup-0")
    let totalID = ["ChoiceCard1","ChoiceCard2","ChoiceCard3","ChoiceCard4"]
    this.g_cartoon.getCartoonElement("timer").stop()
    totalID.forEach((item)=>{
      this.g_cartoon.getCartoonElement(item).showAnswer()
    })
    setTimeout(()=>{
      if(!this.nextQuestion()){
        this.endAnswer(()=>{
          this.g_cartoon.playAudio("sm-choice-complete")
          this.showEndPatal();
          // this.showRankingList()
        })
      }else{
        let totalID = ["ChoiceCard1","ChoiceCard2","ChoiceCard3","ChoiceCard4"]
        totalID.forEach((item)=>{
          this.g_cartoon.getCartoonElement(item).ready()
        })
        this.g_enableTouch = true;
      }
    }, waitingTime)
  }

  initTestEvent(){
    if(this.g_ForceChangeDefaultRole){
      if(this.g_teacherFlag){
        // let totalTime = 0;
        // let correct = 0;
        let randomUser = []
        for(let i=0; i<18; i++){
          randomUser.push({
            uuid: Math.floor(1000*Math.random()),
            nick_name: "Stu_" + i
          })
        }
       setTimeout(() => {
        setInterval(()=>{
          let user = randomUser[Math.floor(18*Math.random())]
          let score_data = {
            user: randomUser[Math.floor(18*Math.random())],
            correctNumber: Math.floor(100 * Math.random()),
            totalNumber: 100,
            isComplete: Math.floor(10 * Math.random())%2==1?true:false,
            totalTime: Math.floor(100 * Math.random())
          }
          this.sendServerEvent(this.m_Message_NextQuestion, score_data)
        },100)
       }, 3000);
      }else{
        setTimeout(()=>{
          this.sendServerEvent(this.m_Message_Start, {})
        },1000)
      }
    }
  }

  // ------------------------------------------------------------------------------







  // ------------------------------------------------------------------------------
  // 初始化 背景
  // ------------------------------------------------------------------------------

  initBackground(){
    let element = this.g_cartoon.createCartoonElement("background", "MySprite",1280,720)
    element.ref.init(this.g_cartoon.images.get("background"))
    element.ref.x = this.g_canvasWidth / 2;
    element.ref.y = this.g_canvasHeight /2;
    element.ref.scaleX = this.g_canvasWidth / 1280
    element.ref.scaleY = this.g_canvasHeight / 720
    this.render(element.ref)
  }

  // ------------------------------------------------------------------------------







  // ------------------------------------------------------------------------------
  // 初始化 开始页
  // ------------------------------------------------------------------------------
  initStartPage(){
    this.initTitle()
    this.initGroup();
    if(this.g_teacherFlag){
      this.initStartButton()
      // this.initTeacherListener()
    }else{
      this.initCover()
      // this.initStudentListener();
    }
  }

  initStudentListener(){
    this.addServerListener(this.m_Message_Start, (data)=>{
      this.startGame_Student()
      this.g_enableTouch = true;
    })
  }

  initTeacherListener(){
    this.addServerListener(this.m_Message_NextQuestion, (data)=>{
      this.g_cartoon.getCartoonElement("Teacher_RankList").handleGetData(data)
      this.g_enableTouch = true;
    })
  }

  initTitle() {
    let title = this.g_cartoon.createCartoonElement("title", "MySprite",494,82)
    title.ref.init(this.g_cartoon.images.get("title"))
    title.ref.x = this.g_cartoon.clientWidth/2
    title.ref.y = (59+title.ref.height/2)*this.g_mapScale;
    title.ref.setScaleXY(this.g_mapScale);
    title.out = ()=>{
      let moveDistance = this.g_cartoon.clientHeight - title.ref.height - 10
      tweenChange(title.ref,{ x:title.ref.x, y: title.ref.y-moveDistance }, 1 )
    }
    this.render(title.ref)
  }

  initGroup() {
    let group = this.g_cartoon.createCartoonElement("group", "MySprite",1158,226)
    group.ref.init(this.g_cartoon.images.get("group"))
    group.ref.x = this.g_cartoon.clientWidth/2;
    group.ref.y = this.g_cartoon.clientHeight/2;
    group.ref.setScaleXY(this.g_mapScale);
    group.out = ()=>{
      let moveDistance = this.g_cartoon.clientWidth + group.ref.width + 10
      tweenChange(group.ref,{ x:group.ref.x - moveDistance, y:group.ref.y }, 0.5 )
    }
    this.render(group.ref)
  }

  initStartButton() {
    let startBtn = this.g_cartoon.createCartoonElement("startBtn", "MySprite",340,140)
    startBtn.ref.init(this.g_cartoon.images.get("startBtn"))
    startBtn.ref.x = this.g_cartoon.getOrigin().x + (935 + startBtn.ref.width/2)*this.g_mapScale // this.g_cartoon.clientWidth/2;
    startBtn.ref.y = this.g_cartoon.getOrigin().y + (564 + startBtn.ref.height/2)*this.g_mapScale
    startBtn.ref.setScaleXY(this.g_mapScale);
    startBtn.hide = (callback)=>{
      tweenChange(startBtn.ref, { scaleX: this.g_mapScale * 1.5, scaleY: this.g_mapScale * 1.5 } , 0.2,()=>{
        tweenChange(startBtn.ref, { scaleX: 0, scaleY: 0 } , 0.2, ()=>{
          callback && callback()
        } )
      } )
    }
    this.g_startBtn = startBtn.ref;

    this.subscribeClickEvent("startBtn",()=>{
      this.g_cartoon.playAudio("sm-choice-in")
      this.startGame_Teacher();
      this.sendServerEvent(this.m_Message_Start, {startFlag: true})
      startBtn.hide(()=>{
        this.g_enableTouch = true;
      })
      setTimeout(() => {
        this.g_enableTouch = true;
      }, 2000);
    })

    this.render(startBtn.ref)
  }

  initCover() {
   
    let shapeBg = this.g_cartoon.createCartoonElement("cover", "ShapeRectNew")
    const lenW = 400 * this.g_mapScale;
    shapeBg.ref.setSize(lenW, lenW / 3 * 1.5, 30 * this.g_mapScale);
    shapeBg.ref.alpha = 0.5;
    shapeBg.ref.fillColor = '#000000';
    shapeBg.ref.x = this.g_canvasWidth/2 - shapeBg.ref.width / 2;
    shapeBg.ref.y = this.g_canvasHeight/2 - shapeBg.ref.height / 2;
    
    const label = new Label();
    label.fontName = 'BRLNSDB';
    label.fontColor = '#ffffff';
    label.text = "ready .."
    label.fontSize = 50 * this.g_mapScale;
    label.textAlign = 'center';
    label.x = shapeBg.ref.width / 2;
    label.y = shapeBg.ref.height / 2;
    shapeBg.ref.addChild(label);

    shapeBg.hide = ()=>{
      tweenChange(shapeBg.ref, { scaleX: 0, scaleY: 0 } , 0.1 )
    }
    // if (this.g_teacherFlag) {
    //   label.text = 'start';
    // } else {
    //   label.text = 'ready ..';
    // }

    this.render(shapeBg.ref);

  }

  
  // ------------------------------------------------------------------------------
  // End 初始化卡通元素
  // ------------------------------------------------------------------------------

  


  // ------------------------------------------------------------------------------
  // 初始化 排行榜
  // ------------------------------------------------------------------------------
  initTeacherPage() {

    const w = 600;
    const h = 500;
    const lineW = 4 * this.g_mapScale;
    let frame = this.g_cartoon.createCartoonElement("Teacher_RankList", "ShapeRectNew")
    frame.ref.setSize(w, h, 20 * this.g_mapScale );
    frame.ref.fill = false;
    frame.ref.setOutLine('#ffffff', lineW);
    frame.ref.setScaleXY(0);
    frame.ref.x = this.g_canvasWidth / 2 - w * this.g_mapScale / 2;
    frame.ref.y = this.g_canvasHeight / 2 - h * this.g_mapScale / 2// - 20 * this.g_mapScale;
    
    const pageShowNum = 5;
    const itemH = h - lineW;
    const baseY = lineW / 2;
    let listItemArray = []
    for (let i = 0; i < pageShowNum; i ++) {

      const listItem = new ShapeRect();
      listItem.setSize( w * 0.96, itemH / pageShowNum * 1);
      listItem.x = (w - listItem.width) / 2;
      listItem.y = baseY + i * itemH / pageShowNum;
      listItem.alpha = 0;
      frame.ref.addChild(listItem);

      const labelNum = new Label();
      labelNum.textAlign = 'center';
      labelNum.fontColor = '#ffffff';
      labelNum.fontName = 'BRLNSDB';
      labelNum.fontSize = 50;
      labelNum.text = i.toString();
      labelNum.x = 50;
      labelNum.y = listItem.height / 2;
      listItem.addChild(labelNum);

      const labelName = new Label();
      labelName.textAlign = 'center';
      labelName.fontColor = '#ffffff';
      labelName.fontSize = 40;
      labelName.text = '张三';
      labelName.x = 220;
      labelName.y = listItem.height / 2;
      listItem.addChild(labelName);

      const labelScore = new Label();
      labelScore.textAlign = 'center';
      labelScore.fontColor = '#ffffff';
      labelScore.fontSize = 35;
      labelScore.text = Math.floor( Math.random() * 100 ).toString();
      labelScore.x = 410;
      labelScore.y = listItem.height / 2;
      listItem.addChild(labelScore);

      const labelFinish = new MySprite();
      labelFinish.init(this.g_cartoon.images.get('t_right'));
      labelFinish.setScaleXY(0.07);
      labelFinish.x = listItem.width - 30;
      labelFinish.y = listItem.height / 2;
      listItem.addChild(labelFinish);


      if (i != pageShowNum - 1) {
        const line = new ShapeRect();
        line.setSize(listItem.width, lineW);
        line.fillColor = '#ffffff';
        line.y = listItem.height;
        listItem.addChild(line);
      }

      listItem['num'] = labelNum;
      listItem['name'] = labelName;
      listItem['score'] = labelScore;
      listItem['finish'] = labelFinish;

      labelNum.visible = false;
      labelName.visible = false;
      labelScore.visible = false;
      labelFinish.visible = false;
      listItemArray.push(listItem)
    }


    const pageLabel = this.g_cartoon.createCartoonElement("teacher-ranklist-page-nav", "Label")
    pageLabel.ref.textAlign = 'center';
    pageLabel.ref.fontColor = '#ffffff';
    pageLabel.ref.fontName = 'BRLNSDB';
    pageLabel.ref.fontSize = 40 // * this.g_mapScale;
    pageLabel.ref.text = '1 / 1';
    pageLabel.ref.x = this.g_canvasWidth / 2;
    pageLabel.ref.y = this.g_canvasHeight/2 + (frame.ref.height /2 + 50)*this.g_mapScale;
    pageLabel.ref.setScaleXY(0);
    pageLabel.in = ()=>{
      tweenChange(pageLabel.ref,{ scaleX: this.g_mapScale, scaleY: this.g_mapScale }, 0.1 )
    }
    pageLabel.out = ()=>{
      tweenChange(pageLabel.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }
    this.render(pageLabel.ref)

    let arrowL = this.g_cartoon.createCartoonElement("teacher-ranklist-left-btn", "MySprite")
    arrowL.ref.init(this.g_cartoon.images.get('t_arrow'));
    let arrowScale = this.g_mapScale * (50/arrowL.ref.width)
    arrowL.ref.setScaleXY(0);
    arrowL.ref.x = this.g_canvasWidth / 2 - 100*this.g_mapScale;
    arrowL.ref.y = pageLabel.ref.y
    arrowL.in = ()=>{
      tweenChange(arrowL.ref,{ scaleX: arrowScale, scaleY: arrowScale }, 0.1 )
    }
    arrowL.out = ()=>{
      tweenChange(arrowL.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }
    this.render(arrowL.ref);


    const arrowR = this.g_cartoon.createCartoonElement("teacher-ranklist-right-btn", "MySprite")
    arrowR.ref.init(this.g_cartoon.images.get('t_arrow'));
    arrowR.ref.setScaleXY(0);
    arrowR.ref.x = this.g_canvasWidth / 2 + 100*this.g_mapScale
    arrowR.ref.y = pageLabel.ref.y
    arrowR.ref.scaleX *= -1;
    arrowR.in = ()=>{
      tweenChange(arrowR.ref,{ scaleX: arrowScale*-1, scaleY: arrowScale*-1 }, 0.1 )
    }
    arrowR.out = ()=>{
      tweenChange(arrowR.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }
    this.render(arrowR.ref);


    
    let m_rankListData = []
    let allUserList = {}
    let currentPageIndex = 1;

    
    this.subscribeClickEvent("teacher-ranklist-left-btn",()=>{
      if(currentPageIndex != 1){
        currentPageIndex--;
      }
      frame.refreshList();
      this.g_enableTouch = true;
    })

    this.subscribeClickEvent("teacher-ranklist-right-btn",()=>{
      if(m_rankListData.length > currentPageIndex*5){
        currentPageIndex++;
        frame.refreshList();
      }
      this.g_enableTouch = true;
    })

    frame.setRankListData = (data)=>{
      m_rankListData = frame.sortData(data);
      frame.refreshList();
    }

    frame.handleGetData = (data)=>{
      allUserList[data.user.uuid] = data;
      let dataArray = []
      for(let key in allUserList){
        dataArray.push(allUserList[key])
      }
      frame.setRankListData(dataArray)
    }

    frame.sortData = (data)=>{
      data.sort((a,b)=>{
        if (a.correctNumber == b.correctNumber) {
          return a.totalTime - b.totalTime;
        } else {
          return b.correctNumber - a.correctNumber;
        }
      }) 
      return data
    }

    frame.refreshList = ()=>{
      let showDta = this.paginationArray(currentPageIndex,5,m_rankListData)
      listItemArray.forEach((listItem, index)=>{
        if(showDta[index]){
          listItem['num'].text = (currentPageIndex-1)*5 + index + 1 + "";
          listItem['name'].text = showDta[index].user.nick_name;
          listItem['score'].text = showDta[index].correctNumber + " - " + showDta[index].totalNumber;
          listItem['num'].visible = true;
          listItem['name'].visible = true;
          listItem['score'].visible = true;
          listItem['finish'].visible = showDta[index].isComplete;
        }else{
          listItem['num'].visible = false;
          listItem['name'].visible = false;
          listItem['score'].visible = false;
          listItem['finish'].visible = false;
        }
      })
      pageLabel.ref.text = currentPageIndex + ' / ' + Math.ceil(m_rankListData.length/5)
    }

    frame.in = ()=>{
      pageLabel.in()
      arrowL.in()
      arrowR.in()
      tweenChange(frame.ref,{ scaleX: this.g_mapScale, scaleY: this.g_mapScale }, 0.1 )
    }
    frame.out = ()=>{
      pageLabel.out()
      arrowL.out()
      arrowR.out()
      tweenChange(frame.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }






    this.render(frame.ref);

  }
  // ------------------------------------------------------------------------------
  // End 初始化 排行榜
  // ------------------------------------------------------------------------------



















  // ------------------------------------------------------------------------------
  // 初始化 答题页面
  // ------------------------------------------------------------------------------

  // ==> 题干部分
  initAnswer(){
    this.initQuestionBackground()
    this.initQuestionContainer()
    this.initDropDown();
    this.initDropDownBackground();
    this.initTimer()
    this.initAdditionalAudioContainer()
    // 选择卡 ChoiceCard1 x:613,y:246
    // 选择卡 ChoiceCard2 x:929,y:246
    // 选择卡 ChoiceCard3 x:613,y:479
    // 选择卡 ChoiceCard4 x:929,y:479
    this.initChoiceCard("ChoiceCard1", {x:613,y:246})
    this.initChoiceCard("ChoiceCard2", {x:929,y:246})
    this.initChoiceCard("ChoiceCard3", {x:613,y:479})
    this.initChoiceCard("ChoiceCard4", {x:929,y:479})
  }
  
  initQuestionBackground(){
    let element = this.g_cartoon.createCartoonElement("question-background", "MySprite",577,720)
    element.ref.init(this.g_cartoon.images.get("rectangle-3"))
    let scaleY = this.g_canvasHeight / element.ref.height
    let x = this.g_cartoon.getOrigin().x + (69 + (element.ref.width * 497/577)/2)*this.g_mapScale
    let y = this.g_cartoon.getOrigin().y + (0 + element.ref.height/2)*this.g_mapScale
    let readyX = x - 1000 - element.ref.width;
    let readyY = y
    element.ref.x = readyX;
    element.ref.y = readyY;
    element.ref.scaleX = this.g_mapScale
    element.ref.scaleY = scaleY
    element.in = ()=>{
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.out = ()=>{
      tweenChange(element.ref,{ x: readyX, y: readyY }, 0.1 )
    }
    this.render(element.ref)
  }

  initReadyGoBackground(){
    let element = this.g_cartoon.createCartoonElement("ready-go-background", "MySprite",577,720)
    element.ref.init(this.g_cartoon.images.get("bg_ready"))
    element.ref.x = this.g_canvasWidth/2
    element.ref.y = this.g_canvasHeight/2
    element.ref.scaleY = 222*this.g_mapScale / element.ref.height;
    element.ref.scaleX = this.g_canvasWidth / element.ref.width;
    element.ref.alpha = 0;
    element.in = ()=>{
      tweenChange(element.ref, { alpha:1 } , 0.2 )
    }
    element.out = ()=>{
      tweenChange(element.ref, { alpha:0 } , 0.2, ()=>{
        element.ref.setScaleXY(0)
      })
    }
    this.render(element.ref)
  }

  initReadyGoContainer(){
    let element = this.g_cartoon.createCartoonElement("ready-go-container", "MySprite")
    element.ref.init(this.g_cartoon.images.get("bg_1280_222"))
    element.ref.x = this.g_canvasWidth/2
    element.ref.y = this.g_canvasHeight/2
    element.ref.setScaleXY(0);

    let ready = new MySprite()
    ready.init(this.g_cartoon.images.get("ready"))
    ready.x = - this.g_canvasWidth/this.g_mapScale;
    ready.y = 0;
    let r_sy = 125 / ready.height
    let r_sx = 462 / ready.width
    ready.scaleY = r_sy * 0.7
    ready.scaleX = r_sx * 0.7
    element.ref.addChild(ready)

    let go = new MySprite()
    go.init(this.g_cartoon.images.get("go"))
    go.x = - this.g_canvasWidth/this.g_mapScale;
    go.y = 0;
    let g_sy = 130 / ready.height
    let g_sx = 500 / ready.width
    go.scaleY = g_sy * 0.7
    go.scaleX = g_sx * 0.7
    element.ref.addChild(go)

    element.readygo = (callback)=>{
      this.g_cartoon.playAudio("sm-ready")
      tweenChange(ready, { x: 100, y: 0 } , 0.4, ()=>{
        ready.scaleY = r_sy
        ready.scaleX = r_sx
        tweenChange(ready, { x: 0, y: 0 } , 0.1, ()=>{
          setTimeout(()=>{
            this.g_cartoon.playAudio("sm-go")
            tweenChange(ready, { x: this.g_canvasWidth/this.g_mapScale, y: 0 } , 0.3 ) 
            tweenChange(go, { x: 80, y: 0 } , 0.4, ()=>{
              go.scaleY = g_sy
              go.scaleX = g_sx
              setTimeout(()=>{
                tweenChange(go, { alpha:0 } , 0.1, ()=>{
                  element.out(()=>{
                    callback()
                  })
                })
              },300)
            })
          },300)
        })
      })
    }

    element.in = (callback)=>{
      this.g_cartoon.getCartoonElement("ready-go-background").in()
      tweenChange(element.ref, { scaleX: this.g_mapScale, scaleY: this.g_mapScale } , 0.1 )
      element.readygo(callback)
    }
    element.out = (callback)=>{
      this.g_cartoon.getCartoonElement("ready-go-background").out()
      tweenChange(element.ref, { scaleY: 0 } , 0.1, ()=>{
        callback && callback()
      })
    }
    this.render(element.ref)
  }

  initQuestionContainer() {
    let element = this.g_cartoon.createCartoonElement("question-container", "MySprite",577,720)
    element.ref.init(this.g_cartoon.images.get("rectangle-3"))
    let x = this.g_cartoon.getOrigin().x + (69 + (element.ref.width * 497/577)/2)*this.g_mapScale
    let y = this.g_cartoon.getOrigin().y + (0 + element.ref.height/2)*this.g_mapScale
    let readyX = x - 1000 - element.ref.width;
    let readyY = y
    let downDistance = 180
    let downDistance_type2 = 94
    element.ref.x = readyX;
    element.ref.y = readyY;
    element.ref.setScaleXY(this.g_mapScale);

    // 卡片内容
    let contentWidth = 453;
    let contentHeight = 251;
    let contentX = 0;
    let contentY = - 20*this.g_mapScale;
    let currentContent;
    let audio_url = "";
    

    element.contentUp = ()=>{
      tweenChange(currentContent,{ x: 0, y: 0 }, 0.1 )
    }
    element.contentDown_type2 = ()=>{
      tweenChange(currentContent,{ x: 0, y: downDistance_type2 }, 0.1 )
    }
    element.contentDown = ()=>{
      tweenChange(currentContent,{ x: 0, y: downDistance }, 0.1 )
    }
    element.setContent = (type?, key?)=>{
      if(!type){
        element.ref.removeChild(currentContent)
        return;
      }
      switch(type){
        case "text":
          element.ref.removeChild(currentContent)

          currentContent = new MySprite();
          currentContent.id = "image"
          currentContent.init(this.g_cartoon.images.get("bg-453-251"))
          currentContent.x = 0;
          currentContent.y =  - 20*this.g_mapScale + downDistance;
          currentContent.visible = false;
          currentContent.setScaleXY(Math.min(contentWidth / currentContent.width,  contentHeight / currentContent.height));

          let text = new Label();
          text.id = "question-text"
          text.text = "" + key;
          text.fontName = "RoundedBold";
          text.fontColor = "#000000";
          text.fontSize = 36
          text.textAlign = "center";
          text.maxSingalLineWidth = 450
          currentContent.addChild(text);

          element.ref.addChild(currentContent);

          break;
        case "image":
          element.ref.removeChild(currentContent)
          currentContent = new MySprite();
          currentContent.id = "image"
          currentContent.init(this.g_cartoon.images.get(key))
          currentContent.x = contentX;
          currentContent.y = contentY
          currentContent.setScaleXY(Math.min(contentWidth / currentContent.width,  contentHeight / currentContent.height));
          element.ref.addChild(currentContent);
          currentContent.visible = false;
          break;
        case "audio":
          element.ref.removeChild(currentContent)
          currentContent = this.g_cartoon.createCartoonElement("dropdown-speaker", "MySprite")
          currentContent.ref.id = "audio"
          currentContent.ref.init(this.g_cartoon.images.get("speaker-1"))
          let scale = Math.min( 120 / currentContent.ref.width,  126 / currentContent.ref.height)
          currentContent.ref.x = contentX;
          currentContent.ref.y = contentY
          audio_url = key;
          currentContent.ref.playContentAudio = ()=>{
            tweenChange(currentContent, { scaleX: scale*1.2, scaleY: scale*1.2 } , 0.2 )
            this.g_cartoon.playAudio(audio_url,false, ()=>{
              this.g_cartoon.getCartoonElement("dropdown-speaker").stop();
            })
          }
          currentContent.stop = ()=>{
            tweenChange(currentContent, { scaleX: scale, scaleY: scale } , 0.2 )
          }
          this.subscribeClickEvent("dropdown-speaker",()=>{
            if(currentContent.id == "audio"){
              currentContent.playContentAudio()
            }
            this.g_enableTouch = true;
          })
          this.setAutoPlayQuestionAudio(currentContent.ref.playContentAudio)
          currentContent.ref.setScaleXY(scale);
          element.ref.addChild(currentContent.ref);
          currentContent = currentContent.ref
          currentContent.visible = false;
          break;
      }
      setTimeout(()=>{
        currentContent.visible = true;
      },100)
    }

    element.in = ()=>{
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
      this.g_cartoon.getCartoonElement("question-background").in()
    }
    element.out = ()=>{
      tweenChange(element.ref,{ x: readyX, y: readyY }, 0.1 )
      this.g_cartoon.getCartoonElement("question-background").out()
    }
    this.render(element.ref)
  }

  initAdditionalAudioContainer(){
    let element = this.g_cartoon.createCartoonElement("additional-speaker", "MySprite")
    element.ref.id = "Additional_audio";
    element.ref.x = this.g_cartoon.getCartoonElementRef("question-container").x
    element.ref.init(this.g_cartoon.images.get("speaker-1"))
    let scale = Math.min( 120*this.g_mapScale / element.ref.width,  126*this.g_mapScale / element.ref.height)
    element.ref.x = this.g_cartoon.getOrigin().x + 320 * this.g_mapScale;
    element.ref.y = this.g_cartoon.getOrigin().y + this.g_cartoon.stageHeight - (126*scale)/2 - 30*this.g_mapScale;
    let audio_url ;
    element.playContentAudio = ()=>{
      if(audio_url){
        tweenChange(element.ref, { scaleX: scale*1.1, scaleY: scale*1.1 } , 0.2 )
        this.g_cartoon.playAudio(audio_url,false, ()=>{
          this.g_cartoon.getCartoonElement("additional-speaker").stop();
        })
      }
    }
    element.setAduio = (url)=>{
      audio_url = url;
      this.setAutoPlayQuestionAudio(element.playContentAudio)
    }
    element.stop = ()=>{
      tweenChange(element.ref, { scaleX: scale, scaleY: scale } , 0.2 )
    }
    element.show = ()=>{
      tweenChange(element.ref,{ scaleX: scale, scaleY: scale }, 0.1 )
    }
    element.hidden = ()=>{
      tweenChange(element.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }

    element.out = ()=>{
      tweenChange(element.ref,{ x: element.ref.x - 1000, y: element.ref.y }, 0.1 )
    }

    this.subscribeClickEvent("additional-speaker",()=>{
      if(element.ref.id == "Additional_audio"){
        element.playContentAudio()
      }
      this.g_enableTouch = true;
    })
    element.ref.setScaleXY(0);
    this.render(element.ref);
  }

  initDropDownBackground(){
    let element = this.g_cartoon.createCartoonElement("drop-down-bckground", "MySprite",521,422)
    element.ref.init(this.g_cartoon.images.get("rectangle-4"))
    let downDistance = this.g_cartoon.getOrigin().y
    let x = this.g_cartoon.getOrigin().x + (69 + (element.ref.width * 497/521)/2)*this.g_mapScale
    let y = -(element.ref.height/2)*this.g_mapScale
    let readyX = x - 1000 - element.ref.width;
    let readyY = y
    element.ref.x = readyX;
    element.ref.y = readyY;


    element.ref.scaleX = this.g_mapScale
    element.ref.scaleY = this.g_mapScale

    element.in = ()=>{
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.out = ()=>{
      tweenChange(element.ref,{ x: readyX, y: readyY }, 0.1 )
    }
    element.up = ()=>{
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.down = ()=>{
      tweenChange(element.ref,{ x: x, y: y + downDistance }, 0.1 )
    }

    this.render(element.ref)
  }

  initDropDown() {
    let element = this.g_cartoon.createCartoonElement("drop-down", "MySprite",521,422)
    element.ref.init(this.g_cartoon.images.get("drop-down"))
    let downDistance = this.g_cartoon.getOrigin().y + 371 * this.g_mapScale
    let downDistance_type2 = this.g_cartoon.getOrigin().y + 285 * this.g_mapScale
    let x = this.g_cartoon.getOrigin().x + (69 + (element.ref.width * 497/521)/2)*this.g_mapScale 
    let y = this.g_cartoon.getOrigin().y + (0 + element.ref.height/2)*this.g_mapScale - downDistance
    let readyX = x - 1000 - element.ref.width;
    let readyY = y
    element.ref.x = readyX;
    element.ref.y = readyY;
    element.ref.setScaleXY(this.g_mapScale);

    // 卡片内容位置
    let contentWidth = 453;
    let contentHeight = 251;
    let contentX = 0;
    let contentY = - 35*this.g_mapScale;
    let currentContent;
    let audio_url=""
    let number = new Label();
    number.text = "Question " + 1 + "/" + 1;
    number.fontName = "RoundedBold";
    number.fontColor = "#FFFFFF";
    number.fontSize = 22
    number.textAlign = "center";
    number.x = 0;
    number.y =  + 185;
    element.ref.addChild(number);


    element.in = ()=>{
      this.g_cartoon.getCartoonElement("drop-down-bckground").in()
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.out = ()=>{
      this.g_cartoon.getCartoonElement("drop-down-bckground").out()
      tweenChange(element.ref,{ x: readyX, y: readyY }, 0.1 )
    }
    element.up = ()=>{
      this.g_cartoon.getCartoonElement("drop-down-bckground").up()
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.down = ()=>{
      this.g_cartoon.getCartoonElement("drop-down-bckground").down()
      tweenChange(element.ref,{ x: x, y: y + downDistance }, 0.1 )
    }
    element.down_type2 = ()=>{
      currentContent.y = currentContent.y + 45
      this.g_cartoon.getCartoonElement("drop-down-bckground").down()
      tweenChange(element.ref,{ x: x, y: y + downDistance_type2 }, 0.1 )
    }
    element.setTitleNumber = (current, total)=>{
      number.text = "Question " + current + "/" + total;
    }
    element.setContent = (type, key)=>{
      switch(type){
        case "text":
          element.ref.removeChild(currentContent)

          currentContent = new MySprite();
          currentContent.id = "image"
          currentContent.init(this.g_cartoon.images.get("bg-453-251"))
          currentContent.x = 0;
          currentContent.y =  - 20*this.g_mapScale + downDistance;
          currentContent.visible = false;
          currentContent.setScaleXY(Math.min(contentWidth / currentContent.width,  contentHeight / currentContent.height));

          let text = new Label();
          text.id = "dropdown-text"
          text.text = "" + key;
          text.fontName = "RoundedBold";
          text.fontColor = "#fad1f3";
          text.fontSize = 48
          text.textAlign = "center";
          text.maxSingalLineWidth = 450
          
          currentContent.addChild(text);
          element.ref.addChild(currentContent);
          break;
        case "image":
          element.ref.removeChild(currentContent)
          currentContent = new MySprite();
          currentContent.id = "image"
          currentContent.init(this.g_cartoon.images.get(key))
          currentContent.x = contentX;
          currentContent.y = contentY
          currentContent.setScaleXY(Math.min(contentWidth / currentContent.width,  contentHeight / currentContent.height));
          element.ref.addChild(currentContent);
          currentContent.visible = false;
          break;
        case "audio":
          element.ref.removeChild(currentContent)
          currentContent = this.g_cartoon.createCartoonElement("dropdown-speaker", "MySprite")
          currentContent.ref.id = "audio"
          currentContent.ref.init(this.g_cartoon.images.get("speaker-1"))
          let scale = Math.min( 120 / currentContent.ref.width, 126 / currentContent.ref.height)
          currentContent.ref.x = contentX;
          currentContent.ref.y = contentY
          audio_url = key;
          currentContent.ref.playContentAudio = ()=>{
            tweenChange(currentContent, { scaleX: scale*1.2, scaleY: scale*1.2 } , 0.2 )
            this.g_cartoon.playAudio(audio_url,false, ()=>{
              this.g_cartoon.getCartoonElement("dropdown-speaker").stop();
            })
          }
          currentContent.stop = ()=>{
            tweenChange(currentContent, { scaleX: scale, scaleY: scale } , 0.2 )
          }
          this.subscribeClickEvent("dropdown-speaker",()=>{
            if(currentContent.id == "audio"){
              currentContent.playContentAudio()
            }
            this.g_enableTouch = true;
          })
          this.setAutoPlayQuestionAudio(currentContent.ref.playContentAudio)
          currentContent.ref.setScaleXY(scale);
          element.ref.addChild(currentContent.ref);
          currentContent = currentContent.ref;
          currentContent.visible = false;
          break;
      }
      setTimeout(()=>{
        currentContent.visible = true;
      },100)
    }
    this.render(element.ref)
  }
  // 题干部分 <== 结束


  // 定时器
  initTimer(){
    let element = this.g_cartoon.createCartoonElement("timer", "MySprite",105,105)
    element.ref.init(this.g_cartoon.images.get("combined-shape"))
    let x = this.g_cartoon.getOrigin().x + (863 + element.ref.width/2)*this.g_mapScale 
    let y = this.g_cartoon.getOrigin().y + (71 + element.ref.height/2)*this.g_mapScale
    let readyX = x
    let readyY = y - 500 - element.ref.height;
    element.ref.x = readyX;
    element.ref.y = readyY;
    element.ref.setScaleXY(this.g_mapScale);

    let isRun = false;
    const initTime = 10;
    

    // 时间显示
    let count = new Label();
    count.text = "-";
    count.fontName = "RoundedBold";
    count.fontColor = "#fad1f3";
    count.fontSize = 48
    count.textAlign = "center";
    count.x = 0;
    count.y = 0;
    element.ref.addChild(count);

    let circleStep = 0.1;
    let currentPercent = 0;
    let circle  = new ShapeCircle()
    circle.setRadius(47)
    circle.drawType = "stroke"
    circle.strokeColor = "#702dee";
    circle.strokeLineWidth = 13;
    circle.startRadian = this.convertPercentToRadian(0,true);
    circle.endRadian = this.convertPercentToRadian(0.5,true);
    circle.counterclockwise = true;
    element.ref.addChild(circle);

    // 计时变量
    let subTime = 0;
    let totalTime = 0;
    let remind3Sec = true;
    let timeRemaining = initTime;

    // 计时器到时回调
    let _callback;

    let setInter = (s)=>{
      let timeOut = (s)=>{
        setTimeout(()=>{
            if(isRun){
              timeRemaining--;
              subTime++;
              currentPercent += circleStep;
              circle.endRadian = this.convertPercentToRadian(currentPercent,true);
              if(timeRemaining < 0){
                isRun = false;
                element.ref.children[1].text = "0";
                count.fontColor = "#ff2846";
                circle.strokeColor = "#ff2846";
                circle.visible = false;
                element.ref.init(this.g_cartoon.images.get("red-circle"))
                _callback && _callback()
              }else{
                if(remind3Sec && timeRemaining/100<3 ){
                  this.g_cartoon.getCartoonElement("timer")._in3Sec()
                  remind3Sec = false;
                }
                element.ref.children[1].text = Math.floor(timeRemaining/100) + 1 + "";
                timeOut(s);
              }
            }
          },s)
      }
      timeOut(s);
    }

    element.in = ()=>{
      tweenChange(element.ref,{ x: x, y: y }, 0.1 )
    }
    element.out = ()=>{
      tweenChange(element.ref,{ x: readyX, y: readyY }, 0.1 )
    }

    element.run = ()=>{
      isRun = true;
      setInter(10)
    }

    element.stop = ()=>{
      isRun = false;
      totalTime += subTime;
    }

    element.reset = (initTime)=>{
      isRun = false;
      subTime = 0
      timeRemaining = initTime * 100; // initTime 的单位是秒 在定时器中的定时单位是10毫秒 所有要放大100倍
      remind3Sec = true; // 重置3秒倒计时提醒
      element.ref.children[1].text = "" + initTime;
      count.fontColor = "#fad1f3";
      element.ref.init(this.g_cartoon.images.get("combined-shape"))
      circleStep = 1/(initTime*100)
      circle.visible = true;
      currentPercent = 0;
      circle.strokeColor = "#702dee";
      circle.startRadian = this.convertPercentToRadian(0,true);
      circle.endRadian = this.convertPercentToRadian(0,true);
    }

    element._in3Sec = ()=>{
      this.g_cartoon.playAudio("sm-choice-timeup-3")
    }

    element.subscribeTimeup = (callback:Function)=>{
      _callback = callback
    }

    element.getTotalTime = ()=>{
      return Math.floor(totalTime/100);
    }
    this.render(element.ref)
  }


  // 选择卡 ChoiceCard1 x:613 y:246
  // 选择卡 ChoiceCard2 x:929 y:246
  // 选择卡 ChoiceCard3 x:613 y:479
  // 选择卡 ChoiceCard4 x:929 y:246
  initChoiceCard(choiceCardKey, position){
    let element = this.g_cartoon.createCartoonElement(choiceCardKey, "MySprite",305,223)
    element.ref.init(this.g_cartoon.images.get("choice-card-typeA"))
    let x = this.g_cartoon.getOrigin().x + (position.x + element.ref.width/2)*this.g_mapScale 
    let y = this.g_cartoon.getOrigin().y + (position.y + element.ref.height/2)*this.g_mapScale
    let readyX = x
    let readyY = y// - 500 - element.ref.height;
    element.ref.x = readyX;
    element.ref.y = readyY;
    element.ref.setScaleXY(0);

    // 文字
    let textContent = new Label();
    textContent.text = "Demo";
    textContent.fontName = "RoundedBold";
    textContent.fontColor = "#6341af";
    textContent.fontSize = 36
    textContent.textAlign = "center";
    textContent.x = 0;
    textContent.y = -50;
    textContent.visible = false;
    textContent.maxSingalLineWidth = 200 * this.g_mapScale
    element.ref.addChild(textContent);

    // 图片
    let imageContent = new MySprite();
    imageContent.init(this.g_cartoon.images.get("bg-240-180"))
    let sx = imageContent.width / 240;
    let sy = imageContent.height / 180;
    imageContent.x = 0;
    imageContent.y = -50;
    imageContent.visible = false;
    imageContent.setScaleXY(Math.min(sx,sy))
    imageContent.visible = false;
    element.ref.addChild(imageContent);

    // 右上角正确图标
    let correctIcon = new MySprite();
    correctIcon.init(this.g_cartoon.images.get("correct-icon"))
    correctIcon.x = 105;
    correctIcon.y = -85
    correctIcon.setScaleXY(Math.min( 40 / correctIcon.width,  40 / correctIcon.height));
    correctIcon.visible = false;
    element.ref.addChild(correctIcon);
    // 右上角错误图标
    let incorrectIcon = new MySprite();
    incorrectIcon.init(this.g_cartoon.images.get("incorrect-icon"))
    incorrectIcon.x = 105;
    incorrectIcon.y = -85
    incorrectIcon.setScaleXY(Math.min(35 / incorrectIcon.width,  35 / incorrectIcon.height));
    incorrectIcon.visible = false;
    element.ref.addChild(incorrectIcon);


    // 是否是正确选项
    let isCorrectAnswer = false;

    
    element.in = ()=>{
      tweenChange(element.ref,{ scaleX: this.g_mapScale, scaleY: this.g_mapScale }, 0.1 )
    }
    element.out = ()=>{
      tweenChange(element.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }
    element.correct = ()=>{
      correctIcon.visible = true;
      textContent.fontColor = "#FFFFFF"
      textContent.fontSize = 34
      element.ref.init(this.g_cartoon.images.get("choice-card-typeD"))
      this.showCorrectPatal(choiceCardKey,2000)
    }
    element.error = ()=>{
      textContent.fontColor = "#FFFFFF"
      incorrectIcon.visible = true;
      element.ref.init(this.g_cartoon.images.get("choice-card-typeB"))
    }
    element.normal = ()=>{
      textContent.fontColor = "#FFFFFF"
      element.ref.init(this.g_cartoon.images.get("choice-card-typeC"))
    }
    element.ready = ()=>{
      textContent.fontColor = "#6341af"
      textContent.fontSize = 36
      element.ref.init(this.g_cartoon.images.get("choice-card-typeA"))
      correctIcon.visible = false;
      incorrectIcon.visible = false;
    }
    element.setChoice = (setting)=>{
      if(setting.isText){
        textContent.text = setting.text;
        isCorrectAnswer = setting.answer
        textContent.visible = true;
        imageContent.visible = false;
      }else{
        isCorrectAnswer = setting.answer
        imageContent.init(this.g_cartoon.images.get(setting.image_url))
        let sx = 240 / imageContent.width;
        let sy = 180 / imageContent.height;
        imageContent.x = 0;
        imageContent.y = -5;
        textContent.visible = false;
        imageContent.visible = true;
        imageContent.setScaleXY(Math.min(sx,sy))
      }
    }
    element.showAnswer = ()=>{
      element.normal()
      if(isCorrectAnswer){
        correctIcon.visible = true;
      }
    }

    this.subscribeClickEvent(choiceCardKey, ()=>{
      this.handleClickChoice(choiceCardKey, isCorrectAnswer)
    })
    this.render(element.ref)
  }
  // ------------------------------------------------------------------------------





  // ------------------------------------------------------------------------------
  // 初始化 排行榜
  // ------------------------------------------------------------------------------

  initRankingList(){
    let element = this.g_cartoon.createCartoonElement("ranking-list", "MySprite", 756, 676)
    element.ref.init(this.g_cartoon.images.get("complete-card"))

    let x = this.g_cartoon.getOrigin().x + (929 + element.ref.width/2)*this.g_mapScale 
    let y = this.g_cartoon.getOrigin().y + (479 + element.ref.height/2)*this.g_mapScale
    element.ref.x = this.g_canvasWidth / 2;
    element.ref.y = this.g_canvasHeight / 2;
    element.ref.setScaleXY(0);

    let X1 = -164;
    let X2 = -90;
    let X3 = 120;
    let X4 = 235;

    let header = []
    let name = []
    let correctNumber = []
    let time = []
    let pos_Y = [-170,-35,108,236]
    for(let index=0; index<4; index++){
      let line_1_Header = new MySprite();
      line_1_Header.init(this.g_cartoon.images.get("header"))
      line_1_Header.x = X1;
      line_1_Header.y = pos_Y[index];
      line_1_Header.visible = false;
      line_1_Header.setScaleXY(Math.min( 60 / line_1_Header.width,  60 / line_1_Header.height));
      element.ref.addChild(line_1_Header);
      header.push(line_1_Header)
  
      let line_1_Name = new Label();
      line_1_Name.text = "Mildddddddddddddddes";
      line_1_Name.fontName = "BerlinSansFBDemi-Bold";
      line_1_Name.fontColor = "#acacac";
      line_1_Name.fontSize = 24
      line_1_Name.textAlign = "center";
      line_1_Name.x = X2;
      line_1_Name.y = pos_Y[index];
      line_1_Name.visible = false;
      element.ref.addChild(line_1_Name);
      name.push(line_1_Name)
  
      let line_1_CorrectNumber = new Label();
      line_1_CorrectNumber.text = "1dss2";
      line_1_CorrectNumber.fontName = "BerlinSansFBDemi-Bold";
      line_1_CorrectNumber.fontColor = "#3d3d3d";
      line_1_CorrectNumber.fontSize = 48
      line_1_CorrectNumber.textAlign = "center";
      line_1_CorrectNumber.x = X3;
      line_1_CorrectNumber.y = pos_Y[index];
      line_1_CorrectNumber.visible = false;
      element.ref.addChild(line_1_CorrectNumber);
      correctNumber.push(line_1_CorrectNumber)
  
      let line_1_Time = new Label();
      line_1_Time.text = "5dd3s";
      line_1_Time.fontName = "BerlinSansFBDemi-Bold";
      line_1_Time.fontColor = "#acacac";
      line_1_Time.fontSize = 24
      line_1_Time.textAlign = "center";
      line_1_Time.x = X4;
      line_1_Time.y = pos_Y[index];
      line_1_Time.visible = false;
      element.ref.addChild(line_1_Time);
      time.push(line_1_Time)
    }

    let meIcon = new MySprite();
    meIcon.init(this.g_cartoon.images.get("me"))
    meIcon.x = 0;
    meIcon.y = 0;
    meIcon.visible = false;
    meIcon.setScaleXY(Math.min( 24 / meIcon.width,  24 / meIcon.height));
    element.ref.addChild(meIcon);

    
    element.in = ()=>{
      tweenChange(element.ref,{ scaleX: this.g_mapScale, scaleY: this.g_mapScale }, 0.1 )
    }
    element.out = ()=>{
      tweenChange(element.ref,{ scaleX: 0, scaleY: 0 }, 0.1 )
    }
    element.setMeIcon = (index)=>{
      let x = header[index].x -22
      let y = header[index].y -22
      meIcon.x = x;
      meIcon.y = y;
      meIcon.visible = true;
    }
    element.setList = (item, index)=>{
      item = {
        header_url: this.g_cartoon.images.get("header"),
        name: "Miles",
        correctNumber: "" + this.m_correctNumber,
        time: "" + this.m_totalTime + "s"
      }
      header[index].init(this.g_cartoon.images.get(item.header_url))
      header[index].setScaleXY(Math.min( 60 / header[index].width,  60 / header[index].height));
      header[index].visible = true;
      name[index].text = item.name;
      name[index].visible = true;
      correctNumber[index].text = item.correctNumber;
      correctNumber[index].visible = true;
      time[index].text = item.time;
      time[index].visible = true;
    }
    element.hiddeAllItem = ()=>{
      header.forEach((item)=>{
        item.visible = false;
      })
      name.forEach((item)=>{
        item.visible = false;
      })
      correctNumber.forEach((item)=>{
        item.visible = false;
      })
      time.forEach((item)=>{
        item.visible = false;
      })
      meIcon.visible = false;
    }
    this.render(element.ref)
  }


  // ------------------------------------------------------------------------------































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

  @ViewChild("canvas") canvas: ElementRef;
  @ViewChild("wrap") 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, aspect) => {
      if (aspect) {
        console.log('aspect : ', aspect);
        this.g_serverAllUser = aspect.all_user;
      }
      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(() => {
        this.initSystem();
        this.update();
        this.systemReady();

         if(this.g_teacherFlag){
           this.initTeacherListener()
         }else{
           this.initStudentListener();
         }

         window["air"].hideAirClassLoading(this.g_KEY, this.g_data);

       });

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

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


  // ----------------------------------
  // 初始化音乐
  // ----------------------------------
  initAudio() {
    const contentObj = this.g_formData;
    if (!contentObj) {
      return;
    }
    const arr = contentObj.dataArray;
    // 添加用户上传音效
    if (arr) {
      for (let i = 0; i < arr.length; i++) {
        if(arr[i].question.audio_url){
          this.g_cartoon.addAudio(arr[i].question.audio_url, arr[i].question.audio_url);
        }
      }
    }
    // 添加本地音效
    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);
        }
      };
      const arr = this.g_data.contentObj.dataArray;
      if (arr) {
        for (let i = 0; i < arr.length; i++) {
          addPicUrl(arr[i].question.image_url);
          addPicUrl(arr[i].choice.correct.image_url);
          arr[i].choice.incorrect.forEach(item=>{
            addPicUrl(item.image_url);
          })
        }
      }
    }
    // 添加本地图片
    for( var key in localImages ){
     this.g_cartoon.addImage( key, localImages[key] );
    }
  }

  mapDown(event) {
    if (!this.g_enableTouch) {
      return;
    }
    for(let cartoonID in this.m_clickQueue){
      if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(cartoonID))) {
        this.g_enableTouch = false;
        this.m_clickQueue[cartoonID]();
        return;
      }
    }
  }


  mapMove(event) {}


  mapUp(event) {}


  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() {
    this.g_winResizeEventStream.pipe(debounceTime(500)).subscribe(data => {
      this.renderAfterResize();
    });

    if (this.IsPC()) {
      this.canvas.nativeElement.addEventListener("mousedown", event => {
        setMxMyByMouse(event);
        this.mapDown(event);
      });

      this.canvas.nativeElement.addEventListener("mousemove", event => {
        setMxMyByMouse(event);
        this.mapMove(event);
      });

      this.canvas.nativeElement.addEventListener("mouseup", event => {
        setMxMyByMouse(event);
        this.mapUp(event);
      });

      const setMxMyByMouse = event => {
        this.g_clickX = event.offsetX;
        this.g_clickY = event.offsetY;
      };
    } else {
      this.canvas.nativeElement.addEventListener("touchstart", event => {
        setMxMyByTouch(event);
        this.mapDown(event);
      });

      this.canvas.nativeElement.addEventListener("touchmove", event => {
        setMxMyByTouch(event);
        this.mapMove(event);
      });

      this.canvas.nativeElement.addEventListener("touchend", event => {
        setMxMyByTouch(event);
        this.mapUp(event);
      });

      this.canvas.nativeElement.addEventListener("touchcancel", event => {
        setMxMyByTouch(event);
        this.mapUp(event);
      });

      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;
      };
    }
  }


  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;
  }


  subscribeClickEvent(id,callback){
    this.m_clickQueue[id] = callback
  }


  // 全局初始化入口，当资源加载完毕后执行
  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.g_enableTouch = 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();
    if(this.g_EnableStageRuler){
      imageColor.fillColor = '#BE3C38';
    }
    this.m_renderArr.push(imageColor);

    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.m_renderArr.push(image);
    this.g_stage = image
  }

  render(ele){
    this.m_renderArr.push(ele);
  }

  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);
  }
















  












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


  // 泡泡
  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) {
    this.m_elementPetalArr = [];
    this.m_showElementPetalFlag = true;
    this.addCorrectPetal(card_id);
    setTimeout(()=>{
      this.m_elementPetalArr = [];
      this.m_showElementPetalFlag = false;
    },showTime)
  }

  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;
  }

}
