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

import {
  createSprite, createTween, delayCall, getAngleByPos, getMaxScale, getMinScale, getPosDistance,
  Label, moveItem, MyAnimation,
  MySprite, removeItemFromArr, rotateItem, scaleItem, ShapeRect, ShapeRectNew, tweenChange, tweenQueue,

} 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;
  mapScale = 1;

  canvasLeft;
  canvasTop;

  saveKey = 'pu07';


  canTouch = true;

  curPic;
  private bubblePos: any;
  private bgRight: MySprite;
  private letterBg: MySprite;
  private gridBg: any;
  private gridObj: {};
  private curStrip: ShapeRectNew;
  private startGrid: any;
  private endGrid: any;
  private bubbleArr: any[];
  private startBtn: MySprite;
  private curBubble: any;
  private curGridSprArr: any[];
  private stoneLayer: MySprite;
  private petalTime: number;
  private showPetalFlag: boolean;
  private endPageArr: any;
  private starTime: number;
  private popLayer: any;
  private bg: MySprite;
  private restartBtn: MySprite;

  @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);
      console.log('data:', JSON.stringify(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;
    window['curImages'] = this.images;
  }






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


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

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

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



  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) {

    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;

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

  addUrlToImages(url) {
    this.rawImages.set(url, url);
  }






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





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

    if (!this.data.grid) {
      const strData = '{"logicX":10,"logicY":10,"grid":[[{},{},{},{},{},{},{},{},{},{}],[{},{},{},{},{},{},{},{},{},{}],[{},{},{},{"text":"a"},{"text":"a"},{},{},{},{},{}],[{},{},{},{"text":"b"},{"text":"p"},{},{},{},{},{}],[{},{},{},{"text":"c"},{"text":"p"},{"text":"d"},{},{},{},{}],[{},{},{},{},{"text":"l"},{"text":"e"},{},{},{},{}],[{},{},{},{},{"text":"e"},{"text":"f"},{},{},{},{}],[{},{},{},{},{},{},{},{},{},{}],[{},{},{},{},{},{},{},{},{},{}],[{},{},{},{},{},{},{},{},{},{}]],"picArr":[{"text":"apple","audio_url":"assets/play/default/apple.mp3","pic_url":"assets/play/default/apple.png"}]}'
      this.data = JSON.parse(strData);
    }
  }


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

    for (let i = 0; i < this.data.picArr.length; i++) {
      this.addUrlToImages(this.data.picArr[i].pic_url);
    }
    // this.addUrlToImages(this.data.pic_url);
    // this.addUrlToImages(this.data.pic_url_2);

  }

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

    // 音频资源
    this.addUrlToAudioObj(this.data.audio_url);

    for (let i = 0; i < this.data.picArr.length; i++) {
      this.addUrlToAudioObj(this.data.picArr[i].audio_url);
    }

    // 音效
    this.addUrlToAudioObj('apple', this.rawAudios.get('apple'), 0.3);
    this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
    this.addUrlToAudioObj('btn', this.rawAudios.get('btn'), 0.3);
    this.addUrlToAudioObj('finish', this.rawAudios.get('finish'), 0.3);
    this.addUrlToAudioObj('right', this.rawAudios.get('right'), 0.3);
    this.addUrlToAudioObj('star', this.rawAudios.get('star'), 0.3);
    this.addUrlToAudioObj('stone', this.rawAudios.get('stone'), 0.1);
    this.addUrlToAudioObj('wrong', this.rawAudios.get('wrong'), 0.3);

  }



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

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

    // this.mapScale = sx;
    // this.mapScale = sy;


    this.renderArr = [];



  }



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

    this.initBg();

    this.initBubble();

    this.initGrid();

    this.initPopAnima();
  }





  mapDown(event) {

    if (!this.canTouch) {
      return;
    }

    if (this.letterBg.visible) {
      if (this.startBtn.visible && this.checkClickTarget(this.startBtn)) {
        this.clickedStartBtn();
        return;
      }
      return;
    }

    if (this.restartBtn) {
      if (this.checkClickTarget(this.restartBtn)) {
        this.clickedRestartBtn();
      }
      return;
    }


    for (let i = 0; i < this.bubbleArr.length; i++) {
      if (!this.bubbleArr[i].isCheckEnd && this.checkClickTarget(this.bubbleArr[i])) {
        this.clickedBubble(this.bubbleArr[i]);
        return;
      }
    }

    if (!this.curBubble) {
      return;
    }

    for (let x in this.gridObj) {
      for (let y in this.gridObj[x]) {
        if (this.checkClickTarget(this.gridObj[x][y])) {
          this.clickedGrid(this.gridObj[x][y]);
          return;
        }
      }
    }
  }

  mapMove(event) {
    if (!this.curStrip) {
      return;
    }

    for (let x in this.gridObj) {
      for (let y in this.gridObj[x]) {
        if (this.checkClickTarget(this.gridObj[x][y])) {
          if (this.gridObj[x][y] != this.startGrid) {
            this.refreshCurStrip(this.gridObj[x][y]);
            return;
          }
        }
      }
    }

  }

  mapUp(event) {
    if (this.curStrip) {
      this.checkIsWord();
      this.cleanCurStrip();
    }
  }



  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.endPageArr);

  }


  private initBg() {
    const bg = createSprite('bg');
    bg.setScaleXY(getMinScale(bg, this.canvasWidth, this.canvasHeight));
    bg.scaleX = this.canvasWidth / bg.width;
    bg.scaleY = this.canvasHeight / bg.height;
    bg.x = this.canvasWidth / 2;
    bg.y = this.canvasHeight / 2;
    this.renderArr.push(bg);
    this.bg = bg;

    this.popLayer = createSprite('');
    this.renderArr.push(this.popLayer);

    const bgRight = createSprite('bg_right');
    bgRight.setScaleXY(this.mapScale);
    bgRight.x = this.canvasWidth - bgRight.width / 2 * bgRight.scaleX + 30 * this.mapScale;
    bgRight.y = bgRight.height / 2 * bgRight.scaleY + 30 * this.mapScale;
    this.bgRight = bgRight;
    this.renderArr.push(bgRight);

    const letterBg = createSprite('letter_bg');
    letterBg.x = 11.8 ;
    letterBg.y = -0.8 ;
    letterBg.setScaleXY(1.02)
    bgRight.addChild(letterBg, -1);
    this.letterBg = letterBg;
    letterBg['baseX'] = letterBg.x;
    letterBg['baseY'] = letterBg.y;


    const maskSpr = createSprite('letter_bg');
    // maskSpr.y = 200;
    letterBg.setMaskSpr(maskSpr);
    letterBg['maskSpr'] = maskSpr;

    const startBtn = createSprite('start_btn');
    letterBg.addChild(startBtn);
    startBtn.setMaskSpr(maskSpr);
    this.startBtn = startBtn;


    const bgWave = createSprite('bg_wave');
    bgWave.scaleX = bg.scaleX;
    bgWave.scaleY = bg.scaleY;
    bgWave.x = this.canvasWidth / 2;
    bgWave.y = bg.y - 280 * bg.scaleY;
    this.renderArr.push(bgWave);

    const stoneLayer = createSprite('');
    this.renderArr.push(stoneLayer);
    this.stoneLayer = stoneLayer;
  }

  private initBubble() {

    this.initBublePos();
    this.initBubblePic();

  }

  private initBublePos() {

    const centerX = (this.bgRight.x - this.bgRight.width / 2 * this.bgRight.scaleX) / 2
    const centerY = this.bgRight.y;
    let sx = this.canvasWidth / this.canvasBaseW;
    if (sx < this.mapScale) {
      sx = this.mapScale;
    }

    this.bubblePos = [
      {x: centerX + 60 * sx, y: centerY + 78 * this.mapScale},
      {x: centerX + 280 * sx, y: centerY + 70 * this.mapScale},
      {x: centerX - 182 * sx, y: centerY + 78 * this.mapScale},
      {x: centerX - 50 * sx, y: centerY + 178 * this.mapScale},
      {x: centerX + 175 * sx, y: centerY + 178 * this.mapScale},
      {x: centerX + 65 * sx, y: centerY + 278 * this.mapScale},
      {x: centerX - 165 * sx, y: centerY + 278 * this.mapScale},
      {x: centerX + 285 * sx, y: centerY + 278 * this.mapScale},
      {x: centerX - 40 * sx, y: centerY - 40 * this.mapScale},
      {x: centerX + 170 * sx, y: centerY - 40 * this.mapScale},
      {x: centerX - 162 * sx, y: centerY - 150 * this.mapScale},
      {x: centerX + 60 * sx, y: centerY - 150 * this.mapScale},
      {x: centerX - 40 * sx, y: centerY - 260 * this.mapScale},
      {x: centerX + 190 * sx, y: centerY - 270 * this.mapScale}
    ];
  }

  private initBubblePic() {

    this.bubbleArr = [];
    const picArr = this.data.picArr;
    for (let i = 0; i < picArr.length; i++) {
      const bubble = this.createBubble(picArr, i);
      this.renderArr.push(bubble);
      this.bubbleArr.push(bubble);
    }
  }


  private createBubble(picArr, i: number) {
    const bubble = createSprite('bubble_frame');
    bubble.setScaleXY(this.mapScale);

    const bubbleBg = createSprite('bubble_bg');
    bubbleBg.alpha = 0;
    bubble.addChild(bubbleBg, -2);

    const stone = createSprite('stone');
    stone.setScaleXY(this.mapScale);

    const data = picArr[i];
    const pic = createSprite(data.pic_url);
    pic.setScaleXY(getMinScale(pic, stone.width - 54  , stone.height - 46))
    pic.x = -2;
    pic.y = -13;
    pic.isCircleStyle = true;
    bubble.addChild(pic, -1);

    bubble['bg'] = bubbleBg;
    bubble['stone'] = stone;
    bubble['pic'] = pic;
    bubble['data'] = data;

    const pos = this.bubblePos[i];
    bubble.x = pos.x;
    bubble.y = pos.y;

    stone.x = bubble.x;
    stone.y = bubble.y;
    this.stoneLayer.addChild(stone);
    stone.y -= 8;
    stone.alpha = 0;
    // bubble.addChild(stone, -3);

    return bubble;
  }

  private initGrid() {

    this.createGridBg();
    this.createGrid();
  }

  private createGridBg() {

    const gridBg = createSprite('grid_bg');
    this.bgRight.addChild(gridBg, -2);
    gridBg.x = this.letterBg.x;
    gridBg.y = this.letterBg.y;

    const stripLayer = createSprite('');
    gridBg.addChild(stripLayer);
    gridBg['stripLayer'] = stripLayer;

    this.gridBg = gridBg;
  }

  private createGrid() {

    this.gridObj = {};

    const len = 48;
    const dis = 6;
    const baseX = -(len * 10 + dis * 9) / 2 + len / 2;
    let baseY = -(len * 10 + dis * 9) / 2 + len / 2 + 5;

    const grid = this.data.grid;
    for (let j = 0; j < 10; j++) {

      for (let i = 0; i < 10; i++) {
        if (!this.gridObj[i.toString()]) {
          this.gridObj[i.toString()] = {};
        }

        const gridData = grid[i][j];
        const gridSpr = this.createGridRect(gridData, len);
        gridSpr.x = baseX + i * (len + dis);
        gridSpr.y = baseY;
        this.gridBg.addChild(gridSpr);
        gridSpr['logicX'] = i.toString();
        gridSpr['logicY'] = j.toString();

        this.gridObj[i.toString()][j.toString()] = gridSpr;
      }
      baseY += (len + dis);
    }
  }

  private createGridRect(data, len) {
    const grid = new MySprite();

    const shapeRect = new ShapeRect();
    shapeRect.setSize(len, len);
    shapeRect.x = - len / 2;
    shapeRect.y = - len / 2;
    shapeRect.visible = false;
    grid.addChild(shapeRect);

    if (data.text) {
      const label = new Label()
      label.text = data.text;
      label.setMaxSize(len * 0.9);
      label.textAlign = 'center';
      label.fontName = 'MMTextBook-Bold';
      label.fontSize = 48;
      label.fontColor = '#fff8c6';
      label.y = -3;
      grid.addChild(label);
      grid['label'] = label;

    }
    grid.width = len;
    grid.height = len;
    grid.anchorX = 0.5;
    grid.anchorY = 0.5;
    grid['data'] = data;
    return grid;
  }

  private clickedGrid(gridSpr: any) {
    const strip = this.createStrip(gridSpr.width * 1.05);
    strip.x = gridSpr.x;
    strip.y = gridSpr.y;

    console.log('gridSpr.logicX: ', gridSpr.logicX);
    console.log('gridSpr.logicY: ', gridSpr.logicY);

    this.gridBg.addChild(strip);
    this.curStrip = strip;
    this.startGrid = this.endGrid = gridSpr;
  }

  private createStrip(len) {
    const strip = new ShapeRectNew();
    strip.setSize(len, len, len / 2);
    strip.setOutLine('#663f30', 2);
    strip.anchorX = 0.5;
    strip.anchorY = 0.5;
    strip.fill = false;
    return strip;
  }

  private refreshCurStrip(endGrid) {
    if (this.endGrid == endGrid) {
      return;
    }

    const x1 = Number(this.startGrid['logicX']);
    const y1 = Number(this.startGrid['logicY']);
    const x2 = Number(endGrid['logicX']);
    const y2 = Number(endGrid['logicY']);
    const angle = Math.round( getAngleByPos(x1, y1, x2, y2) );

    if (angle % 45 !== 0) {
      return;
    }

    const len = getPosDistance(this.startGrid.x, this.startGrid.y, endGrid.x, endGrid.y);

    this.curStrip.width = len + this.curStrip.height;
    this.curStrip.rotation = angle - 90;
    this.curStrip.anchorX = this.curStrip.height / 2 / this.curStrip.width;
    console.log('angle: ', angle);

    this.endGrid = endGrid;
  }

  private cleanCurStrip() {
    const strip = this.curStrip;
    tweenChange(strip, {alpha: 0}, 0.1, () => {
      strip.parent.removeChild(this.curStrip);
    });
    this.curStrip = null;
  }

  private checkIsWord() {

    if (this.startGrid == this.endGrid) {
      return;
    }

    const word = this.getWord();

    if (word == this.curBubble.data.text) {
      this.showBubbleRight(this.curBubble);
      this.curBubble.isCheckEnd = true;
      this.curBubble = null;

      this.checkGameEnd();
      return;
    }

    this.playAudio('wrong');
    // this.bubbleArr.forEach((bubble) => {
    //   if (word == bubble.data.text && !bubble.isCheckEnd) {
    //     this.showBubbleRight(bubble);
    //     bubble.isCheckEnd = true;
    //     return;
    //   }
    // });

  }

  private getWord() {
    const startNumX = Number(this.startGrid['logicX']);
    const startNumY = Number(this.startGrid['logicY']);
    const endNumX = Number(this.endGrid['logicX']);
    const endNumY = Number(this.endGrid['logicY']);

    let subX = endNumX - startNumX;
    let subY = endNumY - startNumY;
    const loopNum = Math.max(Math.abs(subX), Math.abs(subY));

    subX = subX / loopNum;
    subY = subY / loopNum;

    this.curGridSprArr = [];
    let word = '';
    for (let i = 0; i <= loopNum; i++) {
      const logicX = (startNumX + subX * i).toString();
      const logicY = (startNumY + subY * i).toString();
      const gridSpr = this.gridObj[logicX][logicY];

      this.curGridSprArr.push(gridSpr);

      word += gridSpr['data'].text || ' ';
    }
    console.log('word: ', word);
    return word;
  }

  private showBubbleRight(bubble: any) {

    this.hideBubbleBg(bubble);
    this.showRightStrip();
    this.showPopAnima(this.endGrid.x, this.endGrid.y);
    this.showBubbleStone(bubble);
  }

  private createRightStrip() {
    const strip = this.curStrip;
    const rightStrip = new ShapeRectNew();
    rightStrip.setSize(strip.width, strip.height, strip.radius);
    rightStrip.anchorX = strip.anchorX;
    rightStrip.anchorY = strip.anchorY;
    rightStrip.rotation = strip.rotation;
    rightStrip.x = strip.x;
    rightStrip.y = strip.y;
    rightStrip.fillColor = '#cb7140';
    rightStrip.alpha = 0.72;
    return rightStrip;
  }

  private showPopAnima(x, y) {
    const animation = new MyAnimation();
    this.gridBg.addChild(animation);
    for (let i = 1; i <= 5; i++) {
      animation.addFrameByImg(this.images.get('star_' + i));
    }
    animation.delayPerUnit = 0.1;
    animation.loop = false;
    animation.playEndFunc = () => {
      animation.parent.removeChild(animation);
    };

    animation.x = x;
    animation.y = y;
    animation.play();
  }

  private showRightStrip() {
    const rightStrip = this.createRightStrip();
    this.gridBg.stripLayer.addChild(rightStrip);

    rightStrip.alpha = 0;
    tweenChange(rightStrip, {alpha: 0.72}, 0.5, () => {
      this.changeLetterColor();
    }, TWEEN.Easing.Cubic.In);

    this.playAudio('right');
  }

  private showBubbleStone(bubble: any) {
    tweenChange(bubble.stone, {alpha: 1}, 0.5);
    // this.playAudio('stone');
  }

  private clickedStartBtn() {
    this.canTouch = false;

    const tween1 = createTween(this.startBtn, {scaleX: 0.8, scaleY: 0.8}, 0.1, null, TWEEN.Easing.Cubic.Out);
    const tween2 = createTween(this.startBtn, {scaleX: 1, scaleY: 1}, 0.3, () => {
      this.openDoor();
    }, TWEEN.Easing.Elastic.Out);
    tweenQueue([tween1, tween2]);

    this.playAudio('btn');
  }

  private openDoor(cb = null) {
    const baseY = this.letterBg['baseY'];
    const mask = this.letterBg['maskSpr'];
    tweenChange(this.letterBg, {y: this.letterBg.y - this.letterBg.height}, 1, () => {
      this.canTouch = true;
      this.letterBg.visible = false;
      this.startBtn.visible = false;
      if (cb) {
        cb();
      }
    }, TWEEN.Easing.Cubic.In, () => {
      mask.y = baseY - this.letterBg.y;
    });
  }

  private clickedBubble(bubble: any) {

    if (this.curBubble && bubble != this.curBubble) {
      tweenChange(this.curBubble['bg'], {alpha: 0}, 0.1);
      // this.curBubble['bg'].alpha = 0;
    }

    console.log(' in clickedBubble');
    this.showWaveAnima(bubble);
    this.playAudio(bubble['data'].audio_url);

    this.curBubble = bubble;
  }

  private showWaveAnima(item) {
    const animation = new MyAnimation();
    for (let i = 1; i <= 3; i++) {
      animation.addFrameByImg(this.images.get('wave_' + i));
    }

    item['bg'].addChild(animation);
    tweenChange(item['bg'], {alpha: 1}, 0.1);
    // item['bg'].alpha = 1;
    animation.x = -2;
    animation.y = -30;

    animation.playEndFunc = () => {
      animation.parent.removeChild(animation);
    }
    animation.play();

  }

  private hideBubbleBg(bubble) {
    tweenChange(bubble['bg'], {alpha: 0}, 0.1);
  }

  private changeLetterColor() {
    this.curGridSprArr.forEach((gridSpr) => {
      gridSpr['label'].fontColor = '#fdf351';
    });
  }


  showEndPetal() {

    console.log('showEndPetal');
    this.petalTime = 0;
    this.showPetalFlag = true;
    this.endPageArr = [];

    this.addPetal();
    this.playAudio('finish');
    this.playAudio('star');

    setTimeout(() => {
      this.showPetalFlag = false;
      this.showRestartBtn();
    }, 5000);
  }

  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();
    }, 0 + this.petalTime);

    this.petalTime +=  5;
  }


  getPetal() {

    const petal = new MySprite();

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

  private checkGameEnd() {
    // for (let i = 0; i < this.bubbleArr.length; i++) {
    //   if (!this.bubbleArr[i].isCheckEnd) {
    //     return;
    //   }
    // }

    const isGameEnd = this.bubbleArr.every((bubble) => {
      return bubble.isCheckEnd;
    });

    if (isGameEnd) {
      this.gameEnd();
    }
  }

  private gameEnd() {
    this.showEndPetal();
    this.showEndStar();
  }

  private showEndStar() {

    this.petalTime = 0;
    this.starTime = 0;
    this.addStar();
  }


  private addStar() {
    if (!this.showPetalFlag) {
      return;
    }

    const star = this.getStar();
    this.endPageArr.push(star);

    const sx = star.scaleX;
    star.setScaleXY(0);

    const tween1 = createTween(star, {scaleX: sx, scaleY: sx}, star['time'], null, TWEEN.Easing.Cubic.In);
    const tween2 = createTween(star, {scaleX: 0, scaleY: 0}, star['time'], () => {
      removeItemFromArr(this.endPageArr, star);
    },  TWEEN.Easing.Cubic.Out);

   tweenQueue([tween1, tween2])

    setTimeout(() => {
      this.addStar();
    }, 0 + this.starTime);

    this.starTime += 3;
  }

  private getStar() {
    const star = createSprite('star');

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

    const randomX = Math.random() * this.canvasWidth;
    star.x = randomX;

    const randomY = Math.random() * this.canvasHeight;
    star.y = randomY;

    const randomT = 0.5 + Math.random() * 1;
    star['time'] = randomT;

    return star;
  }

  private initPopAnima() {

    const pop = createSprite('pop');
    this.popLayer.addChild(pop);

    const randomS = 0.3 + Math.random() * 0.7;
    pop.setScaleXY(randomS * this.mapScale);

    const px = this.canvasWidth * Math.random();
    pop.x = px;
    pop.y = this.canvasHeight + pop.height;

    const targetY = this.bg.y - 260 * this.bg.scaleY;
    const time = 5 + Math.random() * 5;
    tweenChange(pop, {y: targetY}, time, () => {
      pop.parent.removeChild(pop);
    });

    delayCall(0.1 + Math.random() * 0.3, this.initPopAnima.bind(this));
  }

  private showRestartBtn() {
    const restartBtn = createSprite('restart_btn');
    restartBtn.setScaleXY(this.mapScale);
    restartBtn.x = this.canvasWidth - 170 * this.mapScale;
    restartBtn.y = this.canvasHeight + restartBtn.height / 2 * restartBtn.scaleY;
    this.renderArr.push(restartBtn);
    this.restartBtn = restartBtn;

    this.canTouch = false;
    tweenChange(restartBtn, {y: this.canvasHeight - 55 * this.mapScale}, 0.9, () => {
      this.canTouch = true;
    }, TWEEN.Easing.Elastic.Out);

  }

  private clickedRestartBtn() {
    tweenChange(this.restartBtn, {y: this.canvasHeight + this.restartBtn.height * this.restartBtn.scaleY}, 0.3, () => {
      this.closeDoor();
      removeItemFromArr(this.renderArr, this.restartBtn);
      this.restartBtn = null;
    });

    this.letterBg.visible = true;
    this.canTouch = false;

    this.playAudio('btn');
  }

  private closeDoor() {
    const baseY = this.letterBg['baseY'];
    const mask = this.letterBg['maskSpr'];
    tweenChange(this.letterBg, {y: baseY}, 1, () => {
      // delayCall(1, this.openDoor.bind(this));
      this.cleanMap();
      this.openDoor();
    }, TWEEN.Easing.Cubic.Out, () => {
      mask.y = baseY - this.letterBg.y;
    });
  }

  private cleanMap() {
    this.cleanBubble();
    this.cleanGrid();
  }

  private cleanBubble() {
    this.bubbleArr.forEach((bubble) => {
      tweenChange(bubble.stone, {alpha: 0}, 0.3);
      bubble.isCheckEnd = false;
    });
  }

  private cleanGrid() {
    this.gridBg.stripLayer.removeChildren();
    for (let x in this.gridObj) {
      for (let y in this.gridObj[x]) {
        if (this.gridObj[x][y].label) {
          this.gridObj[x][y].label.fontColor = '#fff8c6';
        }
      }
    }
  }
}
