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

import {
  MySprite,
  Label,
  moveItem,
  rotateItem,
  removeItemFromArr,
  ShapeRect, randomSortByArr, hideItem, showItem, tweenChange,

} from './Unit';
import {res, resAudio} from './resources';

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

import * as _ from 'lodash';
import TWEEN from '@tweenjs/tween.js';




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

  @Input()
  set data(data) {
    this._data = data;
  }
  get data() {
    return this._data;
  }

  @Input()
  sid;

  @ViewChild('canvas') canvas: ElementRef;
  @ViewChild('wrap') wrap: ElementRef;
  canvasWidth = 1280;
  canvasHeight = 720;

  canvasBaseW = 1280;
  canvasBaseH = 720;

  ctx;
  fps = 0;
  frametime = 0;  // 上一帧动画的时间，   两帧时间差


  mx;
  my; // 点击坐标



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

  images = new Map();

  animationId: any;
  winResizeEventStream = new Subject();


  audioObj = {};
  renderArr;
  mapScale = 1;

  canvasLeft;
  canvasTop;


  canTouch = true;


  KEY = 'dfzx_15';

  // -----
  picArr;
  startBtn;
  mask;
  rabbitArr;
  rabbitIndex;
  holeExistArr;
  holePosArr;
  rabbitExistArr;
  replayBtn;
  soundClose;
  soundOpen;
  showSoundFlag;
  audio;
  showRabbitTimeId;


  pageLabel;
  leftBtn;
  rightBtn;
  bgLayer;
  curPageIndex;



  hotZoneArr;

  picIndex = 0;


  bgArr;
  endPageArr;
  gameEndFlag;
  showPetalFlag;
  bg;




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

  // }


  ngOnInit() {


    const getData = (<any> window).courseware.getData;
    getData((data) => {

      if (data && typeof data == 'object') {
        this.data = data;
      } else {
        this.data = {};
      }

      console.log('data:' , data);
      if (!this.data.contentObj) {
        this.data.contentObj = {};
      }

      this.initDefaultData();



      this.initAudio();
      this.initImg();
      this.initListener();

    }, this.KEY);


    //
    // // this.initAudio();
    // this.initImg();
    // this.initListener();
  }





  initDefaultData() {


    let picArr = this.data.contentObj.picArr;
    // console.log('picArr: ', picArr);
    if (!picArr || picArr.length == 0) {


      const picArr = [];
      for (let i = 0; i < 1; i++) {
        picArr.push({
          'optionType1': 'A',
          'optionType2': 'A',
          'a_text': 'one',
          'b_text': 'two',
          'a_audio_url': 'assets/play/default/audio.mp3',
          'b_audio_url': 'assets/play/default/audio.mp3',
        });
      }

      this.data.contentObj.picArr = picArr;

    }


  }

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

  initData() {

    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;


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

    // this.mapScale = this.canvasWidth / this.canvasBaseW;
    // this.mapScale = this.canvasHeight / this.canvasBaseH;
    this.renderArr = [];

    console.log(' in initData', this.data);






    this.canTouch = true;

    this.curPageIndex = 0;
    this.rabbitIndex = 0;

    this.holeExistArr = [];
    this.rabbitExistArr = [];

    // this.downFlag = false;

    this.endPageArr = [];

    // this.clockUpdateFlag = false;

    if (!this.data.contentObj.picArr) {
      this.data.contentObj.picArr = [];
    }
    this.picArr = this.data.contentObj.picArr;
    this.picArr = randomSortByArr(this.picArr);

    this.picIndex = 0;


    if (this.showRabbitTimeId) {
      clearTimeout(this.showRabbitTimeId);
    }
  }





  initAudio() {

    const contentObj = this.data.contentObj;
    if (!contentObj) { return; }


    const addUrlToAudioObj = (audioUrl) => {

      if (audioUrl) {

        // console.log('audioUrl:', audioUrl);
        const audio = new Audio();
        audio.src = audioUrl;
        audio.load();
        this.audioObj[audioUrl] = audio;
      }
    };


    let arr = contentObj.picArr;

    if (arr) {
      // console.log('arr: ', arr);
      for (let i = 0; i < arr.length; i++) {
        addUrlToAudioObj(arr[i].a_audio_url);
        addUrlToAudioObj(arr[i].b_audio_url);
      }
    }






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

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

      audioObj[key] = audio;

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


    addOneAudio('click', this.rawAudios.get('click'), 0.8);
    addOneAudio('wrong', this.rawAudios.get('wrong'), 0.3);
    // addOneAudio('angry', this.rawAudios.get('angry'), 1);
    // addOneAudio('cow_start', this.rawAudios.get('cow_start'), 1);
    // addOneAudio('eat', this.rawAudios.get('eat'), 1);
    // addOneAudio('grass', this.rawAudios.get('grass'), 1);
    // addOneAudio('happy', this.rawAudios.get('happy'), 1);
    // addOneAudio('right', this.rawAudios.get('right'), 1);
    // addOneAudio('wrong', this.rawAudios.get('wrong'), 1);



    //
    // const titleUrl = this.data.contentObj.title_audio_url;
    // if (titleUrl) {
    //
    //   this.titleAudio.src = titleUrl;
    //   this.titleAudio.load();
    // }



    // this.bgAudio.src = 'assets/bat-mail/music/bg.mp3';
    // this.bgAudio.load();
    // this.bgAudio.loop = true;
    // this.bgAudio.volume = 0.5;
    //
    // this.wrongAudio.src = 'assets/common/music/wrong.mp3';
    // this.wrongAudio.load();
    //
    // this.rightAudio.src = 'assets/common/music/right.mp3';
    // this.rightAudio.load();
    //
    // this.successAudio.src = 'assets/magic-hat/music/finish.mp3';
    // this.successAudio.load();
    //
    // this.successAudio.onended = () => {
    //   // this.showSuccessAudio();
    // };


  }

  initImg() {

    const contentObj = this.data.contentObj;
    if (contentObj) {


      const addPicUrl = (url) => {
        if (url) {
            this.rawImages.set(url, url);
          }
      };


      let arr = this.data.contentObj.picArr;
      if (arr) {

        for (let i = 0; i < arr.length; i++) {
          addPicUrl( arr[i].a_pic_url);
          addPicUrl( arr[i].b_pic_url);
        }
      }


    }


    // this.initFontImg();



    // 预加载资源

    this.loadResources().then(() => {
      // this.setfontData();

      window['air'].hideAirClassLoading(this.KEY, this.data);

      this.init();
      this.update();
    });

  }

  initFontImg() {



    // const fontbaseUrlW = 'assets/mechanical/letter/';
    // const fontDataW = {};
    //
    // let num = 97;
    // for (let i = 0; i < 26; i++) {
    //
    //   const key = String.fromCharCode(num + i);  // 'a'
    //   const url = fontbaseUrlW + key + '.png';
    //
    //   this.rawImages.set(url, url);
    //   fontDataW[key] = url;
    // }
    //
    //
    // this.fontDataW = fontDataW;
  }

  setfontData() {

    // for (let key in this.fontDataW) {
    //   this.fontDataW[key] = this.images.get(this.fontDataW[key]);
    // }
  }








  init() {

    this.initData();
    this.initCtx();

    this.initView();




  }

  initCtx() {

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

    window['curCtx'] = this.ctx;


  }


  initView() {

    this.initBg();

    this.initGrass();

    this.initRabbit();

    this.initSound();

    this.initMask();



    // this.initPic();
    // this.initBottomRight();
    //
    //
    // this.refreshPageLabel();


  }


  initSound() {

    if (!this.data.contentObj.picArr[0].a_audio_url) {
      return;
    }

    const soundClose = new MySprite();
    soundClose.setScaleXY(this.mapScale);
    soundClose.init(this.images.get('sound_close'));
    soundClose.x = this.canvasWidth - soundClose.width * this.mapScale;
    soundClose.y = this.canvasHeight - soundClose.height * this.mapScale * 0.8;
    soundClose['renderZ'] = 98;
    this.renderArr.push(soundClose);
    this.soundClose = soundClose;

    const soundOpen = new MySprite();
    soundOpen.setScaleXY(this.mapScale);
    soundOpen.init(this.images.get('sound_open'));
    soundOpen.x = soundClose.x;
    soundOpen.y = soundClose.y;
    soundOpen['renderZ'] = 99;
    this.renderArr.push(soundOpen);
    this.soundOpen = soundOpen;

    soundClose.visible = false;

  }


  initMask() {

    const mask = new ShapeRect();
    mask.setSize(this.canvasWidth, this.canvasHeight);
    mask.fillColor = '#000000';
    mask.alpha = 0.7;
    this.mask = mask;
    this.mask.renderZ = 100;

    this.renderArr.push(mask);

    const startBtn = new MySprite();
    startBtn.init(this.images.get('start_btn'));
    startBtn.setScaleXY(this.mapScale);
    startBtn.x = this.canvasWidth - startBtn.width / 2 * startBtn.scaleX;
    startBtn.y = this.canvasHeight - startBtn.height / 2 * startBtn.scaleY;
    this.startBtn = startBtn;
    this.renderArr.push(startBtn);

    const replayBtn = new MySprite();
    replayBtn.init(this.images.get('replay_btn'));
    replayBtn.setScaleXY(this.mapScale);
    replayBtn.x = startBtn.x;
    replayBtn.y = this.canvasHeight + replayBtn.height / 2 * replayBtn.scaleY;
    replayBtn['renderZ'] = 110;
    this.replayBtn = replayBtn;
    this.replayBtn.visible = false;
    this.renderArr.push(this.replayBtn);


  }


  initGrass() {

    const grass1 = new MySprite();
    grass1.init(this.images.get('grass_1'));
    grass1.setScaleXY(this.mapScale);
    grass1.x = grass1.width / 2 * grass1.scaleX;
    grass1.y = this.canvasHeight - grass1.height / 2 * grass1.scaleY;


    const grass2 = new MySprite();
    grass2.init(this.images.get('grass_2'));
    grass2.setScaleXY(this.mapScale);
    grass2.x = this.canvasWidth / 2;
    grass2.y = this.canvasHeight - grass2.height / 2 * grass2.scaleY;


    const grass3 = new MySprite();
    grass3.init(this.images.get('grass_3'));
    grass3.setScaleXY(this.mapScale);
    grass3.x = this.canvasWidth - grass3.width / 2 * grass3.scaleX;
    grass3.y = this.canvasHeight - grass3.height / 2 * grass3.scaleY;


    const grass4 = new MySprite();
    grass4.init(this.images.get('grass_4'));
    grass4.setScaleXY(this.mapScale);
    grass4.x = grass4.width / 2 * grass4.scaleX;
    grass4.y = this.canvasHeight - grass4.height / 2 * grass4.scaleY;


    const grass5 = new MySprite();
    grass5.init(this.images.get('grass_5'));
    grass5.setScaleXY(this.mapScale);
    grass5.x = this.canvasWidth / 2;
    grass5.y = this.canvasHeight - grass5.height / 2 * grass5.scaleY + 20 * this.mapScale;


    const grass6 = new MySprite();
    grass6.init(this.images.get('grass_6'));
    grass6.setScaleXY(this.mapScale);
    grass6.x = this.canvasWidth - grass6.width / 2 * grass6.scaleX;
    grass6.y = this.canvasHeight - grass6.height / 2 * grass6.scaleY;


    grass1['renderZ'] = 60;
    grass2['renderZ'] = 50;
    grass3['renderZ'] = 40;
    grass4['renderZ'] = 30;
    grass5['renderZ'] = 20;
    grass6['renderZ'] = 10;

    this.renderArr.push(grass6);
    this.renderArr.push(grass5);
    this.renderArr.push(grass4);
    this.renderArr.push(grass3);
    this.renderArr.push(grass2);
    this.renderArr.push(grass1);

  }


  initRabbit() {

    this.cleanOldRabbit();

    this.rabbitArr = [];
    this.holePosArr = [];

    const edge = 30 * this.mapScale;

    const rightNum = Math.ceil( Math.random() * 3 );
    const idArr = randomSortByArr( [1, 2, 3, 4] );
    idArr.length = rightNum;


    let rabbit = new MySprite();
    rabbit.init(this.images.get('rabbit'));
    rabbit.setScaleXY(this.mapScale);
    rabbit.x = rabbit.width / 2 * rabbit.scaleX + edge;
    rabbit.y = this.canvasHeight - 130 * this.mapScale;
    rabbit['renderZ'] = 59;
    rabbit['id'] = 0;
    this.renderArr.push(rabbit);
    this.addPic(rabbit, idArr.indexOf(1));
    this.holePosArr.push({x: rabbit.x, y: rabbit.y, z: 59});



    rabbit = new MySprite();
    rabbit.init(this.images.get('rabbit'));
    rabbit.setScaleXY(this.mapScale);
    rabbit.x = this.canvasWidth / 2 + 150 * this.mapScale;
    rabbit.y = this.canvasHeight - 120 * this.mapScale;
    rabbit['renderZ'] = 49;
    rabbit['id'] = 1;
    this.renderArr.push(rabbit);
    this.addPic(rabbit, idArr.indexOf(2));
    this.holePosArr.push({x: rabbit.x, y: rabbit.y, z: 49});



    rabbit = new MySprite();
    rabbit.init(this.images.get('rabbit'));
    rabbit.setScaleXY(this.mapScale);
    rabbit.x = this.canvasWidth - rabbit.width / 2 * rabbit.scaleX - edge;
    rabbit.y = this.canvasHeight - 160 * this.mapScale;
    rabbit['renderZ'] = 39;
    rabbit['id'] = 2;
    this.renderArr.push(rabbit);
    this.addPic(rabbit, idArr.indexOf(3));
    this.holePosArr.push({x: rabbit.x, y: rabbit.y, z: 39});



    rabbit = new MySprite();
    rabbit.init(this.images.get('rabbit'));
    rabbit.setScaleXY(this.mapScale);
    rabbit.x = this.canvasWidth / 2 - 160 * this.mapScale;
    rabbit.y = this.canvasHeight - 330 * this.mapScale;
    rabbit['renderZ'] = 9;
    rabbit['id'] = 3;
    this.renderArr.push(rabbit);
    this.addPic(rabbit, idArr.indexOf(4));
    this.holePosArr.push({x: rabbit.x, y: rabbit.y, z: 19});

    this.sortRenderZ(this.renderArr);

    // this.rabbitArr = randomSortByArr(this.rabbitArr);


  }

  cleanOldRabbit() {
    if (this.rabbitArr && this.rabbitArr.length > 0) {
      for (let i = 0; i < this.rabbitArr.length; i++) {
        clearTimeout(this.rabbitArr[i].timeId);
        if (this.rabbitArr[i].tween) {
          this.rabbitArr[i].tween.stop();
        }

      }
    }


    if (this.rabbitArr) {
      for (let i = 0; i < this.rabbitArr.length; i++) {
        removeItemFromArr(this.renderArr, this.rabbitArr[i]);
      }
    }

  }


  sortRenderZ(arr) {
    arr.sort((a, b) => {
      if (!a.renderZ) a.renderZ = 0;
      if (!b.renderZ) b.renderZ = 0;

      return a.renderZ - b.renderZ;
    });

  }


  addPic(rabbit, index) {

    const itemW = 280;
    const itemH = 220;
    const data = this.picArr[0];

    // console.log('data: ', data);

    let item, text, pic_url, rightFlag, optionType;
    if (index != -1) {
      text = data.a_text;
      pic_url = data.a_pic_url;
      optionType = data.optionType1;
      rightFlag = true;
    } else {
      text = data.b_text;
      pic_url = data.b_pic_url;
      optionType = data.optionType2;
      rightFlag = false;
    }

    if (optionType == 'A') {
      const label = new Label();
      label.text = text;
      label.fontSize = 60;
      label.fontName = 'BRLNSDB';
      label.textAlign = 'center';
      label.setMaxSize(280);
      label.y = -187;
      item = label;

    } else {
      const pic = new MySprite();
      pic.init(this.images.get(pic_url));

      // const pic = new ShapeRect();
      // pic.init();
      // pic.alpha = 0.3;
      // pic.setSize(itemW, itemH);
      pic.y = - 187;
      const sx = itemW / pic.width;
      const sy = itemH / pic.height;
      const s = Math.min(sx, sy);
      pic.setScaleXY(s);
      item = pic;

    }

    rabbit.addChild(item);
    rabbit.rightFlag = rightFlag;

    this.rabbitArr.push(rabbit);
    rabbit.targetY = rabbit.y;

    rabbit.y += rabbit.height * rabbit.scaleY;
    rabbit.baseY = rabbit.y;
    rabbit.h = rabbit.height * rabbit.scaleY;


  }














  initBg() {

    this.bgArr = [];

    const bg = new MySprite();
    bg.init(this.images.get('bg'));
    bg.x = this.canvasWidth / 2;
    bg.y = this.canvasHeight / 2;
    const sx = this.canvasWidth / bg.width;
    const sy = this.canvasHeight / bg.height;
    const s = Math.max(sx, sy);
    bg.setScaleXY(s);
    this.renderArr.push(bg);





    this.bgLayer = new MySprite();
    this.renderArr.push(this.bgLayer);

  }








  mapDown(event) {


    if (!this.canTouch) {
      return;
    }




    if (this.startBtn) {
      if (this.checkClickTarget(this.startBtn)) {
        this.clickStartBtn();
        return;
      }
      return;
    }




    if (this.soundClose) {
      if (this.checkClickTarget(this.soundClose)) {
        this.soundClick();
        return;
      }
    }



    for (let i = 0; i < this.rabbitArr.length; i++) {
      if (this.checkClickTarget(this.rabbitArr[i])) {
        this.clickRabbit(this.rabbitArr[i]);
        return;
      }
    }

    if (this.replayBtn.visible) {
      if (this.checkClickTarget(this.replayBtn)) {
        this.clickReplayBtn();
        return;
      }
    }



  }





  mapMove(event) {

  }


  mapUp(event) {


  }


  soundClick() {

    if (this.showSoundFlag) {
      return;
    }


    if (!this.audio) {

      this.audio = this.audioObj[this.picArr[0].a_audio_url];
      this.audio.onended = () => {

        this.closeSound();
      };
    }

    this.audio.play();

    this.showSound();
    // this.soundClose.visible = false;
    // this.soundOpen.visible = true;
  }




  showSound() {

    this.showSoundFlag = true;
    this.updateSound();
  }

  updateSound() {

    if (!this.showSoundFlag) {
      return;
    }

    let time = 300;

    if (this.soundOpen.visible) {
      this.soundOpen.visible = false;
      this.soundClose.visible = true;
    } else {
      this.soundOpen.visible = true;
      this.soundClose.visible = false;
    }


    setTimeout(() => {

      this.updateSound();

    }, time);
  }

  closeSound() {

    // console.log(' in closeSound');
    this.showSoundFlag = false;
    this.soundOpen.visible = true;
    this.soundClose.visible = false;
  }






  clickRabbit(rabbit) {

    if (!rabbit.canHit) {
      return;
    }

    if (rabbit.tween) {
      rabbit.tween.stop();
    }
    if (rabbit.timeId) {
      clearTimeout(rabbit.timeId);
      rabbit.timeId = null;
    }

    if (rabbit.rightFlag) {
      // removeItemFromArr(this.rabbitArr, rabbit);
      // removeItemFromArr(this.renderArr, rabbit);
      rabbit.endFlag = true;


      this.playAudio(this.picArr[0].b_audio_url, true);
    } else {
      this.playAudio('wrong', true);
    }


    rabbit.canHit = false;
    this.rabbitDown(rabbit, 0.35);


  }



  clickStartBtn() {

    hideItem(this.mask, 0.5);
    moveItem(this.startBtn, this.startBtn.x, this.canvasHeight + this.startBtn.height / 2 * this.startBtn.scaleY, 0.5, () => {
      this.showRabbit();
      this.startBtn = null;

      this.playAudio(this.picArr[0].a_audio_url);
    }, TWEEN.Easing.Cubic.Out);

    this.playAudio('click');
    // moveItem()
  }

  clickReplayBtn() {

    this.gameEndFlag = false;


    hideItem(this.mask, 0.5);
    moveItem(this.replayBtn, this.replayBtn.x, this.canvasHeight + this.replayBtn.height / 2 * this.replayBtn.scaleY, 0.5, () => {
      this.showRabbit();

      this.playAudio(this.picArr[0].a_audio_url);

    }, TWEEN.Easing.Cubic.Out);

    this.playAudio('click');

  }



  showRabbit() {

    if (this.gameEndFlag) {
      return;
    }

    if (this.holeExistArr.length >= 2 ) {
      return;
    }

    const holeArr = [0, 1, 2, 3];
    for (let i = 0; i < this.holeExistArr.length; i++) {
      removeItemFromArr(holeArr, this.holeExistArr[i]);
    }
    let tmp = Math.floor( holeArr.length * Math.random() );
    const holeIndex = holeArr[tmp];
    const pos = this.holePosArr[holeIndex];


    const rabbitIndexArr = [0, 1, 2, 3];
    for (let i = 0; i < this.rabbitExistArr.length; i++) {
      removeItemFromArr(rabbitIndexArr, this.rabbitExistArr[i]);
    }

    for (let i = 0; i < rabbitIndexArr.length; i++) {
      const rabbitIndex = rabbitIndexArr[i];
      if (rabbitIndex) {
        const rabbit = this.rabbitArr[rabbitIndex];
        if (rabbit.canHit) {
          rabbitIndexArr.splice(i, 1);
          i --;
        }
      }

    }

    if (rabbitIndexArr.length == 0) {
      return;
    }

    tmp = Math.floor( rabbitIndexArr.length * Math.random() );
    const rabbitIndex = rabbitIndexArr[tmp];
    const rabbit = this.rabbitArr[rabbitIndex];

    rabbit.x = pos.x;
    rabbit.y = pos.y;
    rabbit.holeId = holeIndex;
    rabbit.id = rabbitIndex;
    rabbit.baseY = pos.y;
    rabbit.targetY = pos.y - rabbit.h;
    rabbit.renderZ = pos.z;

    this.sortRenderZ(this.renderArr);

    this.rabbitUp(rabbit);



    this.holeExistArr.push(holeIndex);
    this.rabbitExistArr.push(rabbitIndex);



    this.showRabbitTimeId = setTimeout(() => {
      // console.log(' in timeout')
      this.showRabbit();
    }, 500 + 1500 * Math.random());

  }





  rabbitUp(rabbit) {

    rabbit.canHit = true;

    const tween = moveItem(rabbit, rabbit.x, rabbit.targetY, 0.5 + 0.2 * Math.random(), () => {


      rabbit.timeId = setTimeout(() => {

        rabbit.timeId = null;
        this.rabbitDown(rabbit);

      }, 300 + 1200 * Math.random());

    }, TWEEN.Easing.Cubic.Out);

    rabbit.tween = tween;


  }

  rabbitDown(rabbit, time = 0.8) {

    const tween = moveItem(rabbit, rabbit.x, rabbit.baseY, time, () => {

      // setTimeout(() => {
      //
      //   this.rabbitUp(rabbit);
      //
      // }, 2000);


      rabbit.canHit = false;

      if (!rabbit.endFlag) {
        removeItemFromArr( this.rabbitExistArr, rabbit.id);
      } else {
        this.checkGameEnd();
      }
      removeItemFromArr( this.holeExistArr, rabbit.holeId );



      if (this.holeExistArr.length <= 1) {
        this.showRabbit();
      }


    }, TWEEN.Easing.Cubic.Out);

    rabbit.tween = tween;


  }






  update() {


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

    TWEEN.update();



    this.updateArr(this.renderArr);

  }





  checkGameEnd() {

    for (let i = 0; i < this.rabbitArr.length; i++) {
      if (this.rabbitArr[i].rightFlag && !this.rabbitArr[i].endFlag) {
        return;
      }
    }

    this.gameEnd();
  }


  gameEnd() {

    for (let i = 0; i < this.rabbitArr.length; i++) {
      this.rabbitArr[i].childDepandAlpha = true;
      hideItem(this.rabbitArr[i], 0.3);
    }

    this.gameEndFlag = true;
    this.canTouch = false;

    this.replayBtn.visible = true;

    tweenChange(this.mask, {alpha: 0.7}, 0.5, () => {

      moveItem(this.replayBtn, this.replayBtn.x, this.canvasHeight - this.replayBtn.height / 2 * this.replayBtn.scaleY, 0.5, () => {
        this.canTouch = true;


        this.holeExistArr = [];
        this.rabbitExistArr = [];

        this.initRabbit();

      }, TWEEN.Easing.Cubic.Out);

    });

    (<any> window).courseware.sendEvent('gameEnd');
    console.log('send gameEnd event')

  }






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

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


  initHotZone() {


    let curBgRect;
    if (this.bg) {
      curBgRect = this.bg.getBoundingBox();
    }

    let oldBgRect = this.data.contentObj.bgItem.rect;
    if (!oldBgRect) {
      oldBgRect = curBgRect;
    }

    const rate = curBgRect.width / oldBgRect.width;


    // this.imgArr = [];
    // const arr = this.data.contentObj.imgItemArr;
    this.hotZoneArr = [];
    const arr = this.data.contentObj.hotZoneItemArr;
    if (!arr) return;

    for (let i = 0; i < arr.length; i++) {

      const data = JSON.parse(JSON.stringify(arr[i]));
      // const img = {pic_url: data.pic_url};

      data.rect.x *= rate;
      data.rect.y *= rate;
      data.rect.width *= rate;
      data.rect.height *= rate;

      data.rect.x += curBgRect.x;
      data.rect.y += curBgRect.y;

      const hotZone = this.getHotZoneItem(data);
      hotZone.alpha = 0;
      this.hotZoneArr.push(hotZone);

    }

  }


  getHotZoneItem(data) {
    const saveRect = data.rect;
    const item = new ShapeRect(this.ctx);
    item.setSize(saveRect.width, saveRect.height);
    item.x = saveRect.x ;
    item.y = saveRect.y ;
    item['data'] = data;

    this.renderArr.push(item);

    return item;
  }


  showEndPatal() {
    this.endPageArr = [];
    this.showPetalFlag = true;
    this.addPetal();
  }

  stopEndPatal() {
    this.endPageArr = [];
    this.showPetalFlag = false;
  }

  addPetal() {

    if (!this.showPetalFlag) {
      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();
    }, 200);

  }


  getPetal() {

    const petal = new MySprite(this.ctx);

    const id = Math.ceil( Math.random() * 3 );
    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;
  }





  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();
      }
      console.log('touch down');
      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();
      }
      // console.log('mouse down');
      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();
  }


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




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

  }


  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) => {
        
      };
      audio.onerror = () => {
        reject();
      };
      audio.src = url;
      audio.load();
      resolve();
    });
  }


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

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

}
