import {
  Component,
  ElementRef,
  ViewChild,
  OnInit,
  Input,
  OnDestroy,
  HostListener
  
} from "@angular/core";
import { UUID } from 'angular2-uuid';

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

import { localImages, localAudios } 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 defauleFormData from '../../assets/play/default/formData/defaultData.js'

const zIndexMap = {
  cutter: 130,
  dot: 120,
  line: 115,
  longAudioPlay: 110,
  cardUp: 110,
  cardBorder: 111,
  lianzi: 100,
  window: 50,
  star: 1000,
  animation: 9999
}

@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_enableMapDown = true; // 触摸使能
  g_enableMapUp = true; // 抬起使能
  g_enableMapMove = true; // 移动ss使能
  g_canvasLeft;
  g_canvasTop;
  g_animationId: any;
  g_mapScale = 1; // 缩放比例
  g_KEY = "DataKey_PU03";
  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;
  // ------------------------------------

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

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

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

  // ------------  游戏逻辑数据  ------------
  m_currentSelectedCard = ""
  m_allUpCards = []
  m_fishes = []
  // ------------------------------------


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


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


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

  // 映射预加载图片[网路]资源 返回包含图片路径的数组
  mapToImageArray(contentObj){
    let array = []
    if(contentObj.mainQuestion.type == "Image"){
      array.push(contentObj.mainQuestion.image_url)
    }
    return array
  }

  // 映射预加载音频[网路]资源 返回包含音频路径的数组
  mapToAduioArray(contentObj){
    let array = []
    if(contentObj.mainQuestion.type == "LongAudio"){
      array.push(contentObj.mainQuestion.longAudio_url)
    }
    array.push(contentObj.mainQuestion.shortAudio_url)
    return array
  }

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

  initGame(){
    console.log(this.g_formData);
    this.initBackground();
    this.initXuanZhuanTai();
    this.initTable();
    this.initShowWindow();
    this.initCard_up();
    this.initCard_down();
    this.initCutter()
    this.initHand()
    this.initBlackMask()
  }

  cleanGameVar(){
    this.g_enableMapDown = true;
    this.stopEndPatal()
    this.m_currentSelectedCard = ""
    this.m_allUpCards = []
    this.m_fishes = []
  }

  startGame(){

  }

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

  endGame(){
    this.g_cartoon.playAudio("xing_xing")
    this.showBlingStar(this.g_cartoon.getCartoonElement("bg-wall-star-container").ref, ()=>{
      this.g_cartoon.playAudio("gu_zhang")
      this.g_cartoon.getCartoonElement("mask-layer").in(()=>{
        this.g_enableMapDown = false;
        this.showEndPatal()
        this.clapHand()
      })
    })
  }


  initBackground(){
    let PA = this.g_cartoon.createCartoonElementImageFunc("bg-wall-l1", "bg_wall", (w, h)=>{
      return {
        sx: this.g_canvasWidth / w,
        sy: (this.g_cartoon.getOrigin().y + 357*this.g_mapScale) / h
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: (this.g_cartoon.getOrigin().y + 357*this.g_mapScale)/2
      }
    })

    let PB = this.g_cartoon.createCartoonElementImageFunc("bg-wall-l2", "bg_floor_1", (w, h)=>{
      return {
        sx: this.g_canvasWidth / w,
        sy: (24*this.g_mapScale) / h
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: PA.ref.y + (PA.ref.height*this.g_mapScale)/2 + (24*this.g_mapScale)/2
      }
    })

    let PC = this.g_cartoon.createCartoonElementImageFunc("bg-wall-l3", "bg_floor_2", (w, h)=>{
      return {
        sx: this.g_canvasWidth / w,
        sy: this.g_canvasHeight / h
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_canvasHeight / 2,
      }
    })

    let PD = this.g_cartoon.createCartoonElementImageFunc("bg-wall-star-container", "star_container", (w, h)=>{
      return {
        sx: 1280 * this.g_mapScale / w,
        sy: 187 * this.g_mapScale / h
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_canvasHeight / 2 + 100 * this.g_mapScale,
      }
    })

    this.subscribeMapUpEvent(PC.id, ()=>{
      let slider = this.g_cartoon.getCartoonElement("audio-plyer-slider")
      if(slider && slider.enableSlider){
        slider.release()
        this.disableMoveAsstant()
      }

      this.g_enableMapUp = true;
    })
    
    this.render(PD.ref, zIndexMap.star)
    this.render(PC.ref)
    this.render(PA.ref)
    this.render(PB.ref)
  }

  initXuanZhuanTai(){
    let element = this.g_cartoon.createCartoonElementImageFunc("XuanZhuanTai", "bg_taizi", (w, h)=>{
      return {
        sx: 1214 * this.g_mapScale / w,
        sy: 317 * this.g_mapScale / h,
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_canvasHeight / 2,
      }
    })

    this.render(element.ref)
  }

  initTable(){
    let element = this.g_cartoon.createCartoonElementImageFunc("table", "bg_green", (w, h)=>{
      return {
        sx: this.g_canvasWidth / w,
        sy: 187 * this.g_mapScale / h,
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_cartoon.getOrigin().y + 630*this.g_mapScale,
      }
    })
    this.render(element.ref)
  }

  initShowWindow(){
    let element = this.g_cartoon.createCartoonElementImageFunc("main_window", "bg_main_window", (w, h)=>{
      return {
        sx: 661 * this.g_mapScale / w,
        sy: 334 * this.g_mapScale / h,
      }
    },  (w, h)=>{
      return {
        x: this.g_canvasWidth / 2,
        y: this.g_cartoon.getOrigin().y + 180*this.g_mapScale,
      }
    })

    let lianzi = this.g_cartoon.createCartoonElementImageFunc("main_window_lianzi", "bg_lianzi", (w, h)=>{
      return {
        sx: 626 * this.g_mapScale / w,
        sy: 46 * this.g_mapScale / h,
      }
    },  (w, h)=>{
      return {
        x: element.ref.x,
        y: element.ref.y - (334/2 - 27)*this.g_mapScale,
      }
    })
    
    let enableImage = false;
    let enableText = false;
    let enableLongAudio = false;
    let enableShortAudio = false;

    switch(this.g_formData.mainQuestion.type){
      case "Image": enableImage = true; console.log("图片"); break;
      case "Text":  enableText = true; console.log("文字"); break;
      case "LongAudio":  enableLongAudio = true; console.log("长音频"); break;
      default:  enableText = true; break;
    }
    if(this.g_formData.mainQuestion.shortAudio_url){
      enableShortAudio = true
    }

    if(enableImage){
      // 图片内容
      let imageContent = this.g_cartoon.createImage(this.g_formData.mainQuestion.image_url, (w, h)=>{
        let sx = 645*0.92 / w
        let sy = 323*0.92 / h
        let s = Math.min(sx, sy)
        return {
          sx: s,
          sy: s 
        }
      }, ()=>{
        return {
          x: 0,
          y: 0
        }
      })
      element.ref.addChild(imageContent)
    }
    if(enableText){
      // 文字内容
      let DemoText = "It's really a fantastic magic movie which talks about love and friendship."
      let textContent = new RichText(this.g_ctx)
      textContent.fontName = "BRLNSR_1"
      textContent.fontSize = 50
      textContent.disH = 5
      textContent.fontColor = "#623f3d"
      textContent.width = 645*0.965
      textContent.text = DemoText
      textContent.x = -textContent.width/2 + 10
      textContent.y = -textContent.height/2 - (textContent.fontSize+textContent.disH)/2
      element.ref.addChild(textContent)
    }
    if(enableLongAudio){
      // 音乐内容
      let longAudio = this.g_cartoon.createCartoonElementImageFunc("main_window_la_play", "btn_videooff", (w, h)=>{
        return {
          sx: 214 * this.g_mapScale / w,
          sy: 273 * this.g_mapScale / h,
        }
      },  (w, h)=>{
        return {
          x: this.g_canvasWidth / 2,
          y: this.g_cartoon.getOrigin().y + (273 * this.g_mapScale)/2 ,
        }
      })

      let audioClick = this.g_cartoon.createCartoonElementImageFunc("main_window_sa_click", "audio_play_click", (w, h)=>{
        return {
          sx: 60 / w,
          sy: 60 / h,
        }
      },  (w, h)=>{
        return {
          x: 0,
          y: -30,
        }
      })
      
      let longAudio_pause = this.g_cartoon.createImage("btn_videoon", (w, h)=>{
        return {
          sx: 214  / w,
          sy: 273  / h,
        }
      },  (w, h)=>{
        return {
          x: 0,
          y: 0,
        }
      })
      longAudio_pause.visible = false
      longAudio.pause_icon = longAudio_pause;
      longAudio.isPlay = false;

      // 进度条
      let progressBar = this.g_cartoon.createCartoonElementImageFunc("audio-plyer-progress", "bg_tiaodi", (w, h)=>{
        return {
          sx: 527 / w,
          sy: 31 / h
        }
      }, (w, h)=>{
        return {
          x: 0,
          y: 0
        }
      })

      // 播放控制
      longAudio.isPlay = false;
      longAudio.play = ()=>{
        longAudio.isPlay = true;
        longAudio.audio.play()
      }
      longAudio.pause = ()=>{
        longAudio.isPlay = false;
        longAudio.audio.pause()
      }
  
      // 停止播放
      longAudio.stop = ()=>{
        if(longAudio.isPlay){
          longAudio.isPlay = false;
          longAudio.audio.pause()
          longAudio.audio.currentTime = 0;
          longAudio_pause.visible = false;
          slider.ref.y = -longAudio.limited + 20
        }
      }

      longAudio.audio = this.g_cartoon.audio.get(this.g_formData.mainQuestion.longAudio_url)
      // 时间标签
      let time = 0;
      if(longAudio.audio){
        time = longAudio.audio.duration
      }
      let endTime = Math.floor(time / 60)
      let timeString = ""
      if(endTime<9){
        timeString = "0" + endTime
      }else{
        timeString + endTime + ""
      }
      if(time % 60<9){
        timeString = timeString + ":0" + (time % 60 + "").substring(0, 2)
      }else{
        timeString = timeString + ":" + (time % 60 + "").substring(0, 2)
      }
      
      let timeStart = this.g_cartoon.createCartoonElementLabel("audio-player-start", "00:00", "BRLNSR_1", "#623f3d", 28)
      timeStart.ref.x = -(527/2) + 30
      timeStart.ref.y = 30
      longAudio.currentTimeString = timeStart

      let timeEnd = this.g_cartoon.createCartoonElementLabel("audio-player-start", timeString,  "BRLNSR_1", "#623f3d", 28)
      timeEnd.ref.x = (527/2) - 30
      timeEnd.ref.y = 30

      // 滑块
      let slider = this.g_cartoon.createCartoonElementImageFunc("audio-plyer-slider", "icon_tiao", (w, h)=>{
        return {
          sx: 14 / w,
          sy: 36 / h
        }
      }, (w, h)=>{
        return {
          x: -527/2,
          y: 0
        }
      })
      
      slider.enableSlider = false;
      slider.release = ()=>{
        if(!longAudio.audio){
          this.g_enableMapUp = true;
          return;
        }
        slider.enableSlider= false;
        this.g_enableMapDown = true;
        longAudio.refreshAudioProgress();
      }
      progressBar.update = ()=>{
        coverColor.width = slider.ref.x - (-527/2)
      }

      // 已播放的覆盖颜色
      let coverColor = this.g_cartoon.createRectangula({
        width: 0,
        height: 31,
        x: -(527/2),
        y: -31/2,
        radius: 5,
        fillColor: "#9ccfef"
      })

      // 音频时间
      if(longAudio.audio){
        longAudio.audio.onended = ()=>{
          longAudio.pause()
          longAudio.pause_icon.visible = false;
          slider.ref.x = -(527/2)
          coverColor.width = 0;
          longAudio.currentTimeString.ref.text = "00:00"
        }
        longAudio.audio.ontimeupdate = ()=>{
          if(!slider.enableSlider){
            let per = longAudio.audio.currentTime / time
            slider.ref.x = -(527/2) + 527 * per
            coverColor.width = slider.ref.x - (-(527/2))
          }
          let _time = parseInt(longAudio.audio.currentTime)
          let m = parseInt(_time/60 + "")
          let s = parseInt(_time%60 + "")
          longAudio.currentTimeString.ref.text = `${m<9?"0"+m:m}:${s<9?"0"+s:s}`
        }
        longAudio.refreshAudioProgress = ()=>{
          slider.enableSlider = false;
          let dis = slider.ref.x + (527/2)
          let per = dis / 527
          longAudio.audio.currentTime = longAudio.audio.duration * per
        }
      }
      

      longAudio.ref.addChild(audioClick.ref)
      longAudio.ref.addChild(longAudio_pause)
      progressBar.ref.addChild(timeStart.ref)
      progressBar.ref.addChild(timeEnd.ref)
      progressBar.ref.addChild(coverColor)
      element.ref.addChild(progressBar.ref)
      progressBar.ref.addChild(slider.ref)

      // 滑动滑块
      this.subscribeMapDownEvent(slider.id, ()=>{
        if(!longAudio.audio){
          this.g_enableMapDown = true;
          return;
        }
        if(!slider.enableSlider){
          slider.enableSlider= true;
          this.enableMoveAsstant(()=>{
            let moveDis = (this.g_clickX - element.ref.x) / this.g_mapScale
            if( moveDis > (527/2)){
              moveDis = (527/2)
            }else if( moveDis < (-527/2)){
              moveDis = -(527/2)
            }
            slider.ref.x = moveDis;
            progressBar.update()
          })
        }
        this.g_enableMapDown = true;
      })
      this.subscribeMapUpEvent(slider.id, ()=>{
        if(slider.enableSlider){
          slider.release()
          this.disableMoveAsstant();
        }
        this.g_enableMapUp = true;
      })

      // 播放按钮点击
      this.subscribeMapDownEvent(audioClick.id, ()=>{
        if(!longAudio.audio){
          this.g_enableMapDown = true;
          return;
        }
        if(longAudio.isPlay){
          longAudio.pause()
          longAudio.isPlay = false;
          longAudio.pause_icon.visible = false;
        }else{
          longAudio.play()
          longAudio.isPlay = true;
          longAudio.pause_icon.visible = true;
        }
        this.g_enableMapDown = true;
      })
      this.render(longAudio.ref, zIndexMap.longAudioPlay)
    }
    if(enableShortAudio){
      // 音乐内容
      let shortAudio = this.g_cartoon.createCartoonElementImageFunc("main_window_la_play", "btn_laba", (w, h)=>{
        return {
          sx: 77 / w,
          sy: 260 / h,
        }
      },  (w, h)=>{
        return {
          x: (512/2) + 125,
          y: -80
        }
      })
      shortAudio.play = this.g_cartoon.createAnimation("icon_laba", 3, 500)
      shortAudio.play.x = -50;
      shortAudio.play.y = -7;
      shortAudio.ref.addChild(shortAudio.play)
      shortAudio.play.loop = true;
      shortAudio.isPlay = false;

      // 播放按钮点击
      this.subscribeMapDownEvent(shortAudio.id, ()=>{
        if(!shortAudio.isPlay){
          this.g_cartoon.playAudio(this.g_formData.mainQuestion.shortAudio_url, false, ()=>{
            shortAudio.play.stop()
            shortAudio.play.visible = false
            shortAudio.isPlay = true;
          })
          shortAudio.play.visible = true
          shortAudio.play.play()
          shortAudio.isPlay = true;
        }else{
          this.g_cartoon.stopAudio(this.g_formData.mainQuestion.shortAudio_url)
          shortAudio.play.stop()
          shortAudio.play.visible = false
          shortAudio.isPlay = false;
        }
        this.g_enableMapDown = true;
      })
      element.ref.addChild(shortAudio.ref)
    }

    this.render(lianzi.ref, zIndexMap.lianzi)
    this.render(element.ref, zIndexMap.window)
    
  }

  initCard_up(){
    let xuanzhuanTai = this.g_cartoon.getCartoonElement("XuanZhuanTai")
    let cardAll = []
    this.g_formData.textUp.forEach(item => {
      let labelText = this.g_cartoon.createLabel(item.text, "BRLNSR_1", "#634846", 50)
      labelText.setScaleXY(this.g_mapScale)
      labelText.refreshSize();
      cardAll.push(labelText)
    });

    let spaceing = 10 * this.g_mapScale;
    let totalWidth = 0
    cardAll.forEach((element, index) => {
      let bd = element.getBoundingBox()
      totalWidth += (bd.width) + spaceing
    });
    let fontSize = 50;
    if(totalWidth > 1920){
      fontSize = 50 * (1920 / (totalWidth)) * 0.7
    }
    cardAll.forEach((card, index)=>{
      card.fontSize = fontSize
      card.refreshSize()
    })

    let cardX =  xuanzhuanTai.ref.x - (xuanzhuanTai.ref.width*this.g_mapScale)/2 + 100 * this.g_mapScale
    let A1 = []
    let A2 = []
    let A3 = []
    let A4 = []
    let A5 = []
    let A6 = []
    cardAll.forEach((card, index)=>{
      let pos = {x:0, y:0}
      if(index!=0){
        pos = {
          x: cardX + ((card.width + 20) * this.g_mapScale)/2, y: xuanzhuanTai.ref.y + 10 * this.g_mapScale
        }
        cardX = cardX + (card.width + 20) * this.g_mapScale + 20 * this.g_mapScale
      }else{
        pos = {
          x: cardX, y: xuanzhuanTai.ref.y + 10 * this.g_mapScale
        }
        cardX = cardX + ((card.width + 20) * this.g_mapScale)/2 + 20 * this.g_mapScale
      }

      let temp = this.createCardUp(index, card, pos)
      A1.push(temp.e1)
      A2.push(temp.e2)
      A3.push(temp.e3)
      A4.push(temp.e4)
      A5.push(temp.e5)
    })
    let movedDis =this.alignCenter(A1)
    A2.forEach(item=>{
      item.x = item.x + movedDis
    })
    A3.forEach(item=>{
      item.x = item.x + movedDis
    })
    A4.forEach(item=>{
      item.x = item.x + movedDis
    })
    A5.forEach(item=>{
      item.x = item.x + movedDis
    })
  }

  createCardUp(index, label, pos){
    pos.y = pos.y + 20*this.g_mapScale
    label.x = pos.x
    label.y = pos.y
    let card = this.g_cartoon.createCartoonElementImageFunc(`card-up-${index}`, "bg_kuang1", (w, h)=>{
      return {
        sx: (label.width + 20)*this.g_mapScale / w,
        sy: 84*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: pos.x,
        y: pos.y
      }
    })
    this.m_allUpCards.push(`card-up-${index}`)

    // 连接节点
    let dot = this.g_cartoon.createImage("icon_dian1", (w, h)=>{
      return {
        sx: 37*this.g_mapScale / w,
        sy: 37*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x,
        y: card.ref.y + 84*this.g_mapScale / 2
      }
    })
    dot.visible = false;
    this.render(dot, zIndexMap.dot);
    card.ref.dot = dot;

    // 红色节点
    let dot_red = this.g_cartoon.createImage("icon_dian1", (w, h)=>{
      return {
        sx: 37*this.g_mapScale / w,
        sy: 37*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x,
        y: card.ref.y + 84*this.g_mapScale / 2
      }
    })
    dot_red.visible = false;
    this.render(dot_red, zIndexMap.dot);
    card.ref.dot_red = dot_red;

    card.showRedDot = ()=>{
      dot.visible = false;
      dot_red.visible = true;
    }
    card.showBlueDot = ()=>{
      dot.visible = true;
      dot_red.visible = false;
    }


    // 链接关系
    card.lineTo = JSON.parse(JSON.stringify(this.g_formData.textUp[index].lineTo))
    card.complete = false
    card.checkLink = (id)=>{
      let index = Number(id.split("-")[2])
      let z = card.lineTo.indexOf(index)
      if(z==-1){
        return false
      }else{
        card.lineTo.splice(z, 1)
        if(card.lineTo.length==0){
          card.complete = true;
        }
        return true
      }
    }

    card.showHideDoc = (status)=>{
      if(status){
        this.showItemDot(card.ref)
      }else {
        if(!card.lined){
          this.hideItemDot(card.ref)
        }
      }
    }

    let panzi = this.g_cartoon.createImage("pan_zi", (w, h)=>{
      return {
        sx: 98*this.g_mapScale / w,
        sy: 70*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: pos.x,
        y: pos.y + (84*this.g_mapScale)/2
      }
    })

    card.text = label;
    card.panzi = panzi;

    card.lined = false;

    card.startAni = null;
    card.showStar = (callback?)=>{
      this.g_cartoon.stopAudio("zheng_que")
      this.g_cartoon.playAudio("zheng_que")
      card.startAni = this.g_cartoon.createAnimation("bg_star", 11, 500, ()=>{
        card.startAni.visible = false;
        this.deleteElementInRender(card.startAni.id)
        callback && callback()
      })
      card.startAni.x = card.ref.x
      card.startAni.y = card.ref.y
      this.render(card.startAni, zIndexMap.animation)
      card.startAni.play()
    }

    this.subscribeMapDownEvent(card.id, ()=>{
      this.g_cartoon.playAudio("dian_ji")
      if(this.m_currentSelectedCard == card.id){
        this.g_enableMapDown = true;
        return
      }
      card.showHideDoc(true)
      if(this.m_currentSelectedCard){
        if(this.m_currentSelectedCard.indexOf("down")!=-1){
          let result = false
          if( this.m_currentSelectedCard.indexOf("up")!= -1){
            result = this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).checkLink(card.id)
          }else{
            result = this.g_cartoon.getCartoonElement(card.id).checkLink(this.m_currentSelectedCard)
          }
          if(!result){
            this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showRedDot()
            card.showRedDot()
          }
          this.lineTo(card.id, this.m_currentSelectedCard, result, ()=>{
            if(!result){
              this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showHideDoc(false)
              card.showHideDoc(false)
            }else{
              card.showStar()
            }
            this.m_currentSelectedCard = null
            this.g_enableMapDown = true;
            if(this.checkEndGame()){
              this.endGame()
            }
          })
        }else{
          this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showHideDoc(false)
          this.m_currentSelectedCard = card.id
          this.g_enableMapDown = true;
        }
      }else{
        this.m_currentSelectedCard = card.id
        this.g_enableMapDown = true;
      }
    })

    this.render(panzi, zIndexMap.cardUp)
    this.render(card.ref, zIndexMap.cardUp)
    this.render(label, zIndexMap.cardUp)
    return {
      e1: card.ref,
      e2: panzi,
      e3: label,
      e4: dot,
      e5: dot_red
    }
  }

  initCard_down(){
    let table = this.g_cartoon.getCartoonElement("table")
    let cardAll = []
    this.g_formData.textDown.forEach(item => {
      let labelText = this.g_cartoon.createLabel(item.text, "BRLNSR_1", "#634846", 50)
      labelText.setScaleXY(this.g_mapScale)
      labelText.refreshSize();
      cardAll.push(labelText)
    });

    let spaceing = 10 * this.g_mapScale;
    let totalWidth = 0
    cardAll.forEach((element, index) => {
      let bd = element.getBoundingBox()
      totalWidth += (bd.width) + spaceing
    });
    let fontSize = 50;
    if(totalWidth > 1920){
      fontSize = 50 * (1920 / (totalWidth)) * 0.7
    }
    cardAll.forEach((card, index)=>{
      card.fontSize = fontSize
      card.refreshSize()
    })

    let lastWidth = 0
    let cardX = 0
    let A1 = []
    let A2 = []
    let A3 = []
    let A4 = []
    let A5 = []
    let A6 = []
    cardAll.forEach((card, index)=>{
      let pos = {x:0, y:0}
      if(index!=0){
        pos = {
          x: cardX + ((card.width + 30) * this.g_mapScale)/2, y: table.ref.y - 30 * this.g_mapScale
        }
        cardX = cardX + (card.width + 30) * this.g_mapScale + 20 * this.g_mapScale
      }else{
        pos = {
          x: cardX + ((card.width + 20) * this.g_mapScale)/2 + 20 * this.g_mapScale, y: table.ref.y - 30 * this.g_mapScale
        }
        cardX = cardX + ((card.width + 20) * this.g_mapScale)/2 + 20 * this.g_mapScale + ((card.width + 20) * this.g_mapScale)/2 + 20 * this.g_mapScale
      }

      let temp = this.createCardDown(index, card, pos)
      A1.push(temp.e1)
      A2.push(temp.e2)
      A3.push(temp.e3)
      A4.push(temp.e4)
      A5.push(temp.e5)
      A6.push(temp.e6)
    })
    let movedDis = this.alignCenter(A1)
    A2.forEach(item=>{
      item.x = item.x + movedDis
    })
    A3.forEach(item=>{
      item.x = item.x + movedDis
    })
    A4.forEach(item=>{
      item.x = item.x + movedDis
    })
    A5.forEach(item=>{
      item.x = item.x + movedDis
    })
    A6.forEach(item=>{
      item.x = item.x + movedDis
    })
  }

  createCardDown(index, label, pos){
    pos.y = pos.y + 20*this.g_mapScale
    label.x = pos.x
    label.y = pos.y
    let card = this.g_cartoon.createCartoonElementImageFunc(`card-down-${index}`, "bg_card_down", (w, h)=>{
      return {
        sx: (label.width + 20)*this.g_mapScale / w,
        sy: 108*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: pos.x,
        y: pos.y
      }
    })
    card.text = label;
    let cardBB = card.ref.getBoundingBox()
    let borderLeft = this.g_cartoon.createCartoonElementImageFunc(`card-down-border-left-${index}`, "border_left", (w, h)=>{
      return {
        sx: 10*this.g_mapScale / w,
        sy: 108*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x - ((label.width + 20)*this.g_mapScale) / 2,
        y: card.ref.y
      }
    })

    let borderRight = this.g_cartoon.createCartoonElementImageFunc(`card-down-border-right-${index}`, "border_right", (w, h)=>{
      return {
        sx: 10*this.g_mapScale / w,
        sy: 108*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x + ((label.width + 20)*this.g_mapScale) / 2,
        y: card.ref.y
      }
    })
    borderRight.ref.visible = true

    // 连接节点
    let dot = this.g_cartoon.createImage("icon_dian2", (w, h)=>{
      return {
        sx: 37*this.g_mapScale / w,
        sy: 37*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x,
        y: card.ref.y - 84*this.g_mapScale / 2
      }
    })
    dot.visible = false;
    this.render(dot, zIndexMap.dot);
    card.ref.dot = dot;

    // 红色节点
    let dot_red = this.g_cartoon.createImage("icon_dian2", (w, h)=>{
      return {
        sx: 37*this.g_mapScale / w,
        sy: 37*this.g_mapScale / h
      }
    }, (w, h)=>{
      return {
        x: card.ref.x,
        y: card.ref.y - 84*this.g_mapScale / 2
      }
    })
    dot_red.visible = false;
    this.render(dot_red, zIndexMap.dot);
    card.ref.dot_red = dot_red;

    card.showRedDot = ()=>{
      dot.visible = false;
      dot_red.visible = true;
    }
    card.showBlueDot = ()=>{
      dot.visible = true;
      dot_red.visible = false;
    }


    card.showHideDoc = (status)=>{
      if(status){
        this.showItemDot(card.ref)
      }else {
        if(!card.lined){
          this.hideItemDot(card.ref)
        }
      }
    }

    card.lined = false;
    
    this.subscribeMapDownEvent(card.id, ()=>{
      this.g_cartoon.playAudio("dian_ji")
      if(this.m_currentSelectedCard == card.id){
        this.g_enableMapDown = true;
        return
      }
      card.showHideDoc(true)
      if(this.m_currentSelectedCard){
        if(this.m_currentSelectedCard.indexOf("up")!=-1){
          let result = false
          if( this.m_currentSelectedCard.indexOf("up")!= -1){
            result = this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).checkLink(card.id)
          }else{
            result = this.g_cartoon.getCartoonElement(card.id).checkLink(this.m_currentSelectedCard)
          }
          if(!result){
            this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showRedDot()
            card.showRedDot()
          }
          this.lineTo(card.id, this.m_currentSelectedCard, result, ()=>{
            if(!result){
              this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showHideDoc(false)
              card.showHideDoc(false)
            }else{
              this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showStar()
            }
            
            this.m_currentSelectedCard = null
            this.g_enableMapDown = true;
            if(this.checkEndGame()){
              this.endGame()
            }
          })
        }else{
          this.g_cartoon.getCartoonElement(this.m_currentSelectedCard).showHideDoc(false)
          this.m_currentSelectedCard = card.id
          this.g_enableMapDown = true;
        }
      }else{
        this.m_currentSelectedCard = card.id
        this.g_enableMapDown = true;
      }
    })

    this.render(borderLeft.ref, zIndexMap.cardBorder)
    this.render(borderRight.ref, zIndexMap.cardBorder)
    this.render(card.ref, zIndexMap.cardUp)
    this.render(label, zIndexMap.cardUp)

    return {
      e1: card.ref,
      e2: label,
      e3: dot,
      e4: borderLeft.ref,
      e5: borderRight.ref,
      e6: dot_red
    }
  }

  alignCenter(elementArray){
    let first = null
    let last = null
    let length = elementArray.length
    if(length > 0){
      first = elementArray[0].getBoundingBox()
    }
    if(length==1){
      last = first
    }else{
      last = elementArray[length-1].getBoundingBox()
    }
    
    let avDis = ((this.g_canvasWidth - (last.x + last.width)) + first.x) / 2
    let moveDis = avDis - first.x

    elementArray.forEach(element => {
      element.x = element.x + moveDis
    });

    return moveDis
  }

  lineTo(T1, T2, isCorrect, callback?){
    let color = ""
    if(isCorrect){
      color = "line_6"
    }else{
      color = "line_7"
    }

    const line_1 = new MySprite();
    line_1.init(this.g_cartoon.images.get(color));
    line_1.setScaleXY(this.g_mapScale)
    line_1.anchorX = (line_1.width - line_1.height / 2) / line_1.width;
    line_1.anchorY = 0;
    line_1.id = UUID.UUID();

    const line_2 = new MySprite();
    line_2.init(this.g_cartoon.images.get(color));
    line_2.setScaleXY(this.g_mapScale)
    line_2.anchorX = (line_2.width - line_2.height / 2) / line_2.width;
    line_2.anchorY = 0;
    line_2.id = UUID.UUID();

    line_1.visible = false
    line_2.visible = false

    const line_3 = new MySprite();
    line_3.init(this.g_cartoon.images.get(color));
    line_3.setScaleXY(this.g_mapScale)
    line_3.anchorX = (line_3.width - line_3.height / 2) / line_3.width;
    line_3.anchorY = 0;
    line_3.id = UUID.UUID();

    let target_1 = this.g_cartoon.getCartoonElement(T1).ref.dot
    let target_2 = this.g_cartoon.getCartoonElement(T2).ref.dot
    let target_3 = this.g_cartoon.getCartoonElement(T1).ref.dot

    if(isCorrect){
      this.g_cartoon.getCartoonElement(T1).lined = true
      this.g_cartoon.getCartoonElement(T2).lined = true
    }
    let middlePos = {
      x: (target_1.x + target_2.x) / 2,
      y: (target_1.y + target_2.y) / 2
    }

    line_1.x = target_2.x;
    line_1.y = target_2.y;
    line_2.x = target_1.x;
    line_2.y = target_1.y;
    line_3.x = target_2.x;
    line_3.y = target_2.y;

    this.render(line_1, zIndexMap.line);
    this.render(line_2, zIndexMap.line);
    this.render(line_3, zIndexMap.line);

    if (middlePos.x < line_1.x) {
      line_1.scaleX *= -1;
    }
    if (middlePos.x < line_2.x) {
      line_2.scaleX *= -1;
    }
    if (target_3.x < line_3.x) {
      line_3.scaleX *= -1;
    }

    const angle_3 = getAngleByPos(line_3.x, line_3.y, target_3.x, target_3.y);
    line_3.rotation = angle_3 + 180;
    const distance_3 = getPosDistance(line_3.x, line_3.y, target_3.x, target_3.y);
    const targetS_3 = distance_3 / line_3.height;
    tweenChange( line_3, { scaleY: targetS_3 }, 1.2, () => {
      if(isCorrect){
        line_1.visible = false
        line_2.visible = false
        callback && callback(line_1, line_2)
      }else{
        line_1.visible = true
        line_2.visible = true
        this.deleteElementInRender(line_3.id)
        this.cutLine(line_1, line_2, ()=>{
          callback && callback(line_1, line_2)
        })
      }

    }, TWEEN.Easing.Sinusoidal.Out);

    const angle_1 = getAngleByPos(line_1.x, line_1.y, middlePos.x, middlePos.y);
    line_1.rotation = angle_1 + 180;
    const distance_1 = getPosDistance(line_1.x, line_1.y, middlePos.x, middlePos.y);
    const targetS_1 = distance_1 / line_1.height;
    tweenChange( line_1, { scaleY: targetS_1 }, 1, () => {}, TWEEN.Easing.Sinusoidal.Out);

    const angle_2 = getAngleByPos(line_2.x, line_2.y, middlePos.x, middlePos.y);
    line_2.rotation = angle_2 + 180;
    const distance_2 = getPosDistance(line_2.x, line_2.y, middlePos.x, middlePos.y);
    const targetS_2 = distance_2 / line_2.height;
    tweenChange( line_2, { scaleY: targetS_2 }, 1, () => {}, TWEEN.Easing.Sinusoidal.Out);
  }

// 生长连接点
showItemDot(item) {
  if (item.sTween) {
    item.sTween.stop();
  }
  const dot = item.dot;
  const dot_red = item.dot_red;
  dot.visible = true;
  dot_red.visible = false
  dot_red.setScaleXY(this.g_mapScale)
  dot.setScaleXY(0);
  item.sTween = scaleItem(
    dot,
    this.g_mapScale,
    0.3,
    () => {
      item.sTween = null;
    },
    TWEEN.Easing.Cubic.Out
  );
}

// 隐藏连接点
hideItemDot(item) {
  if (item.sTween) {
    item.sTween.stop();
  }
  const dot = item.dot;
  const dot_red = item.dot_red;
  dot.visible = false
  dot_red.visible = true;
  item.sTween = scaleItem(
    dot_red,
    0,
    0.3,
    () => {
      item.sTween = null;
    },
    TWEEN.Easing.Cubic.Out
  );
}

initCutter(){
  let element =this.g_cartoon.createCartoonElementImageFunc(`cutter`, "icon_jian", (w, h)=>{
    return {
      sx: 138  *this.g_mapScale / w,
      sy: 146 * this.g_mapScale / h
    }
  }, (w, h)=>{
    return {
      x: -138 * this.g_mapScale,
      y: this.g_canvasWidth + 146  *this.g_mapScale,
    }
  })
  element.ref.visible = false
  this.render(element.ref, zIndexMap.cutter)
}

cutLine(line1, line2, callback?){
  let cutter = this.g_cartoon.getCartoonElement("cutter")
  let middlePos = {
    x: (line1.x + line2.x) / 2 - (cutter.ref.width * this.g_mapScale) / 2 * 0.8,
    y: (line1.y + line2.y) / 2 + (cutter.ref.height * this.g_mapScale) / 2 * 0.8
  }

  cutter.ref.visible = true
  tweenChange(cutter.ref, middlePos, 1, ()=>{
    this.g_cartoon.playAudio("jian_dao", false, ()=>{
      cutter.ref.visible = false
      cutter.ref.x = cutter.initX
      cutter.ref.y = cutter.initY
      this.g_cartoon.playAudio("hua_luo")
      tweenChange(line1, {rotation: 360}, 1.3, TWEEN.Easing.Cubic.In)
      tweenChange(line2, {rotation: 360}, 1.3, ()=>{
        this.m_setTimeoutIDs.push(setTimeout(() => {
          this.deleteElementInRender(line1.id)
          this.deleteElementInRender(line2.id)
          callback && callback()
        }, 200))
      }, TWEEN.Easing.Cubic.In)
    })
  },TWEEN.Easing.Cubic.Out)
}

checkEndGame(){
  let result = true
  this.m_allUpCards.forEach(id=>{
    if(!this.g_cartoon.getCartoonElement(id).complete){
      result = false
    }
  })
  return result
}

initHand(){
  let basePos = {
    x: this.g_canvasWidth / 2,
    y: this.g_canvasHeight / 2
  }
  let elementLeft = this.g_cartoon.createCartoonElementImage("hand-left", "bg_hand1", 391, 254, basePos.x, basePos.y, true)
  elementLeft.pos_1 = {
    x: basePos.x,
    y: basePos.y
  }
  elementLeft.pos_2 = {
    x: basePos.x - 50*this.g_mapScale,
    y: basePos.y
  }
  elementLeft.ref.setScaleXY(0)

  let elementRight = this.g_cartoon.createCartoonElementImage("hand-right", "bg_hand2", 392, 392, basePos.x, basePos.y, true)
  elementRight.pos_1 = {
    x: basePos.x,
    y: basePos.y
  }
  elementRight.pos_2 = {
    x: basePos.x + 50*this.g_mapScale,
    y: basePos.y
  }
  elementRight.ref.setScaleXY(0)

  
  let fishPos = [
    {x: this.g_canvasWidth/2 + 280*this.g_mapScale, y: this.g_canvasHeight/2 - 150*this.g_mapScale, w: 107, h:91},
    {x: this.g_canvasWidth/2 + 150*this.g_mapScale, y: this.g_canvasHeight/2 - 250*this.g_mapScale, w: 48, h:69},
    {x: this.g_canvasWidth/2 + 50*this.g_mapScale, y: this.g_canvasHeight/2 - 220*this.g_mapScale, w: 64, h:70}
  ]
  for(let index=0; index<3; index++){
    let item = this.g_cartoon.createCartoonElementImage("hand-fish-" + index, "icon_yu"+(index+1), fishPos[index].w, fishPos[index].h, fishPos[index].x, fishPos[index].y, true)
    elementRight.pos_1 = {
      x: basePos.x,
      y: basePos.y
    }
    item.ref.setScaleXY(0)

    item.show = ()=>{
      tweenChange(item.ref, {scaleX:item.initScaleX, scaleY:item.initScaleY}, 0.2, ()=>{})
    }

    item.rote = (callback?)=>{
      setTimeout(() => {
        this.m_setIntervalIDs.push(setInterval(()=>{
          tweenChange(item.ref, {rotation:360}, 0.2, ()=>{
            item.ref.rotation = 0
            callback && callback
          })
         }, 410))
      }, 505);
    }

    this.m_fishes.push(item)
  }
 
  elementRight.pos_2 = {
    x: basePos.x + 50*this.g_mapScale,
    y: basePos.y
  }
  elementRight.ref.setScaleXY(0)

  elementLeft.clap = (callback?)=>{
    tweenChange(elementLeft.ref, {scaleX:elementLeft.initScaleX, scaleY:elementLeft.initScaleY}, 0.2, ()=>{
      this.m_setIntervalIDs.push(setInterval(()=>{
          tweenChange(elementLeft.ref, elementLeft.pos_2, 0.2, ()=>{
            tweenChange(elementLeft.ref, elementLeft.pos_1, 0.2)
          })
      }, 410))
    })
  }

  elementRight.clap = (callback?)=>{
    tweenChange(elementRight.ref, {scaleX:elementRight.initScaleX, scaleY:elementRight.initScaleY}, 0.2, ()=>{
      this.m_setIntervalIDs.push(setInterval(()=>{
        tweenChange(elementRight.ref, elementRight.pos_2, 0.2, ()=>{
          tweenChange(elementRight.ref, elementRight.pos_1, 0.2)
        })
      }, 410))
    })
  }

  this.m_fishes.forEach(item=>{
    this.render(item.ref, 9995)
  })
  this.render(elementLeft.ref, 9999)
  this.render(elementRight.ref, 9990)
}

clapHand(callback?){
  this.m_fishes.forEach(item=>{
    item.show()
    item.rote()
  })
  this.g_cartoon.getCartoonElement("hand-left").clap()
  this.g_cartoon.getCartoonElement("hand-right").clap()
}


initBlackMask(callback?){
  let element = this.g_cartoon.createCartoonElementImage("mask-layer", "_mask_layer_1280_720", this.g_canvasWidth, this.g_canvasHeight, this.g_canvasWidth/2, this.g_canvasHeight/2)
  element.ref.visible = false;

  element.in = (callback?)=>{
    element.ref.visible = true;
    tweenChange(element.ref, {alpha:1}, 0.1, ()=>{
      callback && callback()
    })
  }

  element.out = (callback?)=>{
    tweenChange(element.ref, {alpha:0}, 0.1, ()=>{
      element.ref.visible = false;
      callback && callback()
    })
  }
 
  this.render(element.ref, 998)
}





































































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

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

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

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

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

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

  // ----------------------------------
  // 初始化默认数据
  // ----------------------------------
  initDefaultData() {
    if ( Object.keys(this.g_formData).length===0 ) {
      this.g_formData = defauleFormData;
    }
  }


  // ----------------------------------
  // 初始化音乐
  // ----------------------------------
  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(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;
    }
    // for(let cartoonID in this.m_mapUpQueue){
    //   if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(cartoonID))) {
    //     this.g_enableMapUp = false;
    //     this.m_mapUpQueue[cartoonID]();
    //   }
    // }
    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;
          }
        }
      }
    })

  }


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


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


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


  initListener() {
    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)
    })
  }


  // 全局初始化入口，当资源加载完毕后执行
  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_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("background-color", "ShapeRect").ref;
    // console.log( "stageWidth: " + this.g_cartoon.stageWidth + " stageHeight" + this.g_cartoon.stageHeight)
    // console.log( "clientWidth: " + this.g_cartoon.clientWidth + " clientHeight" + this.g_cartoon.clientHeight)
    // console.log( "canvasWidth: " + this.g_canvasWidth + " canvasHeight" + this.g_canvasHeight)
    // console.log( "mapScale: " + this.g_mapScale )
    imageColor.x = this.g_cartoon.clientWidth / 2 ;
    imageColor.y = this.g_cartoon.clientHeight / 2 ;
    imageColor.setSize(this.g_cartoon.clientWidth, this.g_cartoon.clientHeight);
    imageColor.init();
    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, -999999)
    this.g_stage = image
  }

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

  enableMoveAsstant(callback){
    if(this.m_moveAsstantIntervalId){
      clearInterval(this.m_moveAsstantIntervalId)
    }
    this.m_moveAsstantIntervalId = setInterval(()=>{
      callback()
    },50)
  }

  disableMoveAsstant(){
    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.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
    this.render(bgRect, 9999);
  }












  













  












  // --------------------------------------------------
  // --------------  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, 2);
    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);
  }
}

