import {
  Component,
  ElementRef,
  ViewChild,
  OnInit,
  Input,
  OnDestroy,
  HostListener

} from "@angular/core";

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

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

import { Cartoon } from './Cartoon'
import { Subject } from "rxjs";
import { debounceTime, map, takeWhile, retry } from "rxjs/operators";
import * as _ from "lodash";
import TWEEN from "@tweenjs/tween.js";
import { demoData as defaultData } from '../../assets/default/formData/defaultData.js'

const zIndexMap = {
  mainBackground: 0,
  hotZooContainer: 100,
  hotZoo: 110,

}

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

export class PlayComponent implements OnInit, OnDestroy {

  g_cartoon = new Cartoon()

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

  // ------------  私有数据  ------------
  m_mapDownQueue = {} //按下事件处理队列
  m_mapDownArray = [] //按下事件处理队列
  m_mapDownObject = []

  m_mapUpQueue = {} //抬起事件处理队列
  m_mapUpArray = [] //抬起事件处理队列
  m_mapUpObject = []

  m_mapDragQueue = {} //拖拽事件处理队列
  m_mapDragArray = [] //拖拽事件处理队列
  m_mapDragObject = []

  m_mapMoveArray = [] //移动事件处理队列
  m_mapMoveObject = []
  m_endPageArr;
  m_showPetalFlag;
  m_elementPetalArr;
  m_showElementPetalFlag;
  m_PetalImage = "_scrap-pic-"  // 飘落动画
  m_renderArr // 渲染队列
  m_renderObject = [];
  m_frontCurtainArr = []; // 前置幕布
  m_defaultZindex = 0;
  m_setTimeoutIDs = [];
  m_setIntervalIDs = [];
  m_moveAsstantIntervalId = null;

  g_currentDragElementID: any = null; // 当前拖动的动画元件
  g_dragFirstClickY: any;
  g_dragFirstClickX: any;
  g_enableDragDistance: number = 20 * this.g_mapScale;
  g_enableDragging: any;
  // ------------------------------------


  // ------------  游戏逻辑数据  ------------
  m_elementDraged = null;
  m_currentQuestionIndex = 0;
  m_answerAll = []
  // ------------------------------------


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

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


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


  // 当数据加载完毕后，执行
  systemReady() {
    this.setLeftCornerTest()
    this.initGame()
  }

  // 屏幕尺寸变化后执行
  handleScreenResize() {
    this.cleanSystemVar()
    this.cleanGameVar();
    this.setLeftCornerTest();
    this.disableMoveAsstant();
    this.initSystem();
    this.initGame()
  }

  // 映射预加载图片[网路]资源 返回包含图片路径的数组
  mapToImageArray(contentObj) {
    let array = []
    array.push(contentObj.bgItem.url)

    contentObj.bgImages.leftTop && array.push(contentObj.bgImages.leftTop)
    contentObj.bgImages.middleTop && array.push(contentObj.bgImages.middleTop)
    contentObj.bgImages.rightTop && array.push(contentObj.bgImages.rightTop)
    contentObj.bgImages.leftMiddle && array.push(contentObj.bgImages.leftMiddle)
    contentObj.bgImages.rightMiddle && array.push(contentObj.bgImages.rightMiddle)
    contentObj.bgImages.leftBottom && array.push(contentObj.bgImages.leftBottom)
    contentObj.bgImages.middleBottom && array.push(contentObj.bgImages.middleBottom)
    contentObj.bgImages.rightBottom && array.push(contentObj.bgImages.rightBottom)

    contentObj.hotZoneItemArr.forEach(element => {
      array.push(element.media.image_url)
    });
    return array
  }

  // 映射预加载音频[网路]资源 返回包含音频路径的数组
  mapToAduioArray(contentObj) {
    let array = []

    contentObj.bgItem.mainTextAudio && array.push(contentObj.bgItem.mainTextAudio)
    contentObj.bgItem.audio_url && array.push(contentObj.bgItem.audio_url)
    contentObj.bgItem.wordLeft_audio_url && array.push(contentObj.bgItem.wordLeft_audio_url)
    contentObj.bgItem.wordRight_audio_url && array.push(contentObj.bgItem.wordRight_audio_url)

    contentObj.bgItem.questions.forEach(element => {
      element.audio_url && array.push(element.audio_url)
    });

    contentObj.hotZoneItemArr.forEach(element => {
      element.media.audio_url && array.push(element.media.audio_url)
    });

    return array
  }

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

  initGame() {
    console.log(this.g_formData);
    this.initSelectPannel();
    this.initMainWindow();
    this.initLeftPart();
    this.initRightPart();
    this.initWordLines();
    this.initTitle_Part();
    this.initTitle_Main();
    this.initAudioPlayerAll();
    this.initBackgroundSide();
  }

  cleanGameVar() {
    this.m_elementDraged = null;
    this.m_currentQuestionIndex = -1;
    this.m_answerAll = []
  }

  startGame() {

  }

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

  endGame() {

  }

  initSelectPannel() {
    let element = this.g_cartoon.createCartoonElementImageFunc("select-background", "select-bg", (w, h) => {
      return {
        sx: 1065 * this.g_mapScale / w,
        sy: 188 * this.g_mapScale / h
      }
    }, (w, h) => {
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_cartoon.getOrigin().y + (114 + 168 / 2) * this.g_mapScale
      }
    })

    this.render(element.ref)
  }

  initTitle_Part() {
    let element = this.g_cartoon.createCartoonElementImageFunc("part-title", "bg_title_part", (w, h) => {
      return {
        sx: 57 * this.g_mapScale / w,
        sy: 65 * this.g_mapScale / h,
      }
    }, (w, h) => {
      return {
        x: 53 * this.g_mapScale + (57 * this.g_mapScale) / 2,
        y: (65 * this.g_mapScale) / 2
      }
    })
    element.ref.x = this.g_partTitle_x - 20 * this.g_mapScale + (57 * this.g_mapScale) / 2;
    let text = this.g_cartoon.createLabel(this.g_formData.bgItem.NO, "BerlinSansFBDemi-Bold", "#FFFFFF", 48)
    element.ref.addChild(text)

    let partBG = this.g_cartoon.createCartoonElementImageFunc("part-title-background", "part_number_bg", (w, h) => {
      return {
        sx: 121 * this.g_mapScale / w,
        sy: 69 * this.g_mapScale / h,
      }
    }, (w, h) => {
      return {
        x: (121 * this.g_mapScale) / 2,
        y: (69 * this.g_mapScale) / 2
      }
    })

    this.render(partBG.ref)
    this.render(element.ref)
  }

  initTitle_Main() {
    let labelWidth = 0
    let text = this.g_cartoon.createCartoonElementLabelFunc("main-title", this.g_formData.bgItem.mainText, "FuturaBT-Medium", "#000000", 36 * this.g_mapScale, (w, h) => {
      labelWidth = w / 2
      return {
        x: this.g_partTitle_x - 20 * this.g_mapScale + labelWidth * this.g_mapScale,
        y: 5 * this.g_mapScale + (65 * this.g_mapScale) / 2
      }
    })

    let background = this.g_cartoon.createCartoonElementImageFunc("main-title-background", "bg_50_20", (w, h) => {
      return {
        sx: labelWidth * 2 / w,
        sy: 50 * this.g_mapScale / h,
      }
    }, (w, h) => {
      return {
        x: this.g_partTitle_x + labelWidth + 60 * this.g_mapScale,
        y: 5 * this.g_mapScale + (65 * this.g_mapScale) / 2
      }
    })

    text.ref.x = this.g_partTitle_x + 60 * this.g_mapScale
    text.ref.textAlign = "left"

    this.subscribeMapDownEvent(background.id, () => {
      this.g_cartoon.stopAllAudio()
      this.g_cartoon.playAudio(this.g_formData.bgItem.mainTextAudio)
      this.g_enableMapDown = true;
    })

    this.render(background.ref)
    this.render(text.ref)
  }

  initMainWindow() {
    let element = this.g_cartoon.createCartoonElementImageFunc("select-container", "defalut_container", (w, h) => {
      return {
        sx: 960 * this.g_mapScale / w,
        sy: 155 * this.g_mapScale / h
      }
    }, (w, h) => {
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_cartoon.getOrigin().y + (114 + 168 / 2) * this.g_mapScale
      }
    })


    let contentImageWidth = 0
    let element_t = this.g_cartoon.createCartoonElementImageFunc("window-image-container", this.g_formData.bgItem.url, (w, h) => {
      let s = Math.min(960 / w, 155 / h)
      contentImageWidth = (w * s) / 2
      return {
        sx: s,
        sy: s
      }
    }, (w, h) => {
      return {
        x: 0,
        y: 0
      }
    })

    element.originX = element.ref.x - contentImageWidth * element.ref.scaleX
    element.originY = element.ref.y - 155 * this.g_mapScale / 2

    element.ref.addChild(element_t.ref)

    this.render(element.ref)
    this.initHotZooes(element)
  }

  initHotZooes(parent) {
    let hotZoneItemArr = this.g_formData.hotZoneItemArr
    hotZoneItemArr.forEach((item, index) => {
      let element = null

      element = this.g_cartoon.createCartoonElement("image-puzzle-" + index, "MySprite")
      element.ref.init(this.g_cartoon.images.get(item.media.image_url))
      element.ref.scaleX = (item.rect.width / element.ref.width) * parent.ref.scaleX
      element.ref.scaleY = (item.rect.height / element.ref.height) * parent.ref.scaleY
      element.ref.rotation = item.rect.rotation
      element.ref.x = parent.originX + (item.rect.x + item.rect.width / 2) * parent.ref.scaleX * 1.05
      element.ref.y = parent.originY + (item.rect.y + item.rect.height / 2) * parent.ref.scaleY * 1.05
      element.initX = element.ref.x;
      element.initY = element.ref.y
      element.margin = 0
      element.hotZooIndex = index;
      element.ref.alpha = 0;
      let elementDrag = null
      elementDrag = this.g_cartoon.createCartoonElementLabel("text-puzzle-" + index, " " + item.media.text + " ", "MMTextBook-Bold", "#000", 48 * this.g_mapScale, -500, -500)
      this.render(element.ref)
      this.render(elementDrag.ref, 10)

      element.timeoutID = null
      this.subscribeMapDragEvent(element.id, () => {
        element.timeoutID = setTimeout(() => {
          if (item.media.audio_url) {
            this.g_cartoon.stopAllAudio()
            this.g_cartoon.playAudio(item.media.audio_url)
          }
        }, this.g_dragDelay);
      }, () => {
        if (element.timeoutID) {
          clearTimeout(element.timeoutID)
          element.timeoutID = null
        }
        elementDrag.ref.x = this.g_clickX,
          elementDrag.ref.y = this.g_clickY
      }, () => {
        elementDrag.ref.x = elementDrag.initX,
          elementDrag.ref.y = elementDrag.initY
        this.m_elementDraged = element.id;
        setTimeout(() => {
          this.m_elementDraged = null;
        }, 100);
        this.g_enableMapUp = true;
        this.g_enableMapDown = true;
      })
    });
  }

  initLeftPart() {
    if (!this.g_formData.bgItem.wordLeft_text) {
      return;
    }

    let element = this.g_cartoon.createCartoonElementImageFunc("left-part-container", "oval-copy", (w, h) => {
      return {
        sx: 100 * this.g_mapScale / w,
        sy: 100 * this.g_mapScale / h
      }
    }, (w, h) => {
      return {
        x: this.g_cartoon.getOrigin().x + (108 + 96 / 2) * this.g_mapScale,
        y: this.g_cartoon.getOrigin().y + (150 + 96 / 2) * this.g_mapScale
      }
    })
    element.hotZooIndex = -1;

    let text = null
    text = this.g_cartoon.createCartoonElementLabel("text-puzzle-left", " " + this.g_formData.bgItem.wordLeft_text + " ", "MMTextBook-Bold", "#FFF", 48)
    element.ref.addChild(text.ref)

    let textDrag = null
    textDrag = this.g_cartoon.createCartoonElementLabel("text-puzzle-left-drag", " " + this.g_formData.bgItem.wordLeft_text + " ", "MMTextBook-Bold", "#C8161E", 48 * this.g_mapScale, -500, -500)
    this.render(textDrag.ref, 10)
    this.render(element.ref)

    element.timeoutID = null
    this.subscribeMapDragEvent(element.id, () => {
      element.timeoutID = setTimeout(() => {
        if (this.g_formData.bgItem.wordLeft_audio_url) {
          this.g_cartoon.stopAllAudio()
          this.g_cartoon.playAudio(this.g_formData.bgItem.wordLeft_audio_url)
        }
      }, this.g_dragDelay);
      text.ref.alpha = 0.5;
    }, () => {
      if (element.timeoutID) {
        clearTimeout(element.timeoutID)
        element.timeoutID = null
      }
      textDrag.ref.x = this.g_clickX,
        textDrag.ref.y = this.g_clickY
    }, () => {
      text.ref.alpha = 1;
      textDrag.ref.x = textDrag.initX,
        textDrag.ref.y = textDrag.initY
      this.m_elementDraged = element.id;
      setTimeout(() => {
        this.m_elementDraged = null;
      }, 100);
      this.g_enableMapUp = true;
      this.g_enableMapDown = true;
    })

  }

  initRightPart() {
    if (!this.g_formData.bgItem.wordRight_text) {
      return;
    }

    let element = this.g_cartoon.createCartoonElementImageFunc("right-part-container", "oval-copy", (w, h) => {
      return {
        sx: 100 * this.g_mapScale / w,
        sy: 100 * this.g_mapScale / h
      }
    }, (w, h) => {
      return {
        x: this.g_cartoon.getOrigin().x + (1087 + 96 / 2) * this.g_mapScale,
        y: this.g_cartoon.getOrigin().y + (150 + 96 / 2) * this.g_mapScale
      }
    })
    element.hotZooIndex = -2;

    let text = null
    text = this.g_cartoon.createCartoonElementLabel("text-puzzle-right", " " + this.g_formData.bgItem.wordRight_text + " ", "MMTextBook-Bold", "#FFF", 48)
    element.ref.addChild(text.ref)

    let textDrag = null
    textDrag = this.g_cartoon.createCartoonElementLabel("text-puzzle-right-drag", " " + this.g_formData.bgItem.wordRight_text + " ", "MMTextBook-Bold", "#C8161E", 48 * this.g_mapScale, -500, -500)
    this.render(textDrag.ref, 10)
    this.render(element.ref)

    element.timeoutID = null
    this.subscribeMapDragEvent(element.id, () => {
      element.timeoutID = setTimeout(() => {
        if (this.g_formData.bgItem.wordRight_audio_url) {
          this.g_cartoon.stopAllAudio()
          this.g_cartoon.playAudio(this.g_formData.bgItem.wordRight_audio_url)
        }
      }, this.g_dragDelay);
      text.ref.alpha = 0.5;
    }, () => {
      if (element.timeoutID) {
        clearTimeout(element.timeoutID)
        element.timeoutID = null
      }
      textDrag.ref.x = this.g_clickX,
        textDrag.ref.y = this.g_clickY
    }, () => {
      text.ref.alpha = 1;
      textDrag.ref.x = textDrag.initX,
        textDrag.ref.y = textDrag.initY
      this.m_elementDraged = element.id;
      setTimeout(() => {
        this.m_elementDraged = null;
      }, 100);
      this.g_enableMapUp = true;
      this.g_enableMapDown = true;
    })
  }

  initWordLines() {
    let totalCount = this.g_formData.bgItem.questions.length
    this.g_formData.bgItem.questions.forEach((item, index) => {
      let textAll = []
      item.combin.forEach(combinIndex => {
        let key = Number(combinIndex)
        let value = ""
        if (key == -1) {
          value = this.g_formData.bgItem.wordLeft_text;
        } else if (key == -2) {
          value = this.g_formData.bgItem.wordRight_text;
        } else if (this.g_formData.hotZoneItemArr.length > 0) {
          value = this.g_formData.hotZoneItemArr[combinIndex].media.text
        }
        textAll.push(
          {
            key,
            value
          }
        )
      });

      let element = this.g_cartoon.createCartoonElementImageFunc(`line-word-container-${index}`, "bg_250_46", (w, h) => {
        return {
          sx: 250 * this.g_mapScale / w,
          sy: 46 * this.g_mapScale / h
        }
      }, (w, h) => {
        if(totalCount<=6) {
          let y = 0;
          if (index > 2) {
            y = this.g_cartoon.getOrigin().y + (509 + 46 / 2) * this.g_mapScale
          } else {
            y = this.g_cartoon.getOrigin().y + (383 + 46 / 2) * this.g_mapScale
          }
          
          let xIndex = index % 3
          return {
            x: 170 * this.g_mapScale + this.g_cartoon.getOrigin().x + (58 + 250 / 2) * this.g_mapScale + 295 * this.g_mapScale * xIndex,
            y: y
          }
        } else {
          let y = 0;
          if (index > 3) {
            y = this.g_cartoon.getOrigin().y + (509 + 46 / 2) * this.g_mapScale
          } else {
            y = this.g_cartoon.getOrigin().y + (383 + 46 / 2) * this.g_mapScale
          }
  
          let xIndex = index % 4
          return {
            x: this.g_cartoon.getOrigin().x + (58 + 250 / 2) * this.g_mapScale + 295 * this.g_mapScale * xIndex,
            y: y
          }
        }
        
      })

      element.questionIndex = index;
      element.done = false;
      element.audio_url = item.audio_url ? item.audio_url : ""

      let number = this.g_cartoon.createCartoonElementImageFunc(`line-word-number-${index}`, `num-${index + 1}`, (w, h) => {
        return {
          sx: 46 / w,
          sy: 46 / h
        }
      }, (w, h) => {
        return {
          x: -102,
          y: 0
        }
      })

      let line = this.g_cartoon.createCartoonElementImageFunc(`line-word-line-${index}`, "line", (w, h) => {
        return {
          sx: 200 / w,
          sy: 2 / h
        }
      }, (w, h) => {
        return {
          x: 25,
          y: 22
        }
      })

      let startX = -70
      let totalWidth = 0
      let answerIndex = []

      this.m_answerAll.push([])
      textAll.forEach((item, partIndex) => {
        let color = "#000"
        if (item.key < 0) {
          color = "#C8161E"
        }
        answerIndex.push(item.key)
        this.m_answerAll[index].push(`word-part-${index}-${item.key}-${partIndex}`)
        let textShow = this.g_cartoon.createCartoonElementLabel(`word-part-${index}-${item.key}-${partIndex}`, item.value, "MMTextBook-Bold", color, 48, startX, 0)
        textShow.isShown = false;
        textShow.ref.alpha = 0;
        textShow.ref.x = startX + textShow.ref.width / 2
        textShow.ref.y = -10
        startX += textShow.ref.width
        totalWidth += textShow.ref.width
        element.ref.addChild(textShow.ref)
      })

      startX = -totalWidth / 2 + 20
      this.m_answerAll[index].forEach(element => {
        let el = this.g_cartoon.getCartoonElement(element)
        el.ref.x = startX + el.ref.width / 2
        startX += el.ref.width
      });

      this.subscribeMapUpEvent(element.id, () => {
        if (!this.m_elementDraged) {
          this.g_enableMapUp = true;
          return;
        }
        let dragElement = this.g_cartoon.getCartoonElement(this.m_elementDraged)
        if (!dragElement) {
          this.g_enableMapUp = true;
          return;
        }

        if (!this.m_elementDraged) {
          this.g_enableMapUp = true;
          return;
        }

        if (this.m_currentQuestionIndex != element.questionIndex) {
          this.g_cartoon.stopAllAudio()
          this.g_cartoon.playAudio("cuowu")
          this.g_enableMapUp = true;
          return;
        }

        let right = false;
        for (let index = 0; index < answerIndex.length; index++) {
          if (dragElement.hotZooIndex == answerIndex[index]) {
            let textShow = this.g_cartoon.getCartoonElement(`word-part-${this.m_currentQuestionIndex}-${dragElement.hotZooIndex}-${index}`)
            if (!textShow.isShown) {
              textShow.isShown = true;
              textShow.ref.alpha = 1;
              right = true;
              break;
            }
          }
        }

        this.g_cartoon.stopAllAudio();
        if(right) {
          this.g_cartoon.playAudio("sm-choice-correct");
        } else {
          this.g_cartoon.playAudio("cuowu");
        }

        if (this.checkQuestionDone()) {
          this.m_currentQuestionIndex++
          element.done = true;
          if (this.m_currentQuestionIndex > this.g_formData.bgItem.questions.length) {
            this.endGame()
          }
        }

        this.m_elementDraged = null;
        this.g_enableMapUp = true;
      })

      this.subscribeMapDownEvent(element.id, () => {
        if (element.done) {
          this.g_cartoon.stopAllAudio()
          this.g_cartoon.playAudio(element.audio_url)
        }
        this.g_enableMapDown = true;
      })

      element.ref.addChild(line.ref)
      element.ref.addChild(number.ref)
      this.render(element.ref)
    })
  }

  checkQuestionDone() {
    let partAll = []
    let result = []
    if (this.m_currentQuestionIndex >= 0) {
      partAll = this.m_answerAll[this.m_currentQuestionIndex] ? this.m_answerAll[this.m_currentQuestionIndex] : []
    }

    partAll.forEach(item => {
      let el = this.g_cartoon.getCartoonElement(item)
      if (el && el.isShown) {
        result.push(true)
      } else {
        result.push(false)
      }
    })

    if (result.indexOf(false) == -1) {
      return true
    } else {
      return false
    }
  }

  initAudioPlayerAll() {
    if (!this.g_formData.bgItem.audio_url) {
      return
    }

    let element = this.g_cartoon.createCartoonElementImageFunc(`audio-all`, "icon_audio_container", (w, h) => {
      return {
        sx: 76 * this.g_mapScale / w,
        sy: 77 * this.g_mapScale / h,
      }
    }, (w, h) => {
      return {
        x: this.g_cartoon.getOrigin().x + (1190 + 80 / 2) * this.g_mapScale,
        y: this.g_cartoon.getOrigin().y + (630 + 81.5 / 2) * this.g_mapScale
      }
    })

    let play = this.g_cartoon.createCartoonElementImageFunc(`audio-all-play`, "icon_paly", (w, h) => {
      return {
        sx: 76 / w,
        sy: 77 / h,
      }
    }, (w, h) => {
      return {
        x: 0,
        y: 0
      }
    })

    let pause = this.g_cartoon.createCartoonElementImageFunc(`audio-all-pause`, "icon_pause", (w, h) => {
      return {
        sx: 76 / w,
        sy: 77 / h,
      }
    }, (w, h) => {
      return {
        x: 0,
        y: 0
      }
    })
    pause.ref.visible = true;
    element.playIcon = play
    element.pauseIcon = pause

    element.ref.addChild(element.pauseIcon.ref)
    element.ref.addChild(element.playIcon.ref)

    element.isPlaying = false
    element.audio = this.g_cartoon.audio.get(this.g_formData.bgItem.audio_url)
    if (element.audio) {
      element.audio.onended = () => {
        element.pause()
      }
    }

    element.play = () => {
      if (!element.audio) {
        return
      }
      element.isPlaying = true;
      this.g_cartoon.stopAllAudio()
      element.audio.play()
      element.playIcon.ref.visible = false;
      element.pauseIcon.ref.visible = true;
    }

    element.pause = () => {
      if (!element.audio) {
        return
      }
      element.isPlaying = false;
      element.audio.pause()
      element.playIcon.ref.visible = true;
      element.pauseIcon.ref.visible = false;
    }

    element.enable = true;

    this.subscribeMapDownEvent(element.id, () => {
      if (!element.audio || !element.enable) {
        this.g_enableMapDown = true;
        return
      }
      if (element.isPlaying) {
        element.pause()
      } else {
        element.play()
      }
      this.g_enableMapDown = true;
    })

    this.render(element.ref)
  }

  resetLongAudio() {
    let audio = this.g_cartoon.getCartoonElement(`audio-all`)
    if (audio.enable) {
      audio.pause()
    }
  }

  calcPosition(position, width, height) {
    let map = {
      leftTop: { x: 0, y: 0 },
      middleTop: { x: this.g_canvasWidth / 2, y: 0 },
      rightTop: { x: this.g_canvasWidth, y: 0 },
      leftMiddle: { x: 0, y: this.g_canvasHeight / 2 },
      rightMiddle: { x: this.g_canvasWidth, y: this.g_canvasHeight / 2 },
      leftBottom: { x: 0, y: this.g_canvasHeight },
      middleBottom: { x: this.g_canvasWidth / 2, y: this.g_canvasHeight },
      rightBottom: { x: this.g_canvasWidth, y: this.g_canvasHeight }
    }

    switch (position) {
      case "leftTop":
        map["leftTop"].x += width * this.g_mapScale / 2,
          map["leftTop"].y += height * this.g_mapScale / 2;
        return map["leftTop"];
      case "middleTop":
        map["middleTop"].y += height * this.g_mapScale / 2;
        return map["middleTop"];
      case "rightTop":
        map["rightTop"].x -= width * this.g_mapScale / 2,
          map["rightTop"].y += height * this.g_mapScale / 2;
        return map["rightTop"];
      case "leftMiddle":
        map["leftMiddle"].x += height * this.g_mapScale / 2;
        return map["leftMiddle"];
      case "rightMiddle":
        map["rightMiddle"].x -= height * this.g_mapScale / 2;
        return map["rightMiddle"];
      case "leftBottom":
        map["leftBottom"].x += width * this.g_mapScale / 2,
          map["leftBottom"].y -= height * this.g_mapScale / 2;
        return map["leftBottom"];
      case "middleBottom":
        map["middleBottom"].y -= height * this.g_mapScale / 2;
        return map["middleBottom"];
      case "rightBottom":
        map["rightBottom"].x -= width * this.g_mapScale / 2,
          map["rightBottom"].y -= height * this.g_mapScale / 2;
        return map["rightBottom"];
    }

  }

  initBackgroundSide() {
    for (let key in this.g_formData.bgImages) {
      if (!this.g_formData.bgImages[key]) {
        continue
      }
      let ele = this.g_cartoon.createCartoonElementImageFunc(`background-side-${key}`, this.g_formData.bgImages[key], (w, h) => {
        return {
          sx: this.g_mapScale,
          sy: this.g_mapScale,
        }
      }, (w, h) => {
        return this.calcPosition(key, w, h)
      })
      console.log(this.g_formData.bgImages[key])
      this.render(ele.ref, -1)
    }
  }





















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

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

        this.g_data = data;
        this.g_formData = data.contentObj;

        if (data.contentObj && data.contentObj.data) {
          this.g_data = data.contentObj.data;
          this.g_formData = data.contentObj.data.contentObj
        }
      } else {
        this.g_data = {};
      }


      if (!this.g_data.contentObj) {
        this.g_data.contentObj = {};
        this.g_formData = {};
      }

      this.initDefaultData();
      this.initAudio();
      this.initImg();

      // 预加载资源
      this.g_cartoon.loadResources().then(() => {
        window["air"].hideAirClassLoading(this.g_KEY, this.g_data);
        console.log('hideAirClassLoading');
        this.initSystem();
        this.update();
        this.systemReady()
      });

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

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


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

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

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


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

    this.m_mapDownArray.forEach((item) => {
      if (!myStopPropagation) {
        if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(item.id))) {
          this.g_enableMapDown = false;
          if (this.m_mapDragObject[item.id]) {
            this.m_mapDragObject[item.id].trigger()
            let dragElement = this.g_cartoon.getCartoonElement(item.id)
            if (dragElement) {
              this.g_currentDragElementID = item.id
              this.g_dragFirstClickX = this.g_clickX
              this.g_dragFirstClickY = this.g_clickY
              this.enableMoveAsstant(() => {
                this.m_mapDragObject[item.id].move(dragElement)
              })
            }
            myStopPropagation = true;
          } else {
            if (item.callback()) {
              myStopPropagation = true;
            }
          }
        }
      }
    })
  }

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

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

  mapUp(event) {
    let myStopPropagation = false;
    if (!this.g_enableMapUp) {
      return;
    }
    this.m_mapUpArray.forEach((item) => {
      if (!myStopPropagation) {
        if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(item.id))) {
          this.g_enableMapUp = false;
          if (item.callback()) {
            myStopPropagation = true;
          }
          this.g_currentDragElementID = null;
          this.disableMoveAsstant();
        }
      }
    })
  }

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

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

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

  initListener() {
    const element = this.canvas.nativeElement;
    this.g_winResizeEventStream.pipe(debounceTime(500)).subscribe(data => {
      this.renderAfterResize();
    });

    const addTouchListener = () => {
      element.addEventListener('touchstart', touchDownFunc);
      element.addEventListener('touchmove', touchMoveFunc);
      element.addEventListener('touchend', touchUpFunc);
      element.addEventListener('touchcancel', touchUpFunc);
    };
    const removeTouchListener = () => {
      element.removeEventListener('touchstart', touchDownFunc);
      element.removeEventListener('touchmove', touchMoveFunc);
      element.removeEventListener('touchend', touchUpFunc);
      element.removeEventListener('touchcancel', touchUpFunc);
    };

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

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

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

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

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

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

    addMouseListener();
    addTouchListener();
  }

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

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

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

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

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

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

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

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

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

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

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

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

  subscribeMapDragEvent(id, trigger, move, release) {
    this.subscribeMapDownEvent(id, () => { })
    this.m_mapDragObject[id] = {
      trigger,
      move,
      release
    };
  }

  releaseDragElement() {
    if (this.g_currentDragElementID) {
      this.m_mapDragObject[this.g_currentDragElementID].release()
    }
  }

  // 全局初始化入口，当资源加载完毕后执行
  initSystem() {

    this.g_canvasWidth = this.wrap.nativeElement.clientWidth;
    this.g_canvasHeight = this.wrap.nativeElement.clientHeight;

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

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

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

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

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


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

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

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

    this.subscribeMapUpEvent("g-ackground-color", () => {
      if (this.g_currentDragElementID) {
        this.releaseDragElement()
        this.g_currentDragElementID = null;
      } else {
        this.g_enableMapUp = true;
      }
    })
  }

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

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

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

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

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

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

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

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

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

      this.m_renderArr = []
      this.m_renderObject.forEach(item => {
        this.m_renderArr.push(item.element)
      })
    }
  }

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

  enableMoveAsstant(callback) {
    if (this.m_moveAsstantIntervalId) {
      this.g_enableDragging = false;
      clearInterval(this.m_moveAsstantIntervalId)
    }
    this.m_moveAsstantIntervalId = setInterval(() => {
      if (this.g_enableDragging) {
        callback()
      } else {
        let moveX = Math.abs(this.g_dragFirstClickX - this.g_clickX)
        let moveY = Math.abs(this.g_dragFirstClickY - this.g_clickY)
        if (moveX > this.g_enableDragDistance || moveY > this.g_enableDragDistance) {
          this.g_enableDragging = true;
        }
      }
    }, 50)
  }

  disableMoveAsstant() {
    this.g_enableDragging = false;
    clearInterval(this.m_moveAsstantIntervalId)
  }

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

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

  setLeftCornerTest() {
    const bgRect = new ShapeRect();
    bgRect.setSize(57, 65);
    bgRect.fillColor = '#f8c224';
    const sx = this.g_canvasWidth / this.g_canvasBaseW;
    bgRect.setScaleXY(sx);
    bgRect.x = 65 * sx;
    this.g_partTitle_x = bgRect.x
    this.g_mainTitle_x = bgRect.x + 80 * sx
    bgRect.alpha = 0.5
    bgRect.visible = this.g_showLeftCornerTest
    this.render(bgRect, 9999);
  }

  alignCenter(elementArray, spacing, withAni?, callback?) {
    let totlaWidth = 0
    let length = elementArray.length
    elementArray.forEach(element => {
      let bd = element.ref.getBoundingBox()
      totlaWidth += bd.width
    });
    totlaWidth += (elementArray.length - 1) * spacing
    let laseX = this.g_canvasWidth / 2 - totlaWidth / 2;
    elementArray.forEach((element, index) => {
      let bd = element.ref.getBoundingBox()
      if (withAni) {
        tweenChange(element.ref, { x: laseX + bd.width / 2 }, 0.2, () => {
          this.g_cartoon.saveSize(element.id)
          if (length == (index + 1)) {
            callback && callback()
          }
        })
      } else {
        element.ref.x = laseX + bd.width / 2
        this.g_cartoon.saveSize(element.id)
        if (length == (index + 1)) {
          callback && callback()
        }
      }

      laseX = (laseX + bd.width + spacing)
    });
  }

  alignLeft(elementArray, spacing, startX = 0, withAni?, callback?) {
    let laseX = startX;
    elementArray.forEach((element, index) => {
      let bd = element.ref.getBoundingBox()
      if (withAni) {
        tweenChange(element.ref, { x: laseX + bd.width / 2 }, 0.2, () => {
          this.g_cartoon.saveSize(element.id)
          if (length == (index + 1)) {
            callback && callback()
          }
        })
      } else {
        element.ref.x = laseX + bd.width / 2
        this.g_cartoon.saveSize(element.id)
        if (length == (index + 1)) {
          callback && callback()
        }
      }

      laseX = (laseX + bd.width + spacing)
    });
  }

  createTestLine(x, y, height, color?) {
    var colorAll = ['#ff0000', '#eb4310', '#f6941d', '#fbb417', '#ffff00', '#cdd541', '#99cc33', '#3f9337', '#219167', '#239676', '#24998d', '#1f9baa', '#0080ff', '#3366cc', '#333399', '#003366', '#800080', '#a1488e', '#c71585', '#bd2158'];
    color = color ? color : colorAll[Math.floor(Math.random() * 20)]
    this.render(this.g_cartoon.createRectangula({
      width: 5,
      height: height,
      x: x,
      y: y,
      fillColor: color
    }), 9999)
  }

  getSuitSizeBackground(key, width) {
    let bgAll = multiSizeBackground[key]
    if (!bgAll) {
      return ""
    }
    let suitFileName = ""
    for (let fileName in bgAll) {
      if (Number(fileName) >= width) {
        suitFileName = bgAll[fileName]
        break
      }
    }
    return suitFileName
  }







































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



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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

    return petal;
  }


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

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

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

  showBlingStar(element, callback?) {
    let rect = element.getBoundingBox()
    showBlingBling(this.g_cartoon.images.get('icon_star'), rect, element, 0.5, 1, 0.08, 0.5);
    this.m_setTimeoutIDs.push(setTimeout(() => {
      callback && callback()
    }, 2000))
    // setTimeout(()=>{
    //   showBlingBling(this.g_cartoon.images.get('icon_star'), rect, element, 0.5, 1, 0.08, 1.8);
    // },200)
    // setTimeout(()=>{
    //   showBlingBling(this.g_cartoon.images.get('icon_star'), rect, element, 0.5, 1, 0.08, 1.6);
    // },400)
  }

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

