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

import {
  Label,
  MySprite, tweenChange,
  removeItemFromArr,
  moveItem,
  rotateItem,
  showPopParticle,
  shake,
  ShapeRect
} from './Unit';
import {res, resAudio} from './resources';

import {Subject} from 'rxjs';
import {debounceTime} from 'rxjs/operators';

import TWEEN from '@tweenjs/tween.js';




@Component({
  selector: 'app-play',
  templateUrl: './play.component.html',
  styleUrls: ['./play.component.css']
})
export class PlayComponent implements OnInit, OnDestroy {

  @ViewChild('canvas', {static: true }) canvas: ElementRef;
  @ViewChild('wrap', {static: true }) wrap: ElementRef;

  // 数据
  data;

  ctx;

  canvasWidth = 1280; // canvas实际宽度
  canvasHeight = 720; // canvas实际高度

  canvasBaseW = 1280; // canvas 资源预设宽度
  canvasBaseH = 720;  // canvas 资源预设高度

  mx; // 点击x坐标
  my; // 点击y坐标


  // 资源
  rawImages = new Map(res);
  rawAudios = new Map(resAudio);

  images = new Map();

  animationId: any;
  winResizeEventStream = new Subject();

  audioObj = {};

  renderArr;

  //子弹渲染
  bulletRenderArr;

  //结束页面渲染
  endPageArr

  //当前答案渲染
  curAnswerArr

  //重玩游戏渲染
  restartArr;

  mapScale = 1;

  canvasLeft;
  canvasTop;

  saveKey = 'DF-L220';

  bg_pic;

  bg;

  weapon_pic;

  weaponTouch = false;

  weapon_fire_pic

  //当前练习题答案:[{pic:spreat,correct:'',canHit:true|false,hited:true|false,show:true|false}]
  curanswers = []

  canTouch = true;

  started = false;

  canRestart = false;

  //练习结束
  practiceEnd = false;

  startPic;

  restartPic;

  playPic;

  playingPic;

  //正在播放
  playing;

  //当前课程
  curPractice;

  //当前练习题正确答案个数
  curRightAnswerNum = 0;

  nextPracticeIndex = 0;

  //气球间隔宽度
  interval_width = 60

  min_interval_width = 40

  max_interval_width = 80

  max_ballon = 5;

  //临时最大个数
  temp_max_ballon;

  //气球悬浮时间
  suspension_time = 3000;

  //气球上浮临时数组
  ballon_temp_arr;

  //气球显示x坐标数组[{mapx:numuber,free:false}]
  ballon_mapx_arr;


  @HostListener('window:resize', ['$event'])
  onResize(event) {
    this.winResizeEventStream.next();
  }


  ngOnInit() {

    this.data = {};

    // 获取数据
    const getData = (<any> window).courseware.getData;
    getData((data) => {

      if (data && typeof data == 'object') {
        this.data = data;
      }
      console.log('data:' , data);

      // 初始化 各事件监听
      this.initListener();

      // 若无数据 则为预览模式 需要填充一些默认数据用来显示
      this.initDefaultData();

      // 初始化 音频资源
      this.initAudio();
      // 初始化 图片资源
      this.initImg();
      // 开始预加载资源
      this.load();

    }, this.saveKey);

  }

  ngOnDestroy() {
    window['curCtx'] = null;
    window.cancelAnimationFrame(this.animationId);
  }


  load() {

    // 预加载资源
    this.loadResources().then(() => {
      window["air"].hideAirClassLoading(this.saveKey, this.data);
      this.init();
      this.update();
    });
  }


  init() {

    this.initCtx();
    this.initData();
    this.initView();
  }

  initCtx() {
    this.canvasWidth = this.wrap.nativeElement.clientWidth;
    this.canvasHeight = this.wrap.nativeElement.clientHeight;
    this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth;
    this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight;


    this.ctx = this.canvas.nativeElement.getContext('2d');
    this.canvas.nativeElement.width = this.canvasWidth;
    this.canvas.nativeElement.height = this.canvasHeight;

    window['curCtx'] = this.ctx;
  }






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


    // ---------------------------------------------
    const setParentOffset = () => {
      const rect = this.canvas.nativeElement.getBoundingClientRect();
      this.canvasLeft = rect.left;
      this.canvasTop = rect.top;
    };
    const setMxMyByTouch = (event) => {
      if (event.touches.length <= 0) {
        return;
      }
      if (this.canvasLeft == null) {
        setParentOffset();
      }
      this.mx = event.touches[0].pageX - this.canvasLeft;
      this.my = event.touches[0].pageY - this.canvasTop;
    };

    const setMxMyByMouse = (event) => {
      this.mx = event.offsetX;
      this.my = event.offsetY;
    };
    // ---------------------------------------------


    let firstTouch = true;

    const touchDownFunc = (e) => {
      if (firstTouch) {
        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 (firstTouch) {
        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 element = this.canvas.nativeElement;

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

    addMouseListener();
    addTouchListener();
  }

  /**
   * 播放练习题音频
   * @param key 
   * @param now 
   * @param callback 
   */
  playPracticeAudio(key, now = false, callback = null){
      this.playPic.alpha = 0;

      this.playingPic.alpha = 1;
      
      this.playAudio(key, now, callback);

      this.playing = true;

      this.showPlayStauts();
  }

  showPlayStauts(){
    if(this.playing){
      
      if(this.playPic.alpha == 0){
        this.playingPic.alpha = 0;
        this.playPic.alpha = 1
      }
      else{
        this.playingPic.alpha = 1;
        this.playPic.alpha = 0;
      }

      setTimeout(() => {
        this.showPlayStauts();
      }, 1000);
    }
    else{
      this.playingPic.alpha = 0;
      this.playPic.alpha = 1;
    }
  }

  playAudio(key, now = false, callback = null) {

    const audio = this.audioObj[key];
    if (audio) {
      if (now) {
        audio.pause();
        audio.currentTime = 0;
      }

      if (callback) {
        audio.onended = () => {
          callback();
        };
      }
      audio.play();
    }
  }

  /**
   * 暂停练习题音频
   * @param key 
   * @param callback 
   */
  pausePracticeAudio(key, callback = null){
    this.playPic.alpha = 1;

    this.playingPic.alpha = 0;

    this.pauseAudio(key, callback);

    this.playing =false;
  }


  pauseAudio(key, callback = null){

    const audio = this.audioObj[key];
    if (audio) {

      if (callback) {
        audio.onended = () => {
          callback();
        };
      }
      audio.pause();
    }
  }


  loadResources() {
    const pr = [];
    this.rawImages.forEach((value, key) => {// 预加载图片

      const p = this.preload(value)
        .then(img => {
          this.images.set(key, img);
        })
        .catch(err => console.log(err));

      pr.push(p);
    });

    this.rawAudios.forEach((value, key) => {// 预加载音频

      const a = this.preloadAudio(value)
        .then(() => {
          // this.images.set(key, img);
        })
        .catch(err => console.log(err));

      pr.push(a);
    });
    return Promise.all(pr);
  }

  preload(url) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      // img.crossOrigin = "anonymous";
      img.onload = () => resolve(img);
      img.onerror = reject;
      img.src = url;
    });
  }

  preloadAudio(url) {
    return new Promise((resolve, reject) => {
      const audio = new Audio();
      audio.oncanplay = (a) => {
        resolve();
      };
      audio.onerror = () => {
        reject();
      };
      audio.src = url;
      audio.load();
    });
  }


  renderAfterResize() {
    this.canvasWidth = this.wrap.nativeElement.clientWidth;
    this.canvasHeight = this.wrap.nativeElement.clientHeight;
    this.init();
  }





  checkClickTarget(target) {

    const rect = target.getBoundingBox();

    if (this.checkPointInRect(this.mx, this.my, 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;
  }





  addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null, callbackParam = null) {
    
    const audioObj = this.audioObj;

    if (url == null) {
      url = key;
    }

    this.rawAudios.set(key, url);

    const audio = new Audio();
    audio.src = url;
    audio.load();
    audio.loop = loop;
    audio.volume = vlomue;

    audioObj[key] = audio;

    audio.addEventListener('ended', function () {
      if (callback) {
        callback(callbackParam);
      }
    }, false);
  }

  addUrlToImages(key, url = null) {

    if(!key){
      return;
    }

    if(url == null){
      url = key;
    }
    
    this.rawImages.set(url, url);
    
  }






  // ======================================================编写区域==========================================================================


  shake(item, time = 0.5, callback = null, rate = 1, update = null) {

    if (item.shakeTween) {
      return;
    }
  
    item.shakeTween = true;
    const offX = 15 * item.scaleX * rate;
    const offY = 15 * item.scaleX * rate;
    const baseX = item.x;
    const baseY = item.y;
    const easing = TWEEN.Easing.Sinusoidal.InOut;
  
    const move4 = () => {
      this.moveItem(item, baseX, baseY, time / 4, () => {
        item.shakeTween = false;
        if (callback) {
          callback();
        }
      }, easing, update);
    };
  
    const move3 = () => {
      this.moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => {
        move4();
      }, easing, update);
    };
  
    const move2 = () => {
      this.moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => {
        move3();
      }, easing, update);
    };
  
    const move1 = () => {
      this.moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => {
        move2();
      }, easing,update);
    };
  
    move1();
  }

  moveItem(item, x, y, time = 0.8, callBack = null, easing = null, update = null) {

    const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000);
  
    if (callBack) {
      tween.onComplete(() => {
        callBack();
      });
    }
    if(update){
      tween.onUpdate( (a, b) => {
        update(a, b, tween);
      });
    }
    if (easing) {
      tween.easing(easing);
    }
  
    tween.start();
  
    return tween;
  }


  /**
   * 添加默认数据 便于无数据时的展示
   */
  initDefaultData() {

    if(!this.data.practices){
      this.data.practices = [];
    }
  }


  /**
   * 添加预加载图片
   */
  initImg() {

    for(let i = 0; i < this.data.practices.length; ++ i){

      let practice = this.data.practices[i];

      for(let j = 0; j < practice.answers.length; ++ j){
        let answer = practice.answers[j];

        this.addUrlToImages(answer.pic_url);
      }
    }

  }

  /**
   * 添加预加载音频
   */
  initAudio() {

    this.addUrlToAudioObj('start', this.rawAudios.get('start'));

    this.addUrlToAudioObj('fire', this.rawAudios.get('fire'));

    this.addUrlToAudioObj('right', this.rawAudios.get('right'));

    this.addUrlToAudioObj('celebrate', this.rawAudios.get('celebrate'));

    this.addUrlToAudioObj('wrong', this.rawAudios.get('wrong'));

    for(let i = 0; i < this.data.practices.length; ++ i){

      let practice = this.data.practices[i];

      this.addUrlToAudioObj('practice_' + i, practice.audio_url, 1, false, ()=>{
        this.playing = false;
         this.showPlayStauts();
      });
    }
    
  }



  /**
   * 初始化数据
   */
  initData() {

    const sx = this.canvasWidth / this.canvasBaseW;
    const sy = this.canvasHeight / this.canvasBaseH;
    const s = Math.min(sx, sy);
    this.mapScale = s;

    this.interval_width = Math.floor(this.interval_width * this.mapScale);

    //设置最小间隔
    if(this.interval_width < this.min_interval_width){
      this.interval_width = this.min_interval_width;
    }

    //最大值
    if(this.interval_width > this.max_interval_width){
      this.interval_width = this.max_interval_width;
    }

    this.renderArr = [];

    this.restartArr = [];

    this.initRestartData();
  }

  initRestartData(){
    this.bulletRenderArr = [];

    this.endPageArr = [];

    this.nextPracticeIndex = 0;

    this.curPractice = {};

    this.started = false;

    this.canRestart = false;

    //练习结束
    this.practiceEnd = false;

    this.playing = false;

    this.curPractice = {};

    this.temp_max_ballon = this.max_ballon;

    this.initPracticeRender();
  }


  /**
   * 初始化试图
   */
  initView() {


    this.initPic();

    this.initTopPart();

  }

  initTopPart() {
    const btnPlay = new MySprite();
    btnPlay.init(this.images.get('play'));
    btnPlay.x = this.canvasWidth - 100 * this.mapScale;
    btnPlay.y = 100 * this.mapScale;

    btnPlay.setScaleXY(this.mapScale);
    btnPlay.alpha = 0;

    this.renderArr.push(btnPlay);

    this.playPic = btnPlay;

    const playingPic = new MySprite();
    playingPic.init(this.images.get('playing'));
    playingPic.x = btnPlay.x;
    playingPic.y = btnPlay.y;
    playingPic.alpha = 0;
    playingPic.setScaleXY(this.mapScale);

    this.renderArr.push(playingPic);

    this.playingPic = playingPic;
  }

  initPic() {

    const maxW = this.canvasWidth;

    const bg_pic = new MySprite();
    bg_pic.init(this.images.get('bg'));
    bg_pic.x = this.canvasWidth / 2;
    bg_pic.y = this.canvasHeight / 2;
    bg_pic.setScaleXY(maxW/bg_pic.width);

    this.renderArr.push(bg_pic);
    this.bg_pic = bg_pic;

    const bg = new ShapeRect();
    bg.fillColor = '#75d100';
    bg.alpha = 0;
    bg.setSize(this.canvasWidth, this.canvasHeight);
    this.renderArr.push(bg); 
    this.bg = bg;

    const start_pic = new MySprite();
    start_pic.init(this.images.get('start'));
    start_pic.x = this.canvasWidth / 2;
    start_pic.y = this.canvasHeight - start_pic.height / 2;;
    start_pic.setScaleXY(this.mapScale);

    this.renderArr.push(start_pic);
    this.startPic = start_pic;

    const restart_pic = new MySprite();
    restart_pic.init(this.images.get('restart'));
    restart_pic.alpha = 0;
    restart_pic.x = this.canvasWidth / 2;
    restart_pic.y = this.canvasHeight + restart_pic.height;
    restart_pic.setScaleXY(this.mapScale);

    this.restartArr.push(restart_pic);
    this.restartPic = restart_pic;


    //初始化武器图片
    this.initWeaponPic();

  }

  /**
   * 初始化武器图片
   */
  initWeaponPic(){

    const weapon = new MySprite();
    weapon.init(this.images.get('weapon'));
    weapon.x = this.canvasWidth / 2;
    weapon.y = this.canvasHeight - weapon.height / 2;
    weapon.alpha = 0;
    weapon.setScaleXY(this.mapScale);
    this.weapon_pic = weapon;

    this.renderArr.push(weapon);

    const weapon_fire = new MySprite();
    weapon_fire.init(this.images.get('weapon_fire'));
    weapon_fire.alpha = 0;
    weapon_fire.setScaleXY(this.mapScale);

    this.weapon_fire_pic = weapon_fire;

    this.renderArr.push(weapon_fire);

   
  }

  /**
   * 初始化子弹图片
   */
  initBulletPic(){
    const bullet = new MySprite();
    bullet.init(this.images.get('bullet'));
    bullet.y = this.canvasHeight - this.weapon_pic.height - bullet.height/2;
    bullet.setScaleXY(this.mapScale);
    return bullet;
  }

  /**
   * 初始化练习题
   */
  initPracticePic(){

    if(!this.data.practices){
      return;
    }

    //结束整个练习则不再初始化
    if(this.nextPracticeIndex == this.data.practices.length){
      
      return;
    }

    this.canTouch = false;

    //初始化练习题渲染内容
    this.initPracticeRender();

    //获取需要渲染的练习题
    this.curPractice = this.data.practices[this.nextPracticeIndex];

    //循环初始化答案
    for(let i = 0; i< this.curPractice.answers.length; ++ i){

      let answer = this.curPractice.answers[i];
      
      let answerPic = this.getAnswerPic(answer);

      let suspendY = this.getSuspendY();

      this.curAnswerArr.push(answerPic);

      //计算正确答案个数
      if(answer.correct == 1){
        this.curRightAnswerNum += 1;
      }

      this.curanswers.push({correct:answer.correct,pic:answerPic, suspendY:suspendY, show:false, practiceIndex: this.nextPracticeIndex});

      
    }

    console.log('curanswers', this.curanswers);


    //设置当前气球显示索引，最大为max_ballon个
    if(this.curanswers.length < this.max_ballon){
      this.temp_max_ballon = this.curanswers.length;
    }
    else{
      this.temp_max_ballon = this.max_ballon;
    }

    let balloon_width = Math.floor(162 * this.mapScale) ;

    this.ballon_mapx_arr = this.getMapX(balloon_width, this.interval_width, this.temp_max_ballon);

    console.log('ballon_mapx_arr', this.ballon_mapx_arr);

    //播放音频
    setTimeout(() => {
      this.playPracticeAudio('practice_' + (this.nextPracticeIndex - 1));
    }, 100);

    //启动气球生产者
    this.ballonProducer(true);

    //改变下一道练习题索引：当前练习题增加1
    this.nextPracticeIndex = this.nextPracticeIndex + 1;

    //渲染完成
    this.canTouch = true;
  }

  /**
   * 获取显示坐标
   * @param spreat_width 
   * @param interval_width 
   * @param row 
   */
  getMapX(spreat_width, interval_width, row){

    let mapx_arr = [];

    let row_width =  row * spreat_width + ( row - 1) * interval_width;

    //起始X坐标
    let startX = (this.canvasWidth - row_width)/2 + spreat_width/2;

    //初始化气球x坐标
    for(let i = 0; i <  this.temp_max_ballon; ++ i){

      mapx_arr.push(startX);
      
      startX += spreat_width + interval_width;
  
    }

    return mapx_arr;
  }

  /**
   * 
   */
  ballonProducer(firset:Boolean, mapX = null){

      //练习结束
      if(this.practiceEnd){
        return;
      }

      //第一次填满
      if(firset){
        
        //取出气球显示
        for(let i = 0; i < this.temp_max_ballon; ++ i){

          let answer = this.getShowAnswer();

          if(answer){

            //取出坐标
            answer.pic.x = this.ballon_mapx_arr[i];

            this.ballon_temp_arr.push(answer);

            //显示气球
            this.ballonRose(answer);
          }
        }
      }
      else{
        let answer = this.getShowAnswer();

        if(answer){

          //设置坐标
          answer.pic.x = mapX;

          this.ballon_temp_arr.push(answer);

          //显示气球
          this.ballonRose(answer);
        }
      }
      
      
  }

  /**
   * 检查已显示的气球是否全部移除屏幕
   */
  checkAllOut(){

    let check = true;

    if(this.ballon_temp_arr){
      for(let i = 0; i < this.ballon_temp_arr.length; ++ i){

        let answer = this.ballon_temp_arr[i];

        if(answer.show){
          check = false;
          break;
        }
      }
    }

    return check;
  }

  getShowAnswer(){

    let answer;

    while(true){

      answer = this.getRandomItemFromArr(this.curanswers);

      if(!answer){
        break;
      }
  
      let index = this.ballon_temp_arr.indexOf(answer);
  
      if(index == -1){
        break;
      }
    }
    
    return answer;
  }

  getRandomItemFromArr(arr){

    if(!arr){
      return;
    }

    let length = arr.length;

    let index = Math.ceil(Math.random()*100000000)%length;

    return arr[index];
  }

  /**
   * 气球答案上升
   * @param answer 
   */
  ballonRose(answer){

    //设置显示高度
    answer.pic.y = this.canvasHeight + answer.pic.height * this.mapScale;

    answer.pic.alpha = 1;

    answer.pic.setScaleXY(0.3);

    answer.show = true;

    let out = this.canvasHeight - answer.suspendY;

    let speed = this.getRandomSpeed();

    let tween = tweenChange(answer.pic, {y: out}, speed, () => {
      if(!answer.hited){

        this.ballonSuspension(answer);
      }
    },null, (a,b)=>{
      
      this.shakeBallon(tween, answer);
    });

     //从小变到大
    tweenChange(answer.pic, {scaleX:this.mapScale, scaleY:this.mapScale}, 4, ()=>{
        answer.canHit = true;
    });

  }

  /**
   * 气球悬浮停止指定时间后，再次上浮
   * @param answer 
   */
  ballonSuspension(answer){

    answer.suspension = true;

    //悬浮时晃动气球
    this.shake(answer.pic, this.suspension_time/1000, ()=>{

      answer.suspension = false;

      let out =  - answer.pic.height * this.mapScale;

      let speed = this.getRandomSpeed();

      let tween = tweenChange(answer.pic, {y: out}, speed, ()=>{
       
        //设置未显示
        answer.show = false;

        answer.pic.alpha = 0;
        //console.log('can rose:', !this.practiceEnd && answer.practiceIndex  == (this.nextPracticeIndex -1));
        //练习未结束
        if(!this.practiceEnd && answer.practiceIndex  == (this.nextPracticeIndex -1)){

          if(!answer.hited){

           //console.log('play next ballon', answer.pic.x);
            //移除气球
            removeItemFromArr(this.ballon_temp_arr, answer);
  
            //产生下一个气球
            this.ballonProducer(false, answer.pic.x);
          }
        }

      },null,()=>{
        this.shakeBallon(tween, answer);
      });
    }, 1, (a, b, tween)=>{

      if(answer.canShake){
        answer.pic.shakeTween = false;

        tween.pause();
  
        shake(answer.pic, 1.3, ()=>{
  
          answer.pic.shakeTween = true;

          answer.canShake = false;
            
          tween.resume();
            
        }, .5);
      }
      
    });
  }

  /**
   * 晃动气球
   * @param tween 
   * @param answer 
   */
  shakeBallon(tween, answer){

    if(answer.canShake){

      answer.canShake = false;

      tween.pause();

      shake(answer.pic, 1.3, ()=>{
        tween.resume();
        
      }, .5);
    }
  }

  
  /**
   * 初始化练题渲染内容
   */
  initPracticeRender(){

    //播放状态
    this.playing = false;

    //清空子弹
    this.bulletRenderArr = [];

    //清除屏幕花瓣
    this.endPageArr = [];

    this.curAnswerArr = [];

    //清空当前答案
    this.curanswers = [];

    //清空练习题内容
    this.curPractice = {};

    this.curRightAnswerNum = 0;

    //清空气球x坐标
    this.ballon_mapx_arr = [];

    //清空气球缓存
    this.ballon_temp_arr = [];

    this.practiceEnd = false;

    this.temp_max_ballon = this.max_ballon;

    if(this.weapon_pic){
      this.weapon_pic.x = this.canvasWidth / 2;
      this.weapon_pic.y = this.canvasHeight - this.weapon_pic.height / 2 * this.mapScale;
    }
    
  }

  getAnswerPic(answer){

    const answerPic = new MySprite();

    const id = Math.ceil( Math.random() * 4 );

    //获取随机气球
    answerPic.init(this.images.get('balloon_' + id));

    answerPic.setScaleXY(0.3);
    answerPic.alpha = 0;
    answerPic.y = this.canvasHeight + answerPic.height * this.mapScale;
    //console.log('answer.pic_url', answer);
    //图片存在，则渲染图片
    if(answer.pic_url){
      
      const answer_pic = new MySprite();
      answer_pic.init(this.images.get(answer.pic_url));

      let w = answer_pic.width;

      let h = answer_pic.height;

      let r = Math.sqrt( w * w + h * h)/2;

      if(r > 60){
        answer_pic.setScaleXY(60 / r);
      }
      else{
        answer_pic.setScaleXY(r / 60);
      }

      answer_pic.x = 0;
      answer_pic.y = -35*this.mapScale; 
      answerPic.addChild(answer_pic);

      //console.log('answer.pic_url', answer_pic);
    }
    else{
      let answer_val = new Label();
      answer_val.text = answer.val;
      answer_val.textAlign='middle';
      answer_val.fontSize = 50;
      answer_val.fontName = "ARIALBD";
      answer_val.fontColor = "#FFFFFF";

      answer_val.refreshSize();
     
      let changed = this.setFontSize(answerPic.width, answer_val);

      answer_val.refreshSize();

      if(changed){
        answer_val.x = -Math.floor((answer_val.width + 20)* this.mapScale)/2;
      }
      else{
        answer_val.x = -Math.floor(answer_val.width* this.mapScale)/2;
      }

      console.log('answer_val', answer_val.x);

      answer_val.y = -35*this.mapScale;

      answerPic.addChild(answer_val);
    }
    

    answerPic.childDepandAlpha = true;

    return answerPic;
  }

  setFontSize(parWidth, answer: Label){

    if(!answer){
      return;
    }

    let changed = false;

    let w = answer.width;

    let h = answer.height;

    let r = Math.sqrt( w * w + h * h)/2;

    //如果宽度超过，则缩小字体
    if(r > (parWidth - 40)/2){
      
      let fontSize = answer.fontSize;

      fontSize = fontSize - 2;

      answer.fontSize = fontSize;

      answer.refreshSize();

      //console.log('answer_val', answer);

      this.setFontSize(parWidth, answer);

      changed = true;

    }

    return changed;
  }

  /**
   * 获取悬浮Y坐标
   */
  getSuspendY(){
    let y = Math.floor(Math.random() * 100 *this.mapScale) + this.canvasHeight * 0.65;
    return y;
  }

  /**
   * 获得随机速度
   */
  getRandomSpeed(){
    let speed = Math.floor(Math.random() * 4 + 3);
    return speed;
  }

  /**
   * 播放练习题结束画面
   * @param end 是否所有都结束
   */
  playEnd(end:boolean){

    this.canTouch = false;

    //灰色透明背景
    const cover = new ShapeRect();
    cover.setSize(this.canvasWidth, this.canvasHeight);
    cover.fillColor ="#696969";
    cover.alpha = 0.7;
    cover.x = 0;
    cover.y = 0;
    this.endPageArr.push(cover);

    this.addPetal();

    this.playAudio('celebrate');

    //如果未全部结束，则初始化下一个练习题
    if(!end){
      setTimeout(() => {

        tweenChange(cover, {alpha: 0}, 6, ()=>{

          this.initPracticePic();
        }); 
      }, 5000);
    }
  }

  /**
   * 添加结束花辨效果
   */
  addPetal() {

    if(!this.practiceEnd){
      return;
    }
    const petal = this.getPetal();
    this.endPageArr.push(petal);

    moveItem(petal, petal.x, this.canvasHeight + petal.height * petal.scaleY, petal['time'], () => {
      removeItemFromArr(this.endPageArr, petal);
    });

    rotateItem(petal, petal['tr'], petal['time']);

    setTimeout(() => {
      this.addPetal();
    }, 100);

  }


  getPetal() {

    const petal = new MySprite(this.ctx);

    const id = Math.ceil( Math.random() * 9 );
    petal.init(this.images.get('petal_' + id));

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

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

    const randomX = Math.random() * this.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;
  }

  mapDown(event) {

    if (!this.canTouch) {
      return;
    }

    //点击开始
    if(this.checkClickTarget(this.startPic)){
      this.startGame();
      return;
    }

    //点击开始
    if(this.canRestart){
      
      if(this.checkClickTarget(this.restartPic)){
        this.restartGame();
        return;
      }
    }
    
    //未开始直接返回
    if(!this.started){
      return;
    }

    if (this.checkClickTarget(this.weapon_pic)) {
      this.weaponTouch = true;
      return;
    }

    
    if (this.checkClickTarget(this.playPic) && !this.playing) {
      
      this.playPracticeAudio('practice_' + (this.nextPracticeIndex - 1));

      return;
    }

    if (this.checkClickTarget(this.playingPic) && this.playing) {
      
      this.pausePracticeAudio('practice_' + (this.nextPracticeIndex - 1));

      return;
    }
  }

  mapMove(event) {
    //光标在武器满围内
    if (this.checkClickTarget(this.weapon_pic)) {
      
      //武器被按下，则随坐标移动
      if(this.weaponTouch){
        //如果移动超过屏幕，则不移动
        if((this.mx + this.weapon_pic.width/2) > this.canvasWidth || this.mx < this.weapon_pic.width/2){
          return;
        }
        this.weapon_pic.x = this.mx;
      }
      return;
    }
  }

  mapUp(event) {

    if(!this.canTouch){
      return;
    }
    //武器按下弹起，则发射子弹
    if (this.checkClickTarget(this.weapon_pic)) {

      this.weaponTouch = false;
      
      //显示武器发射状态
      this.weapon_fire_pic.x = this.weapon_pic.x;
      this.weapon_fire_pic.y = this.weapon_pic.y;
      this.weapon_fire_pic.alpha = 1;
      this.weapon_pic.alpha = 0;

      setTimeout(() => {
        this.playAudio('fire');
      }, 10);

      //显示子弹
      let bullet_pic = this.initBulletPic();
      bullet_pic.x = this.weapon_pic.x;
      this.bulletRenderArr.push(bullet_pic);
      
      let bullet = {
        pic:bullet_pic,
        hit:false
      };

      let out = -bullet_pic.height;
      tweenChange(bullet.pic, {y: out}, 1, ()=>{

          removeItemFromArr(this.bulletRenderArr, bullet);

      },null,()=>{

        this.checkHit(bullet);

      });
      

      //显示武器准备状态
      this.weapon_fire_pic.alpha = 0;
      this.weapon_pic.alpha = 1;

      return;
    }
    else{

     
      //如果是武器被按下，且坐标未在武器坐标内，则清除被按下状态
      if(this.weaponTouch){
        this.weaponTouch = false;
      }
    }
  }

  /**
   * 检查是否可以射击
   */
  checkCanShoot(){

    if(!this.ballon_temp_arr){
      return;
    }

    let canShoot = true;

    for(let i = 0; i < this.ballon_temp_arr.length; ++ i){
      let answer = this.ballon_temp_arr[i];

      if(!answer.canHit){
        canShoot = false;
        break;
      }

    }

    return canShoot;
  }

  /**
   * 检查是否碰撞
   * @param item 
   */
  checkHit(item){

    if(!item){
      return;
    }

    //已经击中，不检查
    if(item.hit){
      return;
    }

    let x = item.pic.x;

    let y = item.pic.y;

    //坐标出去后不检查
    if(y < 0){
      return;
    }

  
    for(let i = 0; i < this.ballon_temp_arr.length; ++ i){
      let answer = this.ballon_temp_arr[i];
  
      const rect = answer.pic.getBoundingBox();
  
      if (this.checkPointInRect(x, y, rect)) {

        //气球飞出屏幕，不处理
        if(answer.pic.y < 0){
          break;
        }

        item.pic.scaleY = 0;
        item.pic.scaleX = 0;
            
        item.pic.alpha=0;
            
        item.hit = true;

        //正确答案
        if("1" == answer.correct){

          //设置答案被选中
          answer.hited = true;

          answer.pic.scaleX = 0;
          answer.pic.scaleY = 0;

          answer.pic.visible = false;
                
          
          this.showParticle(answer.pic);      

          this.playAudio('right');

          this.curRightAnswerNum = this.curRightAnswerNum - 1;

          //练习题结束，播放庆祝界面
          if(this.curRightAnswerNum < 1){

            this.practiceEnd = true;

            //重置音频
            const audio = this.audioObj['practice_' + (this.nextPracticeIndex - 1)];
            audio.currentTime = 0;

            this.pausePracticeAudio('practice_' + (this.nextPracticeIndex - 1));

            //如果当前索引与练习题相等，则为最后一道题，则播放结束画面
            if(this.nextPracticeIndex == this.data.practices.length){

              this.playEnd(true);

              //显示重新开始按钮
              setTimeout(() => {

                this.restartPic.alpha = 1;

                this.playPic.alpha = 0;

                this.weapon_pic.alpha=0;

                let out = this.canvasHeight - this.restartPic.height/2;

                tweenChange(this.restartPic, {y: out}, 1, ()=>{
      
                  this.canRestart = true;

                  this.canTouch = true;

                });
              }, 1000);

            }
            else{
                    
              this.playEnd(false);
            }
                
          }
          else{
            removeItemFromArr(this.curAnswerArr, answer.pic);

            console.log('answer hit index', this.curanswers.indexOf(answer));

            //将命中的气球从当前答案中移出
            removeItemFromArr(this.curanswers, answer);

            console.log('answer hit index', this.ballon_temp_arr.indexOf(answer));
            removeItemFromArr(this.ballon_temp_arr, answer);

            this.ballonProducer(false, answer.pic.x);
          }
        }
        else{
          this.playAudio('wrong');
          //console.log('answer.suspension', answer.suspension);
          
          answer.canShake = true;
          
        }
             
        break;
    }
  }
    
    
}

/**
 * 开始游戏
 */
startGame(){

  this.playAudio("start");

  setTimeout(() => {
    let out = this.canvasHeight + this.startPic.height;
    tweenChange(this.startPic, {y: out}, .3, ()=>{
      this.weapon_pic.alpha=1;
      this.started = true;
      this.playPic.alpha = 1;
      this.initPracticePic();

      //播放练习题音频
      setTimeout(() => {

        this.playPracticeAudio('practice_' + (this.nextPracticeIndex - 1));
      }, 50);
    });
  }, 200);
}

  /**
   * 重新开始游戏
   */
  restartGame(){
    let out = this.canvasHeight + this.restartPic.height;
    tweenChange(this.restartPic, {y: out}, .3, ()=>{
      
      this.weapon_pic.alpha=1;
      
      this.playPic.alpha = 1;

      this.initRestartData();

      this.started = true;

      this.initPracticePic();

      //播放练习题音频
      setTimeout(() => {
        this.playPracticeAudio('practice_' + (this.nextPracticeIndex - 1));
      }, 50);
    });
  }

  /**
   * 命中显示小星星
   * @param balloon 
   */
  showParticle(balloon){ 
    
    showPopParticle(this.images.get('star'), {x: balloon.x,y: balloon.y} ,this.bg);
  }


  update() {

    // ----------------------------------------------------------
    this.animationId = window.requestAnimationFrame(this.update.bind(this));
    // 清除画布内容
    this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
    // tween 更新动画
    TWEEN.update();
    // ----------------------------------------------------------



    this.updateArr(this.renderArr);

    this.updateArr(this.bulletRenderArr);

    this.updateArr(this.curAnswerArr);

    this.updateArr(this.endPageArr);

    this.updateArr(this.restartArr);

  }



}
